From e8e72743931a1c9544841fb4b7e6f26fda6022ee Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Fri, 20 Jun 2025 09:41:51 -0700 Subject: [PATCH] Add roomote to the monorepo (#109) --- .docker/Dockerfile.api | 27 + .docker/Dockerfile.base | 31 + .docker/Dockerfile.controller | 25 + .docker/Dockerfile.dashboard | 25 + .docker/Dockerfile.worker | 62 + .../scripts/clickhouse/001-create-tables.sql | 0 .../scripts/postgres/create-databases.sh | 0 .gitignore | 5 + apps/web/.prettierrc.json => .prettierrc.json | 0 README.md | 26 + apps/roomote/.env.example | 9 + apps/roomote/.gitignore | 3 + apps/roomote/drizzle.config.ts | 10 + .../roomote/drizzle/0000_cuddly_luke_cage.sql | 21 + .../roomote/drizzle/0001_fluffy_sasquatch.sql | 1 + apps/roomote/drizzle/0002_brief_sentry.sql | 1 + apps/roomote/drizzle/meta/0000_snapshot.json | 164 ++ apps/roomote/drizzle/meta/0001_snapshot.json | 170 ++ apps/roomote/drizzle/meta/0002_snapshot.json | 105 + apps/roomote/drizzle/meta/_journal.json | 27 + apps/roomote/eslint.config.mjs | 4 + apps/roomote/next-env.d.ts | 5 + apps/roomote/next.config.ts | 7 + apps/roomote/package.json | 53 +- apps/roomote/scripts/build.sh | 38 + apps/roomote/scripts/dashboard.ts | 24 + .../scripts/enqueue-github-issue-job.sh | 74 + apps/roomote/src/app/api/health/route.ts | 36 + apps/roomote/src/app/api/jobs/[id]/route.ts | 48 + apps/roomote/src/app/api/jobs/route.ts | 44 + .../webhooks/github/__tests__/route.test.ts | 376 +++ .../github/handlers/__tests__/utils.test.ts | 294 +++ .../app/api/webhooks/github/handlers/index.ts | 5 + .../github/handlers/issueCommentHandler.ts | 109 + .../webhooks/github/handlers/issueHandler.ts | 45 + .../github/handlers/pullRequestHandler.ts | 71 + .../pullRequestReviewCommentHandler.ts | 70 + .../app/api/webhooks/github/handlers/utils.ts | 40 + .../src/app/api/webhooks/github/route.ts | 55 + apps/roomote/src/app/layout.tsx | 18 + apps/roomote/src/app/page.tsx | 3 + apps/roomote/src/db/index.ts | 11 + apps/roomote/src/db/schema.ts | 34 + .../src/lib/__tests__/controller.test.ts | 95 + apps/roomote/src/lib/controller.ts | 159 ++ apps/roomote/src/lib/index.ts | 2 + apps/roomote/src/lib/job.ts | 133 + apps/roomote/src/lib/jobs/fixGitHubIssue.ts | 59 + .../src/lib/jobs/processIssueComment.ts | 71 + .../src/lib/jobs/processPullRequestComment.ts | 85 + apps/roomote/src/lib/logger.ts | 86 + apps/roomote/src/lib/queue.ts | 23 + apps/roomote/src/lib/redis.ts | 8 + apps/roomote/src/lib/runTask.ts | 334 +++ apps/roomote/src/lib/slack.ts | 159 ++ apps/roomote/src/lib/utils.ts | 37 + apps/roomote/src/lib/worker.ts | 72 + apps/roomote/src/types/index.ts | 61 + apps/roomote/tsconfig.json | 15 + .../vitest.config.ts} | 4 - apps/web/.gitignore | 4 - apps/web/docker-compose.yml | 42 - apps/web/eslint.config.mjs | 74 +- apps/web/package.json | 20 +- apps/web/src/db/db.ts | 15 +- apps/web/src/i18n/locale.ts | 4 +- apps/web/tsconfig.json | 50 +- apps/web/vitest-global-setup.ts | 42 - apps/web/vitest.config.ts | 41 + ...vitest-setup.ts => vitest.setup.client.ts} | 0 apps/web/vitest.setup.server.ts | 38 + apps/web/vitest.workspace.ts | 37 - docker-compose.yml | 148 ++ package.json | 8 +- packages/config-eslint/base.js | 44 + packages/config-eslint/next.js | 22 + packages/config-eslint/package.json | 20 +- packages/config-eslint/react.js | 38 + packages/config-typescript/base.json | 19 + packages/config-typescript/cjs.json | 14 + packages/config-typescript/nextjs.json | 12 + packages/config-typescript/package.json | 5 +- .../config-typescript/vscode-library.json | 12 + packages/ipc/eslint.config.mjs | 4 + packages/ipc/package.json | 22 + packages/ipc/src/index.ts | 2 + packages/ipc/src/ipc-client.ts | 129 + packages/ipc/src/ipc-server.ts | 149 ++ packages/ipc/tsconfig.json | 8 + pnpm-lock.yaml | 2171 ++++++++++++++--- 90 files changed, 6181 insertions(+), 592 deletions(-) create mode 100644 .docker/Dockerfile.api create mode 100644 .docker/Dockerfile.base create mode 100644 .docker/Dockerfile.controller create mode 100644 .docker/Dockerfile.dashboard create mode 100644 .docker/Dockerfile.worker rename {apps/web/.docker => .docker}/scripts/clickhouse/001-create-tables.sql (100%) rename {apps/web/.docker => .docker}/scripts/postgres/create-databases.sh (100%) rename apps/web/.prettierrc.json => .prettierrc.json (100%) create mode 100644 apps/roomote/.env.example create mode 100644 apps/roomote/.gitignore create mode 100644 apps/roomote/drizzle.config.ts create mode 100644 apps/roomote/drizzle/0000_cuddly_luke_cage.sql create mode 100644 apps/roomote/drizzle/0001_fluffy_sasquatch.sql create mode 100644 apps/roomote/drizzle/0002_brief_sentry.sql create mode 100644 apps/roomote/drizzle/meta/0000_snapshot.json create mode 100644 apps/roomote/drizzle/meta/0001_snapshot.json create mode 100644 apps/roomote/drizzle/meta/0002_snapshot.json create mode 100644 apps/roomote/drizzle/meta/_journal.json create mode 100644 apps/roomote/eslint.config.mjs create mode 100644 apps/roomote/next-env.d.ts create mode 100644 apps/roomote/next.config.ts create mode 100755 apps/roomote/scripts/build.sh create mode 100644 apps/roomote/scripts/dashboard.ts create mode 100755 apps/roomote/scripts/enqueue-github-issue-job.sh create mode 100644 apps/roomote/src/app/api/health/route.ts create mode 100644 apps/roomote/src/app/api/jobs/[id]/route.ts create mode 100644 apps/roomote/src/app/api/jobs/route.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/__tests__/route.test.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/__tests__/utils.test.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/index.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/issueCommentHandler.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/issueHandler.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/pullRequestHandler.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/pullRequestReviewCommentHandler.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/handlers/utils.ts create mode 100644 apps/roomote/src/app/api/webhooks/github/route.ts create mode 100644 apps/roomote/src/app/layout.tsx create mode 100644 apps/roomote/src/app/page.tsx create mode 100644 apps/roomote/src/db/index.ts create mode 100644 apps/roomote/src/db/schema.ts create mode 100644 apps/roomote/src/lib/__tests__/controller.test.ts create mode 100644 apps/roomote/src/lib/controller.ts create mode 100644 apps/roomote/src/lib/index.ts create mode 100644 apps/roomote/src/lib/job.ts create mode 100644 apps/roomote/src/lib/jobs/fixGitHubIssue.ts create mode 100644 apps/roomote/src/lib/jobs/processIssueComment.ts create mode 100644 apps/roomote/src/lib/jobs/processPullRequestComment.ts create mode 100644 apps/roomote/src/lib/logger.ts create mode 100644 apps/roomote/src/lib/queue.ts create mode 100644 apps/roomote/src/lib/redis.ts create mode 100644 apps/roomote/src/lib/runTask.ts create mode 100644 apps/roomote/src/lib/slack.ts create mode 100644 apps/roomote/src/lib/utils.ts create mode 100644 apps/roomote/src/lib/worker.ts create mode 100644 apps/roomote/src/types/index.ts create mode 100644 apps/roomote/tsconfig.json rename apps/{web/vitest.config.mts => roomote/vitest.config.ts} (57%) delete mode 100644 apps/web/docker-compose.yml delete mode 100644 apps/web/vitest-global-setup.ts create mode 100644 apps/web/vitest.config.ts rename apps/web/{vitest-setup.ts => vitest.setup.client.ts} (100%) create mode 100644 apps/web/vitest.setup.server.ts delete mode 100644 apps/web/vitest.workspace.ts create mode 100644 docker-compose.yml create mode 100644 packages/config-eslint/base.js create mode 100644 packages/config-eslint/next.js create mode 100644 packages/config-eslint/react.js create mode 100644 packages/config-typescript/base.json create mode 100644 packages/config-typescript/cjs.json create mode 100644 packages/config-typescript/nextjs.json create mode 100644 packages/config-typescript/vscode-library.json create mode 100644 packages/ipc/eslint.config.mjs create mode 100644 packages/ipc/package.json create mode 100644 packages/ipc/src/index.ts create mode 100644 packages/ipc/src/ipc-client.ts create mode 100644 packages/ipc/src/ipc-server.ts create mode 100644 packages/ipc/tsconfig.json diff --git a/.docker/Dockerfile.api b/.docker/Dockerfile.api new file mode 100644 index 0000000000..6566096a71 --- /dev/null +++ b/.docker/Dockerfile.api @@ -0,0 +1,27 @@ +# docker compose build base api + +FROM roomote-base AS base + +WORKDIR /roo + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY packages/config-eslint/package.json ./packages/config-eslint/ +COPY packages/config-typescript/package.json ./packages/config-typescript/ +COPY packages/types/package.json ./packages/types/ +COPY packages/ipc/package.json ./packages/ipc/ +COPY apps/roomote/package.json ./apps/roomote/ + +COPY scripts/bootstrap.mjs ./scripts/ +RUN pnpm install + +COPY apps/roomote ./apps/roomote/ +COPY packages/config-eslint ./packages/config-eslint/ +COPY packages/config-typescript ./packages/config-typescript/ +COPY packages/types ./packages/types/ +COPY packages/ipc ./packages/ipc/ + +WORKDIR /roo/apps/roomote +RUN pnpm build +ENV NODE_ENV=production +EXPOSE 3001 +CMD ["pnpm", "start"] diff --git a/.docker/Dockerfile.base b/.docker/Dockerfile.base new file mode 100644 index 0000000000..3320b0b6a5 --- /dev/null +++ b/.docker/Dockerfile.base @@ -0,0 +1,31 @@ +# docker compose build base + +FROM node:20-slim AS base + +# Install pnpm +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable + +# Install common system packages +RUN apt update && \ + apt install -y \ + curl \ + git \ + vim \ + jq \ + netcat-openbsd \ + apt-transport-https \ + ca-certificates \ + gnupg \ + lsb-release \ + wget \ + gpg \ + gh \ + && rm -rf /var/lib/apt/lists/* + +# Install Docker cli +RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \ + && apt update && apt install -y docker-ce-cli \ + && rm -rf /var/lib/apt/lists/* diff --git a/.docker/Dockerfile.controller b/.docker/Dockerfile.controller new file mode 100644 index 0000000000..a2a2e00203 --- /dev/null +++ b/.docker/Dockerfile.controller @@ -0,0 +1,25 @@ +# docker compose build base controller + +FROM roomote-base AS base + +WORKDIR /roo + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY packages/config-eslint/package.json ./packages/config-eslint/ +COPY packages/config-typescript/package.json ./packages/config-typescript/ +COPY packages/types/package.json ./packages/types/ +COPY packages/ipc/package.json ./packages/ipc/ +COPY apps/roomote/package.json ./apps/roomote/ + +COPY scripts/bootstrap.mjs ./scripts/ +RUN pnpm install + +COPY apps/roomote ./apps/roomote/ +COPY packages/config-eslint ./packages/config-eslint/ +COPY packages/config-typescript ./packages/config-typescript/ +COPY packages/types ./packages/types/ +COPY packages/ipc ./packages/ipc/ + +WORKDIR /roo/apps/roomote +ENV NODE_ENV=production +CMD ["pnpm", "controller"] diff --git a/.docker/Dockerfile.dashboard b/.docker/Dockerfile.dashboard new file mode 100644 index 0000000000..f04160d9d2 --- /dev/null +++ b/.docker/Dockerfile.dashboard @@ -0,0 +1,25 @@ +# docker compose build base dashboard + +FROM roomote-base AS base + +WORKDIR /roo + +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY packages/config-eslint/package.json ./packages/config-eslint/ +COPY packages/config-typescript/package.json ./packages/config-typescript/ +COPY packages/types/package.json ./packages/types/ +COPY packages/ipc/package.json ./packages/ipc/ +COPY apps/roomote/package.json ./apps/roomote/ + +COPY scripts/bootstrap.mjs ./scripts/ +RUN pnpm install + +COPY apps/roomote ./apps/roomote/ +COPY packages/config-eslint ./packages/config-eslint/ +COPY packages/config-typescript ./packages/config-typescript/ +COPY packages/types ./packages/types/ +COPY packages/ipc ./packages/ipc/ + +WORKDIR /roo/apps/roomote +EXPOSE 3002 +CMD ["pnpm", "dashboard"] diff --git a/.docker/Dockerfile.worker b/.docker/Dockerfile.worker new file mode 100644 index 0000000000..b1a21c8a62 --- /dev/null +++ b/.docker/Dockerfile.worker @@ -0,0 +1,62 @@ +# docker compose build worker +# Note: Requires $GH_TOKEN to be set as build argument. + +FROM roomote-base AS base + +# Install additional worker-specific packages +RUN apt update && \ + apt install -y \ + xvfb \ + && rm -rf /var/lib/apt/lists/* + +# Install VS Code +RUN wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg \ + && install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg \ + && echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" | tee /etc/apt/sources.list.d/vscode.list > /dev/null \ + && rm -f packages.microsoft.gpg \ + && apt update && apt install -y code \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /roo + +# Install extensions +RUN mkdir -p /roo/.vscode \ + && code --no-sandbox --user-data-dir /roo/.vscode --install-extension dbaeumer.vscode-eslint \ + && code --no-sandbox --user-data-dir /roo/.vscode --install-extension esbenp.prettier-vscode \ + && code --no-sandbox --user-data-dir /roo/.vscode --install-extension csstools.postcss \ + && code --no-sandbox --user-data-dir /roo/.vscode --install-extension RooVeterinaryInc.roo-cline + +# Clone repo (requires $GH_TOKEN) +ARG GH_TOKEN +ENV GH_TOKEN=${GH_TOKEN} +WORKDIR /roo/repos +RUN git config --global user.email "chris@roocode.com" +RUN git config --global user.name "Roo Code" +RUN git config --global credential.helper store +RUN echo "https://oauth2:${GH_TOKEN}@github.com" > ~/.git-credentials +RUN gh repo clone RooCodeInc/Roo-Code +WORKDIR /roo/repos/Roo-Code +RUN gh repo set-default RooCodeInc/Roo-Code +RUN pnpm install + +# Install dependencies +WORKDIR /roo +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ +COPY packages/config-eslint/package.json ./packages/config-eslint/ +COPY packages/config-typescript/package.json ./packages/config-typescript/ +COPY packages/types/package.json ./packages/types/ +COPY packages/ipc/package.json ./packages/ipc/ +COPY apps/roomote/package.json ./apps/roomote/ + +COPY scripts/bootstrap.mjs ./scripts/ +RUN pnpm install + +COPY apps/roomote ./apps/roomote/ +COPY packages/config-eslint ./packages/config-eslint/ +COPY packages/config-typescript ./packages/config-typescript/ +COPY packages/types ./packages/types/ +COPY packages/ipc ./packages/ipc/ + +WORKDIR /roo/apps/roomote +ENV NODE_ENV=production +CMD ["pnpm", "worker"] diff --git a/apps/web/.docker/scripts/clickhouse/001-create-tables.sql b/.docker/scripts/clickhouse/001-create-tables.sql similarity index 100% rename from apps/web/.docker/scripts/clickhouse/001-create-tables.sql rename to .docker/scripts/clickhouse/001-create-tables.sql diff --git a/apps/web/.docker/scripts/postgres/create-databases.sh b/.docker/scripts/postgres/create-databases.sh similarity index 100% rename from apps/web/.docker/scripts/postgres/create-databases.sh rename to .docker/scripts/postgres/create-databases.sh diff --git a/.gitignore b/.gitignore index 2fb2180020..ba03f70657 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ node_modules Thumbs.db # .env +.env .env*.local .env*.production @@ -17,3 +18,7 @@ Thumbs.db # vercel .vercel + +# docker +.docker/data +.docker/logs diff --git a/apps/web/.prettierrc.json b/.prettierrc.json similarity index 100% rename from apps/web/.prettierrc.json rename to .prettierrc.json diff --git a/README.md b/README.md index 5da8ba492c..a415fa96bf 100644 --- a/README.md +++ b/README.md @@ -1 +1,27 @@ # Roo Code Cloud Monorepo + +### Web + +To start the web app you first need to start the `postgres` and `clickhouse` docker services: + +```sh +pnpm db:up +``` + +This will automatically sync your database to the latest version of the schema. If you need to reset your database at any point, you can run: + +```sh +pnpm db:reset +``` + +Then you can start the app in dev mode: + +```sh +pnpm --filter @roo-code-cloud/web dev +``` + +The app will be available at (localhost:3000)[http://localhost:3000/]. + +### Roomote + +TBD diff --git a/apps/roomote/.env.example b/apps/roomote/.env.example new file mode 100644 index 0000000000..0e1eb3ae79 --- /dev/null +++ b/apps/roomote/.env.example @@ -0,0 +1,9 @@ +DATABASE_URL=postgresql://postgres:password@localhost:5433/cloud_agents +REDIS_URL=redis://localhost:6380 + +GH_WEBHOOK_SECRET=your-webhook-secret-here +GH_TOKEN=your-token-here + +OPENROUTER_API_KEY=sk-or-v1-... + +SLACK_API_TOKEN=xoxb-... diff --git a/apps/roomote/.gitignore b/apps/roomote/.gitignore new file mode 100644 index 0000000000..37799ec24d --- /dev/null +++ b/apps/roomote/.gitignore @@ -0,0 +1,3 @@ +# next.js +/.next +/out diff --git a/apps/roomote/drizzle.config.ts b/apps/roomote/drizzle.config.ts new file mode 100644 index 0000000000..9233f37d33 --- /dev/null +++ b/apps/roomote/drizzle.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + schema: './src/db/schema.ts', + out: './drizzle', + dialect: 'postgresql', + dbCredentials: { + url: process.env.DATABASE_URL!, + }, +}); diff --git a/apps/roomote/drizzle/0000_cuddly_luke_cage.sql b/apps/roomote/drizzle/0000_cuddly_luke_cage.sql new file mode 100644 index 0000000000..2535f219e2 --- /dev/null +++ b/apps/roomote/drizzle/0000_cuddly_luke_cage.sql @@ -0,0 +1,21 @@ +CREATE TABLE "cloud_jobs" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "cloud_jobs_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "type" text NOT NULL, + "status" text DEFAULT 'pending' NOT NULL, + "payload" jsonb NOT NULL, + "result" jsonb, + "error" text, + "created_at" timestamp DEFAULT now() NOT NULL, + "started_at" timestamp, + "completed_at" timestamp +); +--> statement-breakpoint +CREATE TABLE "cloud_tasks" ( + "id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "cloud_tasks_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1), + "job_id" integer NOT NULL, + "task_id" integer, + "container_id" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "cloud_tasks" ADD CONSTRAINT "cloud_tasks_job_id_cloud_jobs_id_fk" FOREIGN KEY ("job_id") REFERENCES "public"."cloud_jobs"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/apps/roomote/drizzle/0001_fluffy_sasquatch.sql b/apps/roomote/drizzle/0001_fluffy_sasquatch.sql new file mode 100644 index 0000000000..aeddbe02a8 --- /dev/null +++ b/apps/roomote/drizzle/0001_fluffy_sasquatch.sql @@ -0,0 +1 @@ +ALTER TABLE "cloud_jobs" ADD COLUMN "slack_thread_ts" text; \ No newline at end of file diff --git a/apps/roomote/drizzle/0002_brief_sentry.sql b/apps/roomote/drizzle/0002_brief_sentry.sql new file mode 100644 index 0000000000..2f22f7825c --- /dev/null +++ b/apps/roomote/drizzle/0002_brief_sentry.sql @@ -0,0 +1 @@ +DROP TABLE "cloud_tasks" CASCADE; \ No newline at end of file diff --git a/apps/roomote/drizzle/meta/0000_snapshot.json b/apps/roomote/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000000..ea3dda697f --- /dev/null +++ b/apps/roomote/drizzle/meta/0000_snapshot.json @@ -0,0 +1,164 @@ +{ + "id": "8f65bfed-78de-4e22-a15f-36de8afe5f2e", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.cloud_jobs": { + "name": "cloud_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "cloud_jobs_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_tasks": { + "name": "cloud_tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "cloud_tasks_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "job_id": { + "name": "job_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "container_id": { + "name": "container_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "cloud_tasks_job_id_cloud_jobs_id_fk": { + "name": "cloud_tasks_job_id_cloud_jobs_id_fk", + "tableFrom": "cloud_tasks", + "tableTo": "cloud_jobs", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/roomote/drizzle/meta/0001_snapshot.json b/apps/roomote/drizzle/meta/0001_snapshot.json new file mode 100644 index 0000000000..14c0e9c48c --- /dev/null +++ b/apps/roomote/drizzle/meta/0001_snapshot.json @@ -0,0 +1,170 @@ +{ + "id": "4cae0c18-141d-40a3-acc8-38fcd7f07534", + "prevId": "8f65bfed-78de-4e22-a15f-36de8afe5f2e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.cloud_jobs": { + "name": "cloud_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "cloud_jobs_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cloud_tasks": { + "name": "cloud_tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "cloud_tasks_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "job_id": { + "name": "job_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "task_id": { + "name": "task_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "container_id": { + "name": "container_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "cloud_tasks_job_id_cloud_jobs_id_fk": { + "name": "cloud_tasks_job_id_cloud_jobs_id_fk", + "tableFrom": "cloud_tasks", + "tableTo": "cloud_jobs", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/roomote/drizzle/meta/0002_snapshot.json b/apps/roomote/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000000..dbbc7d7cdc --- /dev/null +++ b/apps/roomote/drizzle/meta/0002_snapshot.json @@ -0,0 +1,105 @@ +{ + "id": "16afebd7-b27a-457e-b3d5-4de50a597c2e", + "prevId": "4cae0c18-141d-40a3-acc8-38fcd7f07534", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.cloud_jobs": { + "name": "cloud_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "always", + "name": "cloud_jobs_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "2147483647", + "cache": "1", + "cycle": false + } + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "slack_thread_ts": { + "name": "slack_thread_ts", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/apps/roomote/drizzle/meta/_journal.json b/apps/roomote/drizzle/meta/_journal.json new file mode 100644 index 0000000000..179f2cdbf7 --- /dev/null +++ b/apps/roomote/drizzle/meta/_journal.json @@ -0,0 +1,27 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1749740498648, + "tag": "0000_cuddly_luke_cage", + "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1750099429125, + "tag": "0001_fluffy_sasquatch", + "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1750107620472, + "tag": "0002_brief_sentry", + "breakpoints": true + } + ] +} diff --git a/apps/roomote/eslint.config.mjs b/apps/roomote/eslint.config.mjs new file mode 100644 index 0000000000..00b53054bd --- /dev/null +++ b/apps/roomote/eslint.config.mjs @@ -0,0 +1,4 @@ +import { nextJsConfig } from '@roo-code-cloud/config-eslint/next-js'; + +/** @type {import("eslint").Linter.Config} */ +export default [...nextJsConfig]; diff --git a/apps/roomote/next-env.d.ts b/apps/roomote/next-env.d.ts new file mode 100644 index 0000000000..1b3be0840f --- /dev/null +++ b/apps/roomote/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/roomote/next.config.ts b/apps/roomote/next.config.ts new file mode 100644 index 0000000000..9d3c27b2e8 --- /dev/null +++ b/apps/roomote/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from 'next'; + +const nextConfig: NextConfig = { + serverExternalPackages: ['postgres', 'ioredis', 'bullmq'], +}; + +export default nextConfig; diff --git a/apps/roomote/package.json b/apps/roomote/package.json index c7398c792b..b51dea3908 100644 --- a/apps/roomote/package.json +++ b/apps/roomote/package.json @@ -1,4 +1,55 @@ { "name": "@roo-code-cloud/roomote", - "private": true + "private": true, + "type": "module", + "scripts": { + "lint": "next lint --max-warnings 0", + "check-types": "tsc --noEmit", + "test": "vitest", + "dev": "concurrently \"next dev --port 3001\" \"ngrok http 3001 --domain cte.ngrok.dev\"", + "build": "next build", + "start": "next start --port 3001", + "clean": "rimraf .next .turbo", + "drizzle-kit": "dotenvx run -f .env -- tsx node_modules/drizzle-kit/bin.cjs", + "db:generate": "pnpm drizzle-kit generate", + "db:migrate": "pnpm drizzle-kit migrate", + "db:push": "pnpm drizzle-kit push", + "db:check": "pnpm drizzle-kit check", + "db:studio": "pnpm drizzle-kit studio", + "services:start": "docker compose up -d db redis dashboard", + "services:stop": "docker compose down dashboard redis db", + "worker": "dotenvx run -f .env -- tsx src/lib/worker.ts", + "controller": "dotenvx run -f .env -- tsx src/lib/controller.ts", + "dashboard": "tsx scripts/dashboard.ts" + }, + "dependencies": { + "@bull-board/api": "^6.10.1", + "@bull-board/express": "^6.10.1", + "@bull-board/ui": "^6.10.1", + "@roo-code/types": "^1.26.0", + "@roo-code-cloud/ipc": "workspace:^", + "bullmq": "^5.37.0", + "drizzle-orm": "^0.44.1", + "execa": "^9.6.0", + "express": "^5.1.0", + "ioredis": "^5.4.3", + "next": "^15.3.3", + "p-wait-for": "^5.0.2", + "postgres": "^3.4.7", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "zod": "^3.25.41" + }, + "devDependencies": { + "@roo-code-cloud/config-eslint": "workspace:^", + "@roo-code-cloud/config-typescript": "workspace:^", + "@types/express": "^5.0.3", + "@types/node": "^22.15.20", + "@types/react": "^19.1.6", + "@types/react-dom": "^19.1.6", + "concurrently": "^9.1.0", + "drizzle-kit": "^0.31.1", + "tsx": "^4.19.3", + "vitest": "^3.2.3" + } } diff --git a/apps/roomote/scripts/build.sh b/apps/roomote/scripts/build.sh new file mode 100755 index 0000000000..1bc54d1e26 --- /dev/null +++ b/apps/roomote/scripts/build.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Build script for roomote services. +# This ensures the base image is built before dependent services. + +set -e + +build_service() { + local service=$1 + + case $service in + "dashboard"|"api"|"worker"|"controller") + echo "Building base image first..." + docker compose build base + echo "Building $service..." + docker compose build $service + ;; + "base") + echo "Building base image..." + docker compose build base + ;; + *) + echo "Building $service..." + docker compose build $service + ;; + esac +} + +if [ $# -eq 0 ]; then + echo "Usage: $0 " + echo "Available services: base, dashboard, api, worker, controller, db, redis" + echo "Example: $0 dashboard" + exit 1 +fi + +build_service $1 + +echo "Build completed successfully!" diff --git a/apps/roomote/scripts/dashboard.ts b/apps/roomote/scripts/dashboard.ts new file mode 100644 index 0000000000..7600bf2251 --- /dev/null +++ b/apps/roomote/scripts/dashboard.ts @@ -0,0 +1,24 @@ +import IORedis from 'ioredis'; +import { Queue } from 'bullmq'; +import { ExpressAdapter } from '@bull-board/express'; +import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'; +import { createBullBoard } from '@bull-board/api'; +import express from 'express'; +import type { Express, Request, Response } from 'express'; + +const redis = new IORedis(process.env.REDIS_URL || 'redis://localhost:6380', { + maxRetriesPerRequest: null, +}); +const queue = new Queue('roomote', { connection: redis }); + +const serverAdapter = new ExpressAdapter(); +serverAdapter.setBasePath('/admin/queues'); +createBullBoard({ queues: [new BullMQAdapter(queue)], serverAdapter }); + +const port = 3002; +const app: Express = express(); +app.use('/admin/queues', serverAdapter.getRouter()); +app.use('/', (req: Request, res: Response) => res.redirect('/admin/queues')); +app.listen(port, () => + console.log(`Bull Board running on: http://localhost:${port}/admin/queues`), +); diff --git a/apps/roomote/scripts/enqueue-github-issue-job.sh b/apps/roomote/scripts/enqueue-github-issue-job.sh new file mode 100755 index 0000000000..5ec7448cc6 --- /dev/null +++ b/apps/roomote/scripts/enqueue-github-issue-job.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +REPO="RooCodeInc/Roo-Code" +ISSUE_NUMBER="$1" +BASE_URL="http://localhost:3001" +JOBS_ENDPOINT="$BASE_URL/api/jobs" + +if [ -z "$ISSUE_NUMBER" ]; then + echo "Usage: $0 [repo]" + echo "" + echo "Examples:" + echo " $0 4567 # Fetch issue #4567 from RooCodeInc/Roo-Code" + echo " $0 123 owner/repo # Fetch issue #123 from owner/repo" + echo "" + echo "This script fetches real GitHub issue data and enqueues it as a job." + exit 1 +fi + +if [ -n "$2" ]; then + REPO="$2" +fi + +if ! command -v gh &> /dev/null; then + echo "Error: GitHub CLI (gh) is not installed. Please install it first." + echo "Visit: https://cli.github.com/" + exit 1 +fi + +if ! gh auth status &> /dev/null; then + echo "Error: Not authenticated with GitHub CLI. Please run 'gh auth login' first." + exit 1 +fi + +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed. Please install it first." + echo "Visit: https://jqlang.github.io/jq/download/" + exit 1 +fi + +echo "Fetching issue #${ISSUE_NUMBER} from ${REPO}..." + +ISSUE_DATA=$(gh issue view ${ISSUE_NUMBER} --repo ${REPO} --json title,body,labels 2>/dev/null) + +if [ $? -ne 0 ]; then + echo "Error: Failed to fetch issue #${ISSUE_NUMBER} from ${REPO}" + echo "Please check that the issue exists and you have access to the repository." + exit 1 +fi + +TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') +BODY=$(echo "$ISSUE_DATA" | jq -r '.body // ""') +LABELS=$(echo "$ISSUE_DATA" | jq -r '[.labels[].name] | @json') + +JSON_PAYLOAD=$(jq -n \ + --arg type "github.issue.fix" \ + --arg repo "$REPO" \ + --argjson issue "$ISSUE_NUMBER" \ + --arg title "$TITLE" \ + --arg body "$BODY" \ + --argjson labels "$LABELS" \ + '{ + type: $type, + payload: { + repo: $repo, + issue: $issue, + title: $title, + body: $body, + labels: $labels + } + }') + +echo "curl -X POST \"$JOBS_ENDPOINT\" -H \"Content-Type: application/json\" -d \"$JSON_PAYLOAD\" -w \"\nStatus: %{http_code}\n\n\"" + +curl -X POST "$JOBS_ENDPOINT" -H "Content-Type: application/json" -d "$JSON_PAYLOAD" -w "\nStatus: %{http_code}\n\n" diff --git a/apps/roomote/src/app/api/health/route.ts b/apps/roomote/src/app/api/health/route.ts new file mode 100644 index 0000000000..8141a28259 --- /dev/null +++ b/apps/roomote/src/app/api/health/route.ts @@ -0,0 +1,36 @@ +import { NextResponse } from 'next/server'; + +import { db } from '@/db'; +import { redis } from '@/lib'; + +export async function GET() { + try { + const services = { database: false, redis: false }; + + try { + await db.execute('SELECT 1'); + services.database = true; + } catch (error) { + console.error('Database health check failed:', error); + } + + try { + await redis.ping(); + services.redis = true; + } catch (error) { + console.error('Redis health check failed:', error); + } + + const allHealthy = Object.values(services).every(Boolean); + return NextResponse.json( + { status: allHealthy ? 'ok' : 'error', services }, + { status: allHealthy ? 200 : 500 }, + ); + } catch (error) { + console.error('Health check error:', error); + return NextResponse.json( + { status: 'error', error: 'Internal server error' }, + { status: 500 }, + ); + } +} diff --git a/apps/roomote/src/app/api/jobs/[id]/route.ts b/apps/roomote/src/app/api/jobs/[id]/route.ts new file mode 100644 index 0000000000..05c02c869f --- /dev/null +++ b/apps/roomote/src/app/api/jobs/[id]/route.ts @@ -0,0 +1,48 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { eq } from 'drizzle-orm'; + +import { db, cloudJobs } from '@/db'; + +type Params = Promise<{ id: string }>; + +export async function GET( + request: NextRequest, + { params }: { params: Params }, +) { + try { + const { id } = await params; + const jobId = parseInt(id, 10); + + if (isNaN(jobId)) { + return NextResponse.json({ error: 'Invalid job ID' }, { status: 400 }); + } + + const [job] = await db + .select() + .from(cloudJobs) + .where(eq(cloudJobs.id, jobId)) + .limit(1); + + if (!job) { + return NextResponse.json({ error: 'Job not found' }, { status: 404 }); + } + + return NextResponse.json({ + id: job.id, + type: job.type, + status: job.status, + payload: job.payload, + result: job.result, + error: job.error, + createdAt: job.createdAt, + startedAt: job.startedAt, + completedAt: job.completedAt, + }); + } catch (error) { + console.error('Error fetching job:', error); + return NextResponse.json( + { error: 'Internal server error' }, + { status: 500 }, + ); + } +} diff --git a/apps/roomote/src/app/api/jobs/route.ts b/apps/roomote/src/app/api/jobs/route.ts new file mode 100644 index 0000000000..50bdc20e24 --- /dev/null +++ b/apps/roomote/src/app/api/jobs/route.ts @@ -0,0 +1,44 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; + +import { createJobSchema } from '@/types'; +import { db, cloudJobs } from '@/db'; +import { enqueue } from '@/lib'; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + const values = createJobSchema.parse(body); + + const [job] = await db + .insert(cloudJobs) + .values({ ...values, status: 'pending' }) + .returning(); + + if (!job) { + throw new Error('Failed to create `cloudJobs` record.'); + } + + const enqueuedJob = await enqueue({ jobId: job.id, ...values }); + + return NextResponse.json({ + message: 'job_enqueued', + jobId: job.id, + enqueuedJobId: enqueuedJob.id, + }); + } catch (error) { + console.error('Create Job Error:', error); + + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: 'bad_request', details: error.errors }, + { status: 400 }, + ); + } + + return NextResponse.json( + { error: 'internal_server_error' }, + { status: 500 }, + ); + } +} diff --git a/apps/roomote/src/app/api/webhooks/github/__tests__/route.test.ts b/apps/roomote/src/app/api/webhooks/github/__tests__/route.test.ts new file mode 100644 index 0000000000..f778d44001 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/__tests__/route.test.ts @@ -0,0 +1,376 @@ +// npx vitest src/app/api/webhooks/github/__tests__/route.test.ts + +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; + +const mockVerifySignature = vi.fn(); +const mockHandleIssueEvent = vi.fn(); +const mockHandlePullRequestEvent = vi.fn(); +const mockHandleIssueCommentEvent = vi.fn(); +const mockHandlePullRequestReviewCommentEvent = vi.fn(); + +vi.mock('../handlers', () => ({ + verifySignature: mockVerifySignature, + handleIssueEvent: mockHandleIssueEvent, + handlePullRequestEvent: mockHandlePullRequestEvent, + handleIssueCommentEvent: mockHandleIssueCommentEvent, + handlePullRequestReviewCommentEvent: mockHandlePullRequestReviewCommentEvent, +})); + +vi.mock('next/server', async () => { + const actual = await vi.importActual('next/server'); + return { + ...actual, + NextResponse: { + json: vi.fn().mockReturnValue({ mocked: true }), + }, + }; +}); + +describe('GitHub Webhook Route', () => { + let POST: typeof import('../route').POST; + + const validSignature = 'sha256=test-signature'; + const validBody = JSON.stringify({ action: 'opened', number: 123 }); + const webhookSecret = 'test-secret'; + + beforeEach(async () => { + vi.clearAllMocks(); + process.env.GH_WEBHOOK_SECRET = webhookSecret; + // Import the POST function after mocks are set up. + const routeModule = await import('../route'); + POST = routeModule.POST; + }); + + afterEach(() => { + vi.restoreAllMocks(); + delete process.env.GH_WEBHOOK_SECRET; + }); + + const createMockRequest = ( + headers: Record, + body: string = validBody, + ) => { + return { + headers: { + get: vi.fn((name: string) => headers[name] || null), + }, + text: vi.fn().mockResolvedValue(body), + } as unknown as NextRequest; + }; + + describe('signature validation', () => { + it('should return 400 when signature header is missing', async () => { + const request = createMockRequest({}); + + await POST(request); + + expect(NextResponse.json).toHaveBeenCalledWith( + { error: 'missing_signature' }, + { status: 400 }, + ); + }); + + it('should return 401 when signature is invalid', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + }); + mockVerifySignature.mockReturnValue(false); + + await POST(request); + + expect(mockVerifySignature).toHaveBeenCalledWith( + validBody, + validSignature, + webhookSecret, + ); + expect(NextResponse.json).toHaveBeenCalledWith( + { error: 'invalid_signature' }, + { status: 401 }, + ); + }); + + it('should proceed when signature is valid', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issues', + }); + mockVerifySignature.mockReturnValue(true); + mockHandleIssueEvent.mockResolvedValue({ status: 200 }); + + await POST(request); + + expect(mockVerifySignature).toHaveBeenCalledWith( + validBody, + validSignature, + webhookSecret, + ); + expect(mockHandleIssueEvent).toHaveBeenCalledWith(validBody); + }); + }); + + describe('event handling', () => { + beforeEach(() => { + mockVerifySignature.mockReturnValue(true); + }); + + it('should handle issues event', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issues', + }); + const expectedResponse = { status: 200, data: 'success' }; + mockHandleIssueEvent.mockResolvedValue(expectedResponse); + + const result = await POST(request); + + expect(mockHandleIssueEvent).toHaveBeenCalledWith(validBody); + expect(result).toBe(expectedResponse); + }); + + it('should handle pull_request event', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'pull_request', + }); + const expectedResponse = { status: 200, data: 'success' }; + mockHandlePullRequestEvent.mockResolvedValue(expectedResponse); + + const result = await POST(request); + + expect(mockHandlePullRequestEvent).toHaveBeenCalledWith(validBody); + expect(result).toBe(expectedResponse); + }); + + it('should handle issue_comment event', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issue_comment', + }); + const expectedResponse = { status: 200, data: 'success' }; + mockHandleIssueCommentEvent.mockResolvedValue(expectedResponse); + + const result = await POST(request); + + expect(mockHandleIssueCommentEvent).toHaveBeenCalledWith(validBody); + expect(result).toBe(expectedResponse); + }); + + it('should handle pull_request_review_comment event', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'pull_request_review_comment', + }); + const expectedResponse = { status: 200, data: 'success' }; + mockHandlePullRequestReviewCommentEvent.mockResolvedValue( + expectedResponse, + ); + + const result = await POST(request); + + expect(mockHandlePullRequestReviewCommentEvent).toHaveBeenCalledWith( + validBody, + ); + expect(result).toBe(expectedResponse); + }); + + it('should ignore unknown events', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'unknown_event', + }); + + await POST(request); + + expect(NextResponse.json).toHaveBeenCalledWith({ + message: 'event_ignored', + }); + expect(mockHandleIssueEvent).not.toHaveBeenCalled(); + expect(mockHandlePullRequestEvent).not.toHaveBeenCalled(); + expect(mockHandleIssueCommentEvent).not.toHaveBeenCalled(); + expect(mockHandlePullRequestReviewCommentEvent).not.toHaveBeenCalled(); + }); + + it('should handle missing event header', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + }); + + await POST(request); + + expect(NextResponse.json).toHaveBeenCalledWith({ + message: 'event_ignored', + }); + }); + }); + + describe('error handling', () => { + beforeEach(() => { + mockVerifySignature.mockReturnValue(true); + }); + + it('should handle ZodError with 400 status', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issues', + }); + const zodError = new z.ZodError([ + { + code: 'invalid_type', + expected: 'string', + received: 'number', + path: ['test'], + message: 'Expected string, received number', + }, + ]); + mockHandleIssueEvent.mockRejectedValue(zodError); + + await POST(request); + + expect(NextResponse.json).toHaveBeenCalledWith( + { + error: 'bad_request', + details: zodError.errors, + }, + { status: 400 }, + ); + }); + + it('should handle generic errors with 500 status', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issues', + }); + const genericError = new Error('Database connection failed'); + mockHandleIssueEvent.mockRejectedValue(genericError); + + const consoleSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await POST(request); + + expect(consoleSpy).toHaveBeenCalledWith( + 'GitHub Webhook Error:', + genericError, + ); + expect(NextResponse.json).toHaveBeenCalledWith( + { error: 'internal_server_error' }, + { status: 500 }, + ); + + consoleSpy.mockRestore(); + }); + + it('should handle errors during signature verification', async () => { + const request = createMockRequest({ + 'x-hub-signature-256': validSignature, + }); + mockVerifySignature.mockImplementation(() => { + throw new Error('Signature verification failed'); + }); + + const consoleSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await POST(request); + + expect(consoleSpy).toHaveBeenCalledWith( + 'GitHub Webhook Error:', + expect.any(Error), + ); + expect(NextResponse.json).toHaveBeenCalledWith( + { error: 'internal_server_error' }, + { status: 500 }, + ); + + consoleSpy.mockRestore(); + }); + + it('should handle errors when reading request body', async () => { + const request = { + headers: { + get: vi.fn().mockReturnValue(validSignature), + }, + text: vi.fn().mockRejectedValue(new Error('Failed to read body')), + } as unknown as NextRequest; + + const consoleSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await POST(request); + + expect(consoleSpy).toHaveBeenCalledWith( + 'GitHub Webhook Error:', + expect.any(Error), + ); + expect(NextResponse.json).toHaveBeenCalledWith( + { error: 'internal_server_error' }, + { status: 500 }, + ); + + consoleSpy.mockRestore(); + }); + }); + + describe('integration scenarios', () => { + beforeEach(() => { + mockVerifySignature.mockReturnValue(true); + }); + + it('should process a complete valid webhook request', async () => { + const issuePayload = { + action: 'opened', + issue: { + number: 123, + title: 'Test issue', + body: 'This is a test issue', + }, + }; + const request = createMockRequest( + { + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issues', + }, + JSON.stringify(issuePayload), + ); + const expectedResponse = { status: 200, jobId: 'job-123' }; + mockHandleIssueEvent.mockResolvedValue(expectedResponse); + + const result = await POST(request); + + expect(request.text).toHaveBeenCalled(); + expect(mockVerifySignature).toHaveBeenCalledWith( + JSON.stringify(issuePayload), + validSignature, + webhookSecret, + ); + expect(mockHandleIssueEvent).toHaveBeenCalledWith( + JSON.stringify(issuePayload), + ); + expect(result).toBe(expectedResponse); + }); + + it('should handle empty request body', async () => { + const request = createMockRequest( + { + 'x-hub-signature-256': validSignature, + 'x-github-event': 'issues', + }, + '', + ); + mockHandleIssueEvent.mockResolvedValue({ status: 200 }); + + await POST(request); + + expect(mockVerifySignature).toHaveBeenCalledWith( + '', + validSignature, + webhookSecret, + ); + expect(mockHandleIssueEvent).toHaveBeenCalledWith(''); + }); + }); +}); diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/__tests__/utils.test.ts b/apps/roomote/src/app/api/webhooks/github/handlers/__tests__/utils.test.ts new file mode 100644 index 0000000000..d535591a5a --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/__tests__/utils.test.ts @@ -0,0 +1,294 @@ +// npx vitest src/app/api/webhooks/github/handlers/__tests__/utils.test.ts + +import { createHmac } from 'crypto'; + +vi.mock('@/db', () => ({ + db: { + insert: vi.fn(), + }, + cloudJobs: {}, +})); + +vi.mock('@/lib', () => ({ + enqueue: vi.fn(), +})); + +describe('GitHub Webhook Utils', () => { + let verifySignature: typeof import('../utils').verifySignature; + let createAndEnqueueJob: typeof import('../utils').createAndEnqueueJob; + let mockDb: { insert: ReturnType }; + let mockEnqueue: ReturnType; + + beforeEach(async () => { + vi.clearAllMocks(); + + const dbModule = await import('@/db'); + const libModule = await import('@/lib'); + mockDb = dbModule.db as unknown as { insert: ReturnType }; + mockEnqueue = libModule.enqueue as unknown as ReturnType; + + const utilsModule = await import('../utils'); + verifySignature = utilsModule.verifySignature; + createAndEnqueueJob = utilsModule.createAndEnqueueJob; + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe('verifySignature', () => { + const secret = 'test-secret'; + + it('should return true for valid signature', () => { + const body = 'test body'; + // Calculate the actual HMAC for this body and secret. + const actualHash = createHmac('sha256', secret) + .update(body, 'utf8') + .digest('hex'); + const validSignature = `sha256=${actualHash}`; + + const result = verifySignature(body, validSignature, secret); + + expect(result).toBe(true); + }); + + it('should return false for invalid signature', () => { + const body = 'test body'; + const invalidSignature = 'sha256=invalid'; + + const result = verifySignature(body, invalidSignature, secret); + + expect(result).toBe(false); + }); + + it('should handle signature without sha256= prefix', () => { + const body = 'test body'; + const signature = '1234567890abcdef'; + + const result = verifySignature(body, signature, secret); + + // This will be false since we're not mocking crypto properly for this test. + expect(typeof result).toBe('boolean'); + }); + + it('should work with empty body', () => { + const body = ''; + const signature = 'sha256=somehash'; + + const result = verifySignature(body, signature, secret); + + expect(typeof result).toBe('boolean'); + }); + + it('should work with special characters in body', () => { + const body = '{"test": "value with 特殊字符 and émojis 🎉"}'; + const signature = 'sha256=somehash'; + + const result = verifySignature(body, signature, secret); + + expect(typeof result).toBe('boolean'); + }); + }); + + describe('createAndEnqueueJob', () => { + const mockJob = { id: 123 }; + const mockEnqueuedJob = { id: 'enqueued-123' }; + const mockCloudJobs = {}; + + beforeEach(() => { + mockDb.insert.mockReturnValue({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([mockJob]), + }), + }); + mockEnqueue.mockResolvedValue(mockEnqueuedJob); + }); + + it('should create and enqueue a job successfully', async () => { + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test issue', + body: 'Test body', + }; + + const result = await createAndEnqueueJob(type, payload); + + expect(mockDb.insert).toHaveBeenCalledWith(mockCloudJobs); + expect(mockEnqueue).toHaveBeenCalledWith({ + jobId: mockJob.id, + type, + payload, + }); + expect(result).toEqual({ + jobId: mockJob.id, + enqueuedJobId: mockEnqueuedJob.id, + }); + }); + + it('should throw error when database insert fails', async () => { + mockDb.insert.mockReturnValue({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([]), + }), + }); + + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test', + body: 'Test', + }; + + await expect(createAndEnqueueJob(type, payload)).rejects.toThrow( + 'Failed to create `cloudJobs` record.', + ); + }); + + it('should throw error when enqueue fails to return job ID', async () => { + mockEnqueue.mockResolvedValue({ id: null }); + + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test', + body: 'Test', + }; + + await expect(createAndEnqueueJob(type, payload)).rejects.toThrow( + 'Failed to get enqueued job ID.', + ); + }); + + it('should throw error when enqueue returns undefined', async () => { + mockEnqueue.mockResolvedValue({}); + + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test', + body: 'Test', + }; + + await expect(createAndEnqueueJob(type, payload)).rejects.toThrow( + 'Failed to get enqueued job ID.', + ); + }); + + it('should handle different job types', async () => { + const type = 'github.pr.comment.respond'; + const payload = { + repo: 'test/repo', + prNumber: 456, + prTitle: 'Test PR', + prBody: 'Test PR body', + prBranch: 'feature/test', + baseRef: 'main', + commentId: 789, + commentBody: 'Test comment', + commentAuthor: 'testuser', + commentType: 'issue_comment' as const, + commentUrl: 'https://github.com/test/repo/issues/456#issuecomment-789', + }; + + const result = await createAndEnqueueJob(type, payload); + + expect(mockEnqueue).toHaveBeenCalledWith({ + jobId: mockJob.id, + type, + payload, + }); + expect(result).toEqual({ + jobId: mockJob.id, + enqueuedJobId: mockEnqueuedJob.id, + }); + }); + + it('should log the enqueued job', async () => { + const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test', + body: 'Test', + }; + + await createAndEnqueueJob(type, payload); + + expect(consoleSpy).toHaveBeenCalledWith( + `🔗 Enqueued ${type} job (id: ${mockJob.id}) ->`, + payload, + ); + + consoleSpy.mockRestore(); + }); + + it('should handle database connection errors', async () => { + mockDb.insert.mockImplementation(() => { + throw new Error('Database connection failed'); + }); + + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test', + body: 'Test', + }; + + await expect(createAndEnqueueJob(type, payload)).rejects.toThrow( + 'Database connection failed', + ); + }); + + it('should handle enqueue service errors', async () => { + mockEnqueue.mockRejectedValue(new Error('Queue service unavailable')); + + const type = 'github.issue.fix'; + const payload = { + repo: 'test/repo', + issue: 123, + title: 'Test', + body: 'Test', + }; + + await expect(createAndEnqueueJob(type, payload)).rejects.toThrow( + 'Queue service unavailable', + ); + }); + }); + + describe('integration tests', () => { + it('should work together in a realistic scenario', async () => { + // Test job creation in a realistic webhook scenario. + const mockJob = { id: 456 }; + const mockEnqueuedJob = { id: 'job-456' }; + + mockDb.insert.mockReturnValue({ + values: vi.fn().mockReturnValue({ + returning: vi.fn().mockResolvedValue([mockJob]), + }), + }); + mockEnqueue.mockResolvedValue(mockEnqueuedJob); + + // Create job after successful verification. + const result = await createAndEnqueueJob('github.issue.fix', { + repo: 'test/repo', + issue: 123, + title: 'Test issue', + body: 'Test issue body', + }); + + expect(result).toEqual({ + jobId: mockJob.id, + enqueuedJobId: mockEnqueuedJob.id, + }); + }); + }); +}); diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/index.ts b/apps/roomote/src/app/api/webhooks/github/handlers/index.ts new file mode 100644 index 0000000000..b077304145 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/index.ts @@ -0,0 +1,5 @@ +export { verifySignature } from './utils'; +export { handleIssueEvent } from './issueHandler'; +export { handlePullRequestEvent } from './pullRequestHandler'; +export { handleIssueCommentEvent } from './issueCommentHandler'; +export { handlePullRequestReviewCommentEvent } from './pullRequestReviewCommentHandler'; diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/issueCommentHandler.ts b/apps/roomote/src/app/api/webhooks/github/handlers/issueCommentHandler.ts new file mode 100644 index 0000000000..08e4c440b2 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/issueCommentHandler.ts @@ -0,0 +1,109 @@ +import { NextResponse } from 'next/server'; +import { z } from 'zod'; + +import type { JobPayload } from '@/types'; + +import { createAndEnqueueJob } from './utils'; + +const githubIssueCommentWebhookSchema = z.object({ + action: z.string(), + issue: z.object({ + number: z.number(), + title: z.string(), + body: z.string().nullable(), + pull_request: z.object({ url: z.string() }).optional(), + }), + comment: z.object({ + id: z.number(), + body: z.string(), + html_url: z.string(), + user: z.object({ login: z.string() }), + }), + repository: z.object({ + full_name: z.string(), + }), +}); + +const githubPullRequestWebhookSchema = z.object({ + number: z.number(), + title: z.string(), + body: z.string().nullable(), + head: z.object({ ref: z.string() }), + base: z.object({ ref: z.string() }), +}); + +export async function handleIssueCommentEvent(body: string) { + const data = githubIssueCommentWebhookSchema.parse(JSON.parse(body)); + const { action, comment, issue, repository } = data; + + if (action !== 'created') { + return NextResponse.json({ message: 'action_ignored' }); + } + + if (!comment.body.includes('@roomote')) { + return NextResponse.json({ message: 'no_roomote_mention' }); + } + + console.log('🗄️ Issue Comment Webhook ->', data); + + // Handle PR comments (when comment is on a pull request) + if (issue.pull_request) { + const response = await fetch(issue.pull_request.url); + + if (!response.ok) { + return NextResponse.json({ message: 'failed_to_fetch_pull_request' }); + } + + // Example: + // https://api.github.com/repos/RooCodeInc/Roo-Code/pulls/4796 + const pull_request = githubPullRequestWebhookSchema.parse( + await response.json(), + ); + console.log(`🗄️ Pull Request -> ${issue.pull_request.url}`, pull_request); + + const payload: JobPayload<'github.pr.comment.respond'> = { + repo: repository.full_name, + prNumber: pull_request.number, + prTitle: pull_request.title, + prBody: pull_request.body || '', + prBranch: pull_request.head.ref, + baseRef: pull_request.base.ref, + commentId: comment.id, + commentBody: comment.body, + commentAuthor: comment.user.login, + commentType: 'issue_comment', + commentUrl: comment.html_url, + }; + + const { jobId, enqueuedJobId } = await createAndEnqueueJob( + 'github.pr.comment.respond', + payload, + ); + return NextResponse.json({ + message: 'pr_comment_job_enqueued', + jobId, + enqueuedJobId, + }); + } + + // Handle issue comments (when comment is on a regular issue) + const type = 'github.issue.comment.respond' as const; + + const payload: JobPayload = { + repo: repository.full_name, + issueNumber: issue.number, + issueTitle: issue.title, + issueBody: issue.body || '', + commentId: comment.id, + commentBody: comment.body, + commentAuthor: comment.user.login, + commentUrl: comment.html_url, + }; + + const { jobId, enqueuedJobId } = await createAndEnqueueJob(type, payload); + return NextResponse.json({ + message: 'issue_comment_job_enqueued', + jobId, + enqueuedJobId, + }); +} diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/issueHandler.ts b/apps/roomote/src/app/api/webhooks/github/handlers/issueHandler.ts new file mode 100644 index 0000000000..6e58795d87 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/issueHandler.ts @@ -0,0 +1,45 @@ +import { NextResponse } from 'next/server'; +import { z } from 'zod'; + +import type { JobPayload } from '@/types'; + +import { createAndEnqueueJob } from './utils'; + +const githubIssueWebhookSchema = z.object({ + action: z.string(), + issue: z.object({ + number: z.number(), + title: z.string(), + body: z.string().nullable(), + labels: z.array(z.object({ name: z.string() })), + }), + repository: z.object({ + full_name: z.string(), + }), +}); + +export async function handleIssueEvent(body: string) { + const data = githubIssueWebhookSchema.parse(JSON.parse(body)); + const { action, repository, issue } = data; + + if (action !== 'opened') { + return NextResponse.json({ message: 'action_ignored' }); + } + + console.log('🗄️ Issue Webhook ->', data); + + const payload: JobPayload<'github.issue.fix'> = { + repo: repository.full_name, + issue: issue.number, + title: issue.title, + body: issue.body || '', + labels: issue.labels.map(({ name }) => name), + }; + + const { jobId, enqueuedJobId } = await createAndEnqueueJob( + 'github.issue.fix', + payload, + ); + + return NextResponse.json({ message: 'job_enqueued', jobId, enqueuedJobId }); +} diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/pullRequestHandler.ts b/apps/roomote/src/app/api/webhooks/github/handlers/pullRequestHandler.ts new file mode 100644 index 0000000000..c762f7db3b --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/pullRequestHandler.ts @@ -0,0 +1,71 @@ +import { NextResponse } from 'next/server'; +import { eq } from 'drizzle-orm'; +import { z } from 'zod'; + +import { db, cloudJobs } from '@/db'; +import { SlackNotifier } from '@/lib/slack'; + +const githubPullRequestWebhookSchema = z.object({ + action: z.string(), + pull_request: z.object({ + number: z.number(), + title: z.string(), + body: z.string().nullable(), + html_url: z.string(), + }), + repository: z.object({ + full_name: z.string(), + }), +}); + +export async function handlePullRequestEvent(body: string) { + const data = githubPullRequestWebhookSchema.parse(JSON.parse(body)); + const { action, pull_request, repository } = data; + + if (action !== 'opened') { + return NextResponse.json({ message: 'action_ignored' }); + } + + console.log('🗄️ PR Webhook ->', data); + + // Extract issue number from PR title or body (looking for "Fixes #123" pattern). + const issueNumberMatch = + pull_request.title.match(/(?:fixes|closes|resolves)\s+#(\d+)/i) || + (pull_request.body && + pull_request.body.match(/(?:fixes|closes|resolves)\s+#(\d+)/i)); + + if (!issueNumberMatch) { + return NextResponse.json({ message: 'no_issue_reference_found' }); + } + + const issueNumber = parseInt(issueNumberMatch[1]!, 10); + + // Find the job that corresponds to this issue. + const jobs = await db + .select() + .from(cloudJobs) + .where(eq(cloudJobs.type, 'github.issue.fix')); + + // Filter jobs to find the one matching this repo and issue. + const job = jobs.find((j) => { + const payload = j.payload as { repo: string; issue: number }; + return ( + payload.repo === repository.full_name && payload.issue === issueNumber + ); + }); + + if (!job || !job.slackThreadTs) { + console.log('No job found or no slack thread for issue', issueNumber); + return NextResponse.json({ message: 'no_job_or_slack_thread_found' }); + } + + const notifier = new SlackNotifier(); + + await notifier.postTaskUpdated( + job.slackThreadTs, + `🎉 Pull request created: <${pull_request.html_url}|PR #${pull_request.number}>\n*${pull_request.title}*`, + 'success', + ); + + return NextResponse.json({ message: 'slack_notification_sent' }); +} diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/pullRequestReviewCommentHandler.ts b/apps/roomote/src/app/api/webhooks/github/handlers/pullRequestReviewCommentHandler.ts new file mode 100644 index 0000000000..bd83cfd092 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/pullRequestReviewCommentHandler.ts @@ -0,0 +1,70 @@ +import { NextResponse } from 'next/server'; +import { z } from 'zod'; + +import type { JobPayload } from '@/types'; + +import { createAndEnqueueJob } from './utils'; + +export const githubPullRequestReviewCommentWebhookSchema = z.object({ + action: z.string(), + comment: z.object({ + id: z.number(), + body: z.string(), + html_url: z.string(), + user: z.object({ + login: z.string(), + }), + }), + pull_request: z.object({ + number: z.number(), + title: z.string(), + body: z.string().nullable(), + head: z.object({ + ref: z.string(), + }), + base: z.object({ + ref: z.string(), + }), + }), + repository: z.object({ + full_name: z.string(), + }), +}); + +export async function handlePullRequestReviewCommentEvent(body: string) { + const data = githubPullRequestReviewCommentWebhookSchema.parse( + JSON.parse(body), + ); + const { action, comment, pull_request, repository } = data; + + if (action !== 'created') { + return NextResponse.json({ message: 'action_ignored' }); + } + + if (!comment.body.includes('@roomote')) { + return NextResponse.json({ message: 'no_roomote_mention' }); + } + + console.log('🗄️ PR Review Comment Webhook ->', data); + + const payload: JobPayload<'github.pr.comment.respond'> = { + repo: repository.full_name, + prNumber: pull_request.number, + prTitle: pull_request.title, + prBody: pull_request.body || '', + prBranch: pull_request.head.ref, + baseRef: pull_request.base.ref, + commentId: comment.id, + commentBody: comment.body, + commentAuthor: comment.user.login, + commentType: 'review_comment', + commentUrl: comment.html_url, + }; + + const { jobId, enqueuedJobId } = await createAndEnqueueJob( + 'github.pr.comment.respond', + payload, + ); + + return NextResponse.json({ message: 'job_enqueued', jobId, enqueuedJobId }); +} diff --git a/apps/roomote/src/app/api/webhooks/github/handlers/utils.ts b/apps/roomote/src/app/api/webhooks/github/handlers/utils.ts new file mode 100644 index 0000000000..0e600fe7f5 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/handlers/utils.ts @@ -0,0 +1,40 @@ +import { createHmac } from 'crypto'; + +import type { JobType, JobPayload } from '@/types'; +import { db, cloudJobs } from '@/db'; +import { enqueue } from '@/lib'; + +export function verifySignature( + body: string, + signature: string, + secret: string, +): boolean { + const expectedSignature = createHmac('sha256', secret) + .update(body, 'utf8') + .digest('hex'); + const receivedSignature = signature.replace('sha256=', ''); + return expectedSignature === receivedSignature; +} + +export async function createAndEnqueueJob( + type: T, + payload: JobPayload, +): Promise<{ jobId: number; enqueuedJobId: string }> { + const [job] = await db + .insert(cloudJobs) + .values({ type, payload, status: 'pending' }) + .returning(); + + if (!job) { + throw new Error('Failed to create `cloudJobs` record.'); + } + + const enqueuedJob = await enqueue({ jobId: job.id, type, payload }); + console.log(`🔗 Enqueued ${type} job (id: ${job.id}) ->`, payload); + + if (!enqueuedJob.id) { + throw new Error('Failed to get enqueued job ID.'); + } + + return { jobId: job.id, enqueuedJobId: enqueuedJob.id }; +} diff --git a/apps/roomote/src/app/api/webhooks/github/route.ts b/apps/roomote/src/app/api/webhooks/github/route.ts new file mode 100644 index 0000000000..7e75f10029 --- /dev/null +++ b/apps/roomote/src/app/api/webhooks/github/route.ts @@ -0,0 +1,55 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { z } from 'zod'; + +import { + verifySignature, + handleIssueEvent, + handlePullRequestEvent, + handleIssueCommentEvent, + handlePullRequestReviewCommentEvent, +} from './handlers'; + +export async function POST(request: NextRequest) { + try { + const signature = request.headers.get('x-hub-signature-256'); + + if (!signature) { + return NextResponse.json({ error: 'missing_signature' }, { status: 400 }); + } + + const body = await request.text(); + + if (!verifySignature(body, signature, process.env.GH_WEBHOOK_SECRET!)) { + return NextResponse.json({ error: 'invalid_signature' }, { status: 401 }); + } + + const event = request.headers.get('x-github-event'); + console.log(`🛎️ GitHub Webhook Event -> ${event}`); + + if (event === 'issues') { + return await handleIssueEvent(body); + } else if (event === 'pull_request') { + return await handlePullRequestEvent(body); + } else if (event === 'issue_comment') { + return await handleIssueCommentEvent(body); + } else if (event === 'pull_request_review_comment') { + return await handlePullRequestReviewCommentEvent(body); + } else { + return NextResponse.json({ message: 'event_ignored' }); + } + } catch (error) { + console.error('GitHub Webhook Error:', error); + + if (error instanceof z.ZodError) { + return NextResponse.json( + { error: 'bad_request', details: error.errors }, + { status: 400 }, + ); + } + + return NextResponse.json( + { error: 'internal_server_error' }, + { status: 500 }, + ); + } +} diff --git a/apps/roomote/src/app/layout.tsx b/apps/roomote/src/app/layout.tsx new file mode 100644 index 0000000000..7b8fca91bd --- /dev/null +++ b/apps/roomote/src/app/layout.tsx @@ -0,0 +1,18 @@ +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Cloud Agents', + description: 'Roo Code task execution service', +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/apps/roomote/src/app/page.tsx b/apps/roomote/src/app/page.tsx new file mode 100644 index 0000000000..5806dda291 --- /dev/null +++ b/apps/roomote/src/app/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return
Hello, World!
; +} diff --git a/apps/roomote/src/db/index.ts b/apps/roomote/src/db/index.ts new file mode 100644 index 0000000000..526c25304c --- /dev/null +++ b/apps/roomote/src/db/index.ts @@ -0,0 +1,11 @@ +import { drizzle } from 'drizzle-orm/postgres-js'; +import postgres from 'postgres'; + +import { schema } from './schema'; + +export const db = drizzle( + postgres(process.env.DATABASE_URL!, { prepare: false }), + { schema }, +); + +export * from './schema'; diff --git a/apps/roomote/src/db/schema.ts b/apps/roomote/src/db/schema.ts new file mode 100644 index 0000000000..5eb82dbbd7 --- /dev/null +++ b/apps/roomote/src/db/schema.ts @@ -0,0 +1,34 @@ +import { pgTable, text, timestamp, integer, jsonb } from 'drizzle-orm/pg-core'; + +import type { JobType, JobStatus, JobPayload } from '@/types'; + +/** + * cloudJobs + */ + +export const cloudJobs = pgTable('cloud_jobs', { + id: integer().primaryKey().generatedAlwaysAsIdentity(), + type: text().notNull().$type(), + status: text().notNull().default('pending').$type(), + payload: jsonb().notNull().$type(), + result: jsonb(), + error: text(), + slackThreadTs: text('slack_thread_ts'), + startedAt: timestamp('started_at'), + completedAt: timestamp('completed_at'), + createdAt: timestamp('created_at').notNull().defaultNow(), +}); + +export type CloudJob = typeof cloudJobs.$inferSelect; + +export type InsertCloudJob = typeof cloudJobs.$inferInsert; + +export type UpdateCloudJob = Partial>; + +/** + * schema + */ + +export const schema = { + cloudJobs, +}; diff --git a/apps/roomote/src/lib/__tests__/controller.test.ts b/apps/roomote/src/lib/__tests__/controller.test.ts new file mode 100644 index 0000000000..e2c12f4935 --- /dev/null +++ b/apps/roomote/src/lib/__tests__/controller.test.ts @@ -0,0 +1,95 @@ +// npx vitest src/lib/__tests__/controller.test.ts + +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; + +const mockQueue = { + getWaiting: vi.fn(() => Promise.resolve([])), + getActive: vi.fn(() => Promise.resolve([])), + close: vi.fn(() => Promise.resolve()), + on: vi.fn(), +}; + +const mockSpawn = vi.fn(() => ({ + stdout: { pipe: vi.fn() }, + stderr: { pipe: vi.fn() }, + on: vi.fn(), + unref: vi.fn(), +})); + +const mockCreateWriteStream = vi.fn(() => ({ + end: vi.fn(), +})); + +vi.mock('../redis', () => ({ + redis: { host: 'localhost', port: 6379 }, +})); + +vi.mock('child_process', () => ({ + spawn: mockSpawn, +})); + +vi.mock('fs', () => ({ + default: { + existsSync: vi.fn(() => false), + createWriteStream: mockCreateWriteStream, + }, +})); + +const mockQueueConstructor = vi.fn(() => mockQueue); + +vi.mock('bullmq', () => ({ + Queue: mockQueueConstructor, +})); + +describe('WorkerController', () => { + let WorkerController: typeof import('../controller').WorkerController; + + beforeEach(async () => { + vi.clearAllMocks(); + const controllerModule = await import('../controller'); + WorkerController = controllerModule.WorkerController; + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should create a Queue instance with correct configuration', () => { + const controller = new WorkerController(); + + expect(mockQueueConstructor).toHaveBeenCalledWith('roomote', { + connection: { host: 'localhost', port: 6379 }, + }); + + expect(controller).toBeDefined(); + }); + + it('should handle queue monitoring without errors', async () => { + const controller = new WorkerController(); + + await controller.start(); + expect(controller).toBeDefined(); + + await controller.stop(); + expect(mockQueue.close).toHaveBeenCalled(); + }); + + it('should handle worker spawning logic', () => { + const controller = new WorkerController(); + expect(mockSpawn).toBeDefined(); + expect(mockCreateWriteStream).toBeDefined(); + expect(controller).toBeDefined(); + }); + + it('should track running state correctly', async () => { + const controller = new WorkerController(); + + expect(controller.isRunning).toBeFalsy(); + + await controller.start(); + expect(controller.isRunning).toBeTruthy(); + + await controller.stop(); + expect(controller.isRunning).toBeFalsy(); + }); +}); diff --git a/apps/roomote/src/lib/controller.ts b/apps/roomote/src/lib/controller.ts new file mode 100644 index 0000000000..12bd78e457 --- /dev/null +++ b/apps/roomote/src/lib/controller.ts @@ -0,0 +1,159 @@ +import { spawn } from 'child_process'; +import fs from 'fs'; +import { Queue } from 'bullmq'; + +import { redis } from './redis'; + +export class WorkerController { + private readonly POLL_INTERVAL_MS = 5000; + private readonly MAX_WORKERS = 5; + + private queue: Queue; + public isRunning = false; + private pollingInterval: NodeJS.Timeout | null = null; + private activeWorkers = new Set(); + + constructor() { + this.queue = new Queue('roomote', { connection: redis }); + } + + async start() { + if (this.isRunning) { + console.log('Controller is already running'); + return; + } + + this.isRunning = true; + console.log('Worker controller started'); + + await this.checkAndSpawnWorker(); + + this.pollingInterval = setInterval(async () => { + await this.checkAndSpawnWorker(); + }, this.POLL_INTERVAL_MS); + } + + async stop() { + if (!this.isRunning) { + return; + } + + this.isRunning = false; + console.log('Stopping worker controller...'); + + if (this.pollingInterval) { + clearInterval(this.pollingInterval); + this.pollingInterval = null; + } + + await this.queue.close(); + console.log('Worker controller stopped'); + } + + private async checkAndSpawnWorker() { + try { + const waiting = await this.queue.getWaiting(); + const active = await this.queue.getActive(); + + const waitingCount = waiting.length; + const activeCount = active.length; + + console.log( + `Queue status: ${waitingCount} waiting, ${activeCount} active, ${this.activeWorkers.size} spawned workers`, + ); + + if (waitingCount > 0 && this.activeWorkers.size < this.MAX_WORKERS) { + await this.spawnWorker(); + } + } catch (error) { + console.error('Error checking queue status:', error); + } + } + + private async spawnWorker() { + const workerId = `worker-${Date.now()}`; + + try { + console.log(`Spawning worker: ${workerId}`); + const isRunningInDocker = fs.existsSync('/.dockerenv'); + + const dockerArgs = [ + `--name roomote-${workerId}`, + '--rm', + '--network roo-code-cloud_default', + '-e HOST_EXECUTION_METHOD=docker', + `-e GH_TOKEN=${process.env.GH_TOKEN}`, + `-e DATABASE_URL=${process.env.DATABASE_URL}`, + `-e REDIS_URL=${process.env.REDIS_URL}`, + `-e NODE_ENV=${process.env.NODE_ENV}`, + '-v /var/run/docker.sock:/var/run/docker.sock', + '-v /tmp/roomote:/var/log/roomote', + ]; + + const cliCommand = 'pnpm worker'; + + const command = isRunningInDocker + ? `docker run ${dockerArgs.join(' ')} roomote-worker sh -c "${cliCommand}"` + : cliCommand; + + console.log('Spawning worker with command:', command); + + const childProcess = spawn('sh', ['-c', command], { + detached: true, + stdio: ['ignore', 'pipe', 'pipe'], + }); + + if (childProcess.stdout) { + childProcess.stdout.on('data', (data) => { + console.log(data.toString()); + }); + } + + if (childProcess.stderr) { + childProcess.stderr.on('data', (data) => { + console.error(data.toString()); + }); + } + + this.activeWorkers.add(workerId); + + childProcess.on('exit', (code) => { + console.log(`Worker ${workerId} exited with code ${code}`); + this.activeWorkers.delete(workerId); + }); + + childProcess.on('error', (error) => { + console.error(`Worker ${workerId} error:`, error); + this.activeWorkers.delete(workerId); + }); + + // Detach the process so it can run independently. + childProcess.unref(); + } catch (error) { + console.error(`Failed to spawn worker ${workerId}:`, error); + this.activeWorkers.delete(workerId); + } + } +} + +// Only run if this file is executed directly (not imported). +if (import.meta.url === `file://${process.argv[1]}`) { + const controller = new WorkerController(); + + process.on('SIGTERM', async () => { + console.log('SIGTERM -> shutting down controller gracefully...'); + await controller.stop(); + process.exit(0); + }); + + process.on('SIGINT', async () => { + console.log('SIGINT -> shutting down controller gracefully...'); + await controller.stop(); + process.exit(0); + }); + + controller.start().catch((error) => { + console.error('Failed to start controller:', error); + process.exit(1); + }); +} diff --git a/apps/roomote/src/lib/index.ts b/apps/roomote/src/lib/index.ts new file mode 100644 index 0000000000..ce0efeaefe --- /dev/null +++ b/apps/roomote/src/lib/index.ts @@ -0,0 +1,2 @@ +export { redis } from './redis'; +export { enqueue } from './queue'; diff --git a/apps/roomote/src/lib/job.ts b/apps/roomote/src/lib/job.ts new file mode 100644 index 0000000000..8f45335de6 --- /dev/null +++ b/apps/roomote/src/lib/job.ts @@ -0,0 +1,133 @@ +import { eq } from 'drizzle-orm'; +import { Job } from 'bullmq'; + +import { db, cloudJobs, type UpdateCloudJob } from '@/db'; +import type { JobType, JobStatus, JobParams, JobPayload } from '@/types'; + +import { fixGitHubIssue } from './jobs/fixGitHubIssue'; +import { processPullRequestComment } from './jobs/processPullRequestComment'; +import { processIssueComment } from './jobs/processIssueComment'; + +export async function processJob({ + data: { type, payload, jobId }, + ...job +}: Job>) { + console.log( + `[${job.name} | ${job.id}] Processing job ${jobId} of type ${type}`, + ); + + try { + let result: unknown; + + switch (type) { + case 'github.issue.fix': + result = await fixGitHubIssue( + payload as JobPayload<'github.issue.fix'>, + { + onTaskStarted: async ( + slackThreadTs: string | null, + _rooTaskId: string, + ) => { + if (slackThreadTs) { + await updateJobStatus( + jobId, + 'processing', + undefined, + undefined, + slackThreadTs, + ); + } + }, + }, + ); + + break; + case 'github.issue.comment.respond': + result = await processIssueComment( + payload as JobPayload<'github.issue.comment.respond'>, + { + onTaskStarted: async ( + slackThreadTs: string | null, + _rooTaskId: string, + ) => { + if (slackThreadTs) { + await updateJobStatus( + jobId, + 'processing', + undefined, + undefined, + slackThreadTs, + ); + } + }, + }, + ); + + break; + case 'github.pr.comment.respond': + result = await processPullRequestComment( + payload as JobPayload<'github.pr.comment.respond'>, + { + onTaskStarted: async ( + slackThreadTs: string | null, + _rooTaskId: string, + ) => { + if (slackThreadTs) { + await updateJobStatus( + jobId, + 'processing', + undefined, + undefined, + slackThreadTs, + ); + } + }, + }, + ); + + break; + default: + throw new Error(`Unknown job type: ${type}`); + } + + await updateJobStatus(jobId, 'completed', result); + console.log( + `[${job.name} | ${job.id}] Job ${jobId} completed successfully`, + ); + } catch (error) { + console.error(`[${job.name} | ${job.id}] Job ${jobId} failed:`, error); + const errorMessage = error instanceof Error ? error.message : String(error); + await updateJobStatus(jobId, 'failed', undefined, errorMessage); + throw error; // Re-throw to mark job as failed in BullMQ. + } +} + +async function updateJobStatus( + jobId: number, + status: JobStatus, + result?: unknown, + error?: string, + slackThreadTs?: string, +) { + const values: UpdateCloudJob = { status }; + + if (status === 'processing') { + values.startedAt = new Date(); + } else if (status === 'completed' || status === 'failed') { + values.completedAt = new Date(); + + if (result) { + values.result = result; + } + + if (error) { + values.error = error; + } + } + + if (slackThreadTs) { + values.slackThreadTs = slackThreadTs; + } + + await db.update(cloudJobs).set(values).where(eq(cloudJobs.id, jobId)); +} diff --git a/apps/roomote/src/lib/jobs/fixGitHubIssue.ts b/apps/roomote/src/lib/jobs/fixGitHubIssue.ts new file mode 100644 index 0000000000..49dfc3f1b5 --- /dev/null +++ b/apps/roomote/src/lib/jobs/fixGitHubIssue.ts @@ -0,0 +1,59 @@ +import * as path from 'path'; +import * as os from 'node:os'; + +import type { JobType, JobPayload } from '@/types'; + +import { runTask, type RunTaskCallbacks } from '../runTask'; +import { Logger } from '../logger'; + +const jobType: JobType = 'github.issue.fix'; + +type FixGitHubIssueJobPayload = JobPayload<'github.issue.fix'>; + +export async function fixGitHubIssue( + jobPayload: FixGitHubIssueJobPayload, + callbacks?: RunTaskCallbacks, +): Promise<{ + repo: string; + issue: number; + result: unknown; +}> { + const prompt = ` +Fix the following GitHub issue: + +Repository: ${jobPayload.repo} +Issue #${jobPayload.issue}: ${jobPayload.title} + +Description: +${jobPayload.body} + +${jobPayload.labels && jobPayload.labels.length > 0 ? `Labels: ${jobPayload.labels.join(', ')}` : ''} + +Please analyze the issue, understand what needs to be fixed, and implement a solution. + +When you're finished: +- Create a git branch to store your work (git checkout -b fix-${jobPayload.issue}) +- Commit your changes to this branch (git commit -m "Fixes #${jobPayload.issue}") +- Push your branch to the remote repository (git push --set-upstream origin fix-${jobPayload.issue}) +- Submit a pull request using the "gh" command line tool (gh pr create --title "Fixes #${jobPayload.issue}\n\n[Your PR description here.]" --fill) + +Your job isn't done until you've created a pull request. Try to solve any git issues that arise while creating your branch and submitting your pull request. +`.trim(); + + const { repo, issue } = jobPayload; + + const result = await runTask({ + jobType, + jobPayload, + prompt, + publish: async () => {}, + logger: new Logger({ + logDir: path.resolve(os.tmpdir(), 'logs'), + filename: 'worker.log', + tag: 'worker', + }), + callbacks, + }); + + return { repo, issue, result }; +} diff --git a/apps/roomote/src/lib/jobs/processIssueComment.ts b/apps/roomote/src/lib/jobs/processIssueComment.ts new file mode 100644 index 0000000000..309706fd84 --- /dev/null +++ b/apps/roomote/src/lib/jobs/processIssueComment.ts @@ -0,0 +1,71 @@ +import * as path from 'path'; +import * as os from 'node:os'; + +import type { JobType, JobPayload } from '@/types'; + +import { runTask, type RunTaskCallbacks } from '../runTask'; +import { Logger } from '../logger'; + +const jobType: JobType = 'github.issue.comment.respond'; + +type ProcessIssueCommentJobPayload = JobPayload<'github.issue.comment.respond'>; + +export async function processIssueComment( + jobPayload: ProcessIssueCommentJobPayload, + callbacks?: RunTaskCallbacks, +): Promise<{ + repo: string; + issueNumber: number; + commentId: number; + result: unknown; +}> { + const prompt = ` +Respond to the following GitHub Issue comment: + +Repository: ${jobPayload.repo} +Issue #${jobPayload.issueNumber}: ${jobPayload.issueTitle} + +Issue Description: +${jobPayload.issueBody || 'No description provided'} + +Comment by @${jobPayload.commentAuthor}: +${jobPayload.commentBody} + +Comment URL: ${jobPayload.commentUrl} + +Please analyze the comment and provide a helpful response. The comment mentions @roomote, which means the user wants you to engage with their question or request. + +Instructions: +1. Read and understand the context of the issue and the specific comment +2. Provide a thoughtful, helpful response to the comment +3. If the comment asks a question, try to answer it based on your knowledge +4. If the comment requests an action, explain what you can do or suggest next steps +5. If the comment is unclear, ask for clarification +6. Use the GitHub CLI or API to respond to the comment with your message + +Your goal is to be helpful and engage meaningfully with the community member who mentioned @roomote. + +Use the "gh" command line tool to respond to the comment: +gh api repos/${jobPayload.repo}/issues/comments/${jobPayload.commentId} --method PATCH --field body="Your response here" + +Or create a new comment response: +gh api repos/${jobPayload.repo}/issues/${jobPayload.issueNumber}/comments --method POST --field body="Your response here" +`.trim(); + + const { repo, issueNumber, commentId } = jobPayload; + + const result = await runTask({ + jobType, + jobPayload, + prompt, + publish: async () => {}, + logger: new Logger({ + logDir: path.resolve(os.tmpdir(), 'logs'), + filename: 'worker.log', + tag: 'worker', + }), + callbacks, + }); + + return { repo, issueNumber, commentId, result }; +} diff --git a/apps/roomote/src/lib/jobs/processPullRequestComment.ts b/apps/roomote/src/lib/jobs/processPullRequestComment.ts new file mode 100644 index 0000000000..e2cadd88cb --- /dev/null +++ b/apps/roomote/src/lib/jobs/processPullRequestComment.ts @@ -0,0 +1,85 @@ +import * as path from 'path'; +import * as os from 'node:os'; + +import type { JobType, JobPayload } from '@/types'; + +import { runTask, type RunTaskCallbacks } from '../runTask'; +import { Logger } from '../logger'; + +const jobType: JobType = 'github.pr.comment.respond'; + +type ProcessPullRequestCommentJobPayload = + JobPayload<'github.pr.comment.respond'>; + +export async function processPullRequestComment( + jobPayload: ProcessPullRequestCommentJobPayload, + callbacks?: RunTaskCallbacks, +): Promise<{ + repo: string; + prNumber: number; + commentId: number; + result: unknown; +}> { + const prompt = ` +Process the following GitHub Pull Request comment: + +Repository: ${jobPayload.repo} +Pull Request #${jobPayload.prNumber}: ${jobPayload.prTitle} + +PR Description: +${jobPayload.prBody || 'No description provided'} + +Comment by @${jobPayload.commentAuthor}: +${jobPayload.commentBody} + +Comment Type: ${jobPayload.commentType} +Comment URL: ${jobPayload.commentUrl} + +PR Branch: ${jobPayload.prBranch} +Base Branch: ${jobPayload.baseRef} + +Please analyze the comment and understand what changes are being requested. Then implement the requested changes directly on the PR branch AND respond to the comment. + +Instructions: +1. First, respond to the comment to acknowledge the request and explain what you'll do +2. Check out the PR branch: git checkout ${jobPayload.prBranch} +3. Analyze the comment in the context of the pull request +4. Make the appropriate changes based on the comment +5. Commit your changes with a clear message referencing the comment +6. Push the changes to the same PR branch: git push origin ${jobPayload.prBranch} +7. After completing the changes, update your response or add a follow-up comment with the results + +The comment mentions @roomote, which means the user wants you to process this request. Make sure to: +- Respond to the comment first to acknowledge the request +- Understand the context of the PR and the specific request in the comment +- Implement the requested changes thoughtfully +- Test your changes if applicable +- Write clear commit messages that reference the comment +- Provide updates on the progress and results + +Use the GitHub CLI to respond to the comment: +gh api repos/${jobPayload.repo}/issues/comments/${jobPayload.commentId} --method PATCH --field body="Your response here" + +Or create a new comment response: +gh api repos/${jobPayload.repo}/issues/${jobPayload.prNumber}/comments --method POST --field body="Your response here" + +Do not create a new pull request - work directly on the existing PR branch. +`.trim(); + + const { repo, prNumber, commentId } = jobPayload; + + const result = await runTask({ + jobType, + jobPayload, + prompt, + publish: async () => {}, + logger: new Logger({ + logDir: path.resolve(os.tmpdir(), 'logs'), + filename: 'worker.log', + tag: 'worker', + }), + callbacks, + }); + + return { repo, prNumber, commentId, result }; +} diff --git a/apps/roomote/src/lib/logger.ts b/apps/roomote/src/lib/logger.ts new file mode 100644 index 0000000000..35a84b27c1 --- /dev/null +++ b/apps/roomote/src/lib/logger.ts @@ -0,0 +1,86 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +enum LogLevel { + INFO = 'INFO', + ERROR = 'ERROR', + WARN = 'WARN', + DEBUG = 'DEBUG', +} + +interface LoggerOptions { + logDir: string; + filename: string; + tag: string; +} + +export class Logger { + private logStream: fs.WriteStream | undefined; + private logFilePath: string; + private tag: string; + + constructor({ logDir, filename, tag }: LoggerOptions) { + this.tag = tag; + this.logFilePath = path.join(logDir, filename); + this.initializeLogger(logDir); + } + + private initializeLogger(logDir: string): void { + try { + fs.mkdirSync(logDir, { recursive: true }); + } catch (error) { + console.error(`Failed to create log directory ${logDir}:`, error); + } + + try { + this.logStream = fs.createWriteStream(this.logFilePath, { flags: 'a' }); + } catch (error) { + console.error(`Failed to create log file ${this.logFilePath}:`, error); + } + } + + private writeToLog(level: LogLevel, message: string, ...args: unknown[]) { + try { + const timestamp = new Date().toISOString(); + + const logLine = `[${timestamp} | ${level} | ${this.tag}] ${message} ${ + args.length > 0 ? JSON.stringify(args) : '' + }\n`; + + console.log(logLine.trim()); + + if (this.logStream) { + this.logStream.write(logLine); + } + } catch (error) { + console.error(`Failed to write to log file ${this.logFilePath}:`, error); + } + } + + public info(message: string, ...args: unknown[]): void { + this.writeToLog(LogLevel.INFO, message, ...args); + } + + public error(message: string, ...args: unknown[]): void { + this.writeToLog(LogLevel.ERROR, message, ...args); + } + + public warn(message: string, ...args: unknown[]): void { + this.writeToLog(LogLevel.WARN, message, ...args); + } + + public debug(message: string, ...args: unknown[]): void { + this.writeToLog(LogLevel.DEBUG, message, ...args); + } + + public log(message: string, ...args: unknown[]): void { + this.info(message, ...args); + } + + public close(): void { + if (this.logStream) { + this.logStream.end(); + this.logStream = undefined; + } + } +} diff --git a/apps/roomote/src/lib/queue.ts b/apps/roomote/src/lib/queue.ts new file mode 100644 index 0000000000..a36fd79c10 --- /dev/null +++ b/apps/roomote/src/lib/queue.ts @@ -0,0 +1,23 @@ +import { Queue, Job } from 'bullmq'; + +import type { JobTypes, JobPayload, JobParams } from '@/types'; + +import { redis } from './redis'; + +const queue = new Queue('roomote', { + connection: redis, + defaultJobOptions: { + removeOnComplete: 100, + removeOnFail: 50, + attempts: 3, + backoff: { type: 'exponential', delay: 2000 }, + }, +}); + +export async function enqueue( + params: JobParams, +): Promise>> { + return queue.add(params.type, params, { + jobId: `${params.type}-${params.jobId}`, + }); +} diff --git a/apps/roomote/src/lib/redis.ts b/apps/roomote/src/lib/redis.ts new file mode 100644 index 0000000000..7a3554f7fa --- /dev/null +++ b/apps/roomote/src/lib/redis.ts @@ -0,0 +1,8 @@ +import IORedis from 'ioredis'; + +export const redis = new IORedis( + process.env.REDIS_URL || 'redis://localhost:6379', + { + maxRetriesPerRequest: null, + }, +); diff --git a/apps/roomote/src/lib/runTask.ts b/apps/roomote/src/lib/runTask.ts new file mode 100644 index 0000000000..92a14f7b61 --- /dev/null +++ b/apps/roomote/src/lib/runTask.ts @@ -0,0 +1,334 @@ +import * as path from 'path'; +import * as os from 'node:os'; +import * as crypto from 'node:crypto'; + +import pWaitFor from 'p-wait-for'; +import { execa } from 'execa'; + +import { + type TaskEvent, + TaskCommandName, + RooCodeEventName, + IpcMessageType, + EVALS_SETTINGS, +} from '@roo-code/types'; +import { IpcClient } from '@roo-code-cloud/ipc'; + +import type { JobPayload, JobType } from '@/types'; + +import { Logger } from './logger'; +import { isDockerContainer } from './utils'; +import { SlackNotifier } from './slack'; + +const TIMEOUT = 30 * 60 * 1_000; + +class SubprocessTimeoutError extends Error { + constructor(timeout: number) { + super(`Subprocess timeout after ${timeout}ms`); + this.name = 'SubprocessTimeoutError'; + } +} + +export type RunTaskCallbacks = { + onTaskStarted?: ( + slackThreadTs: string | null, + rooTaskId: string, + ) => Promise; + onTaskAborted?: (slackThreadTs: string | null) => Promise; + onTaskCompleted?: ( + slackThreadTs: string | null, + success: boolean, + duration: number, + rooTaskId?: string, + ) => Promise; + onTaskTimedOut?: (slackThreadTs: string | null) => Promise; + onClientDisconnected?: (slackThreadTs: string | null) => Promise; +}; + +type RunTaskOptions = { + jobType: T; + jobPayload: JobPayload; + prompt: string; + publish: (taskEvent: TaskEvent) => Promise; + logger: Logger; + callbacks?: RunTaskCallbacks; +}; + +export const runTask = async ({ + jobType, + jobPayload, + prompt, + publish, + logger, + callbacks, +}: RunTaskOptions) => { + const workspacePath = '/roo/repos/Roo-Code'; // findGitRoot(process.cwd()) + const ipcSocketPath = path.resolve( + os.tmpdir(), + `${crypto.randomUUID().slice(0, 8)}.sock`, + ); + const env = { ROO_CODE_IPC_SOCKET_PATH: ipcSocketPath }; + const controller = new AbortController(); + const cancelSignal = controller.signal; + const containerized = isDockerContainer(); + + const codeCommand = containerized + ? `xvfb-run --auto-servernum --server-num=1 code --wait --log trace --disable-workspace-trust --disable-gpu --disable-lcd-text --no-sandbox --user-data-dir /roo/.vscode --password-store="basic" -n ${workspacePath}` + : `code --disable-workspace-trust -n ${workspacePath}`; + + logger.info(codeCommand); + + // Sleep for a random amount of time between 5 and 10 seconds, unless we're + // running in a container, in which case there are no issues with flooding + // VSCode with new windows. + if (!containerized) { + await new Promise((resolve) => + setTimeout(resolve, Math.random() * 5_000 + 5_000), + ); + } + + const subprocess = execa({ + env, + shell: '/bin/bash', + cancelSignal, + })`${codeCommand}`; + + // If debugging, add `--verbose` to `command` and uncomment the following line. + // subprocess.stdout.pipe(process.stdout) + + // Give VSCode some time to spawn before connecting to its unix socket. + await new Promise((resolve) => setTimeout(resolve, 3_000)); + let client: IpcClient | undefined = undefined; + let attempts = 5; + + while (true) { + try { + client = new IpcClient(ipcSocketPath); + await pWaitFor(() => client!.isReady, { interval: 250, timeout: 1_000 }); + break; + } catch (_error) { + client?.disconnect(); + attempts--; + + if (attempts <= 0) { + logger.error(`unable to connect to IPC socket -> ${ipcSocketPath}`); + throw new Error('Unable to connect.'); + } + } + } + + let taskStartedAt = Date.now(); + let taskFinishedAt: number | undefined; + let taskAbortedAt: number | undefined; + let taskTimedOut: boolean = false; + let rooTaskId: string | undefined; + let isClientDisconnected = false; + + const slackNotifier = new SlackNotifier(logger); + let slackThreadTs: string | null = null; + + const ignoreEvents: Record<'broadcast' | 'log', RooCodeEventName[]> = { + broadcast: [RooCodeEventName.Message], + log: [ + RooCodeEventName.TaskTokenUsageUpdated, + RooCodeEventName.TaskAskResponded, + ], + }; + + client.on(IpcMessageType.TaskEvent, async (taskEvent) => { + const { eventName, payload } = taskEvent; + + // Publish all events except for these to Redis. + if (!ignoreEvents.broadcast.includes(eventName)) { + await publish({ ...taskEvent }); + } + + // Log all events except for these. + // For message events we only log non-partial messages. + if ( + !ignoreEvents.log.includes(eventName) && + (eventName !== RooCodeEventName.Message || + payload[0].message.partial !== true) + ) { + logger.info(`${eventName} ->`, payload); + } + + if (eventName === RooCodeEventName.TaskStarted) { + taskStartedAt = Date.now(); + rooTaskId = payload[0]; + + if (rooTaskId) { + slackThreadTs = await slackNotifier.postTaskStarted({ + jobType, + jobPayload, + rooTaskId, + }); + + if (callbacks?.onTaskStarted) { + await callbacks.onTaskStarted(slackThreadTs, rooTaskId); + } + } + } + + if (eventName === RooCodeEventName.TaskAborted) { + taskAbortedAt = Date.now(); + + if (slackThreadTs) { + await slackNotifier.postTaskUpdated( + slackThreadTs, + 'Task was aborted', + 'warning', + ); + } + + if (callbacks?.onTaskAborted) { + await callbacks.onTaskAborted(slackThreadTs); + } + } + + if (eventName === RooCodeEventName.TaskCompleted) { + taskFinishedAt = Date.now(); + + if (slackThreadTs) { + await slackNotifier.postTaskCompleted( + slackThreadTs, + true, + taskFinishedAt - taskStartedAt, + rooTaskId, + ); + } + + if (callbacks?.onTaskCompleted) { + await callbacks.onTaskCompleted( + slackThreadTs, + true, + taskFinishedAt - taskStartedAt, + rooTaskId, + ); + } + } + }); + + client.on(IpcMessageType.Disconnect, async () => { + logger.info(`disconnected from IPC socket -> ${ipcSocketPath}`); + isClientDisconnected = true; + }); + + client.sendCommand({ + commandName: TaskCommandName.StartNewTask, + data: { + configuration: { + ...EVALS_SETTINGS, + openRouterApiKey: process.env.OPENROUTER_API_KEY, + }, + text: prompt, + newTab: true, + }, + }); + + try { + await pWaitFor( + () => !!taskFinishedAt || !!taskAbortedAt || isClientDisconnected, + { + interval: 1_000, + timeout: TIMEOUT, + }, + ); + } catch (_error) { + taskTimedOut = true; + logger.error('time limit reached'); + + if (slackThreadTs) { + await slackNotifier.postTaskUpdated( + slackThreadTs, + 'Task timed out after 30 minutes', + 'error', + ); + } + + if (callbacks?.onTaskTimedOut) { + await callbacks.onTaskTimedOut(slackThreadTs); + } + + if (rooTaskId && !isClientDisconnected) { + logger.info('cancelling task'); + client.sendCommand({ + commandName: TaskCommandName.CancelTask, + data: rooTaskId, + }); + await new Promise((resolve) => setTimeout(resolve, 5_000)); // Allow some time for the task to cancel. + } + + taskFinishedAt = Date.now(); + } + + if (!taskFinishedAt && !taskTimedOut) { + logger.error('client disconnected before task finished'); + + if (slackThreadTs) { + await slackNotifier.postTaskUpdated( + slackThreadTs, + 'Client disconnected before task completion', + 'error', + ); + } + + if (callbacks?.onClientDisconnected) { + await callbacks.onClientDisconnected(slackThreadTs); + } + + throw new Error('Client disconnected before task completion.'); + } + + if (rooTaskId && !isClientDisconnected) { + logger.info('closing task'); + client.sendCommand({ + commandName: TaskCommandName.CloseTask, + data: rooTaskId, + }); + await new Promise((resolve) => setTimeout(resolve, 2_000)); // Allow some time for the window to close. + } + + if (!isClientDisconnected) { + logger.info('disconnecting client'); + client.disconnect(); + } + + logger.info('waiting for subprocess to finish'); + controller.abort(); + + // Wait for subprocess to finish gracefully, with a timeout. + const SUBPROCESS_TIMEOUT = 10_000; + + try { + await Promise.race([ + subprocess, + new Promise((_, reject) => + setTimeout( + () => reject(new SubprocessTimeoutError(SUBPROCESS_TIMEOUT)), + SUBPROCESS_TIMEOUT, + ), + ), + ]); + + logger.info('subprocess finished gracefully'); + } catch (error) { + if (error instanceof SubprocessTimeoutError) { + logger.error('subprocess did not finish within timeout, force killing'); + + try { + if (subprocess.kill('SIGKILL')) { + logger.info('SIGKILL sent to subprocess'); + } else { + logger.error('failed to send SIGKILL to subprocess'); + } + } catch (killError) { + logger.error('subprocess.kill(SIGKILL) failed:', killError); + } + } else { + throw error; + } + } + + logger.close(); +}; diff --git a/apps/roomote/src/lib/slack.ts b/apps/roomote/src/lib/slack.ts new file mode 100644 index 0000000000..62c4de191b --- /dev/null +++ b/apps/roomote/src/lib/slack.ts @@ -0,0 +1,159 @@ +import { JobPayload, JobType } from '@/types'; +import { Logger } from './logger'; + +export interface SlackMessage { + text: string; + blocks?: unknown[]; + attachments?: unknown[]; + thread_ts?: string; + channel?: string; +} + +export interface SlackResponse { + ok: boolean; + channel?: string; + ts?: string; + error?: string; + message?: Record; +} + +export class SlackNotifier { + private readonly logger?: Logger; + private readonly token: string; + + constructor(logger?: Logger, token: string = process.env.SLACK_API_TOKEN!) { + this.logger = logger; + this.token = token; + } + + private async postMessage(message: SlackMessage): Promise { + try { + const messageWithChannel = { + ...message, + channel: message.channel || '#roomote-control', + }; + + const response = await fetch('https://slack.com/api/chat.postMessage', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.token}`, + }, + body: JSON.stringify(messageWithChannel), + }); + + if (!response.ok) { + this.logger?.error( + `Slack API failed: ${response.status} ${response.statusText}`, + ); + return null; + } + + const result: SlackResponse = await response.json(); + + if (!result.ok) { + this.logger?.error(`Slack API error: ${result.error}`); + } + + return result.ts ?? null; + } catch (error) { + this.logger?.error('Failed to send Slack message:', error); + return null; + } + } + + public async postTaskStarted({ + jobType, + jobPayload, + }: { + jobType: T; + jobPayload: JobPayload; + rooTaskId: string; + }) { + switch (jobType) { + case 'github.issue.fix': { + const payload = jobPayload as JobPayload<'github.issue.fix'>; + return await this.postMessage({ + text: `🚀 Task Started`, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `🚀 *Task Started*\nAttempting to fix `, + }, + }, + ], + }); + } + case 'github.issue.comment.respond': { + const payload = + jobPayload as JobPayload<'github.issue.comment.respond'>; + return await this.postMessage({ + text: `🚀 Task Started`, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `🚀 *Task Started*\nResponding to comment on \n*Comment:* ${payload.commentBody.slice(0, 100)}${payload.commentBody.length > 100 ? '...' : ''}`, + }, + }, + ], + }); + } + case 'github.pr.comment.respond': { + const payload = jobPayload as JobPayload<'github.pr.comment.respond'>; + return await this.postMessage({ + text: `🚀 Task Started`, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `🚀 *Task Started*\nResponding to comment on \n*Comment:* ${payload.commentBody.slice(0, 100)}${payload.commentBody.length > 100 ? '...' : ''}`, + }, + }, + ], + }); + } + default: + throw new Error(`Unknown job type: ${jobType}`); + } + } + + public async postTaskUpdated( + threadTs: string, + text: string, + status?: 'info' | 'success' | 'warning' | 'error', + ): Promise { + const emoji = { info: 'ℹ️', success: '✅', warning: '⚠️', error: '❌' }[ + status || 'info' + ]; + await this.postMessage({ text: `${emoji} ${text}`, thread_ts: threadTs }); + } + + public async postTaskCompleted( + threadTs: string, + success: boolean, + duration: number, + taskId?: string, + ): Promise { + const status = success ? '✅ Completed' : '❌ Failed'; + const durationText = `${Math.round(duration / 1000)}s`; + + await this.postMessage({ + text: `${status} Task finished in ${durationText}`, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: `*${status}*\n*Task ID:* ${taskId || 'Unknown'}\n*Duration:* ${durationText}`, + }, + }, + ], + thread_ts: threadTs, + }); + } +} diff --git a/apps/roomote/src/lib/utils.ts b/apps/roomote/src/lib/utils.ts new file mode 100644 index 0000000000..ab6b1a671a --- /dev/null +++ b/apps/roomote/src/lib/utils.ts @@ -0,0 +1,37 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +export const isDockerContainer = () => { + try { + return fs.existsSync('/.dockerenv'); + } catch (_error) { + return false; + } +}; + +/** + * Traverses up the directory tree to find an ancestor directory that contains a .git directory + * @param startPath The starting directory path + * @returns The path to the git repository root + * @throws Error if no .git directory is found + */ +export const findGitRoot = (startPath: string): string => { + let currentPath = path.resolve(startPath); + const root = path.parse(currentPath).root; + + while (currentPath !== root) { + const gitPath = path.join(currentPath, '.git'); + if (fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory()) { + return currentPath; + } + currentPath = path.dirname(currentPath); + } + + const gitPath = path.join(root, '.git'); + + if (fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory()) { + return root; + } + + throw new Error('No .git directory found in any ancestor directory'); +}; diff --git a/apps/roomote/src/lib/worker.ts b/apps/roomote/src/lib/worker.ts new file mode 100644 index 0000000000..b61573a960 --- /dev/null +++ b/apps/roomote/src/lib/worker.ts @@ -0,0 +1,72 @@ +import { Worker } from 'bullmq'; + +import { redis } from './redis'; +import { processJob } from './job'; + +// docker compose build worker +// docker run \ +// --name roomote-worker \ +// --rm \ +// --interactive \ +// --tty \ +// --network roo-code-cloud_default \ +// -e HOST_EXECUTION_METHOD=docker \ +// -e GH_TOKEN=$GH_TOKEN \ +// -e DATABASE_URL=postgresql://postgres:password@db:5432/cloud_agents \ +// -e REDIS_URL=redis://redis:6379 \ +// -e NODE_ENV=production \ +// -v /var/run/docker.sock:/var/run/docker.sock \ +// -v /tmp/roomote:/var/log/roomote \ +// roomote-worker sh -c "bash" + +async function processSingleJob() { + const worker = new Worker('roomote', undefined, { + autorun: false, + connection: redis, + lockDuration: 30 * 60 * 1_000, // 30 minutes + }); + + const token = crypto.randomUUID(); + + try { + const job = await worker.getNextJob(token); + + if (!job) { + console.log('No jobs available, exiting...'); + await worker.close(); + process.exit(0); + } + + console.log(`Processing job ${job.id}...`); + + try { + await processJob(job); + await job.moveToCompleted(undefined, token, false); + console.log(`Job ${job.id} completed successfully`); + } catch (error) { + await job.moveToFailed(error as Error, token, false); + console.error(`Job ${job.id} failed:`, error); + } + } catch (error) { + console.error('Error processing job:', error); + } finally { + await worker.close(); + process.exit(0); + } +} + +process.on('SIGTERM', async () => { + console.log('SIGTERM -> shutting down gracefully...'); + process.exit(0); +}); + +process.on('SIGINT', async () => { + console.log('SIGINT -> shutting down gracefully...'); + process.exit(0); +}); + +if (!process.env.GH_TOKEN) { + throw new Error('GH_TOKEN is not set'); +} + +processSingleJob(); diff --git a/apps/roomote/src/types/index.ts b/apps/roomote/src/types/index.ts new file mode 100644 index 0000000000..14ff057be2 --- /dev/null +++ b/apps/roomote/src/types/index.ts @@ -0,0 +1,61 @@ +import { z } from 'zod'; + +export const createJobSchema = z.discriminatedUnion('type', [ + z.object({ + type: z.literal('github.issue.fix'), + payload: z.object({ + repo: z.string(), + issue: z.number(), + title: z.string(), + body: z.string(), + labels: z.array(z.string()).optional(), + }), + }), + z.object({ + type: z.literal('github.issue.comment.respond'), + payload: z.object({ + repo: z.string(), + issueNumber: z.number(), + issueTitle: z.string(), + issueBody: z.string(), + commentId: z.number(), + commentBody: z.string(), + commentAuthor: z.string(), + commentUrl: z.string(), + }), + }), + z.object({ + type: z.literal('github.pr.comment.respond'), + payload: z.object({ + repo: z.string(), + prNumber: z.number(), + prTitle: z.string(), + prBody: z.string(), + prBranch: z.string(), + baseRef: z.string(), + commentId: z.number(), + commentBody: z.string(), + commentAuthor: z.string(), + commentType: z.enum(['issue_comment', 'review_comment']), + commentUrl: z.string(), + }), + }), +]); + +export type CreateJob = z.infer; + +export type JobTypes = { + [K in CreateJob['type']]: Extract['payload']; +}; + +export type JobType = keyof JobTypes; + +export type JobStatus = 'pending' | 'processing' | 'completed' | 'failed'; + +export type JobPayload = JobTypes[T]; + +export type JobParams = { + jobId: number; + type: T; + payload: JobPayload; +}; diff --git a/apps/roomote/tsconfig.json b/apps/roomote/tsconfig.json new file mode 100644 index 0000000000..473167e0c4 --- /dev/null +++ b/apps/roomote/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@roo-code-cloud/config-typescript/nextjs.json", + "compilerOptions": { + "types": ["vitest/globals"], + "paths": { "@/*": ["./src/*"] } + }, + "include": [ + "next-env.d.ts", + "src/**/*.ts", + "src/**/*.tsx", + ".next/types/**/*.ts", + "drizzle.config.ts" + ], + "exclude": ["node_modules"] +} diff --git a/apps/web/vitest.config.mts b/apps/roomote/vitest.config.ts similarity index 57% rename from apps/web/vitest.config.mts rename to apps/roomote/vitest.config.ts index 2e4497186a..da651da521 100644 --- a/apps/web/vitest.config.mts +++ b/apps/roomote/vitest.config.ts @@ -5,9 +5,5 @@ export default defineConfig({ globals: true, watch: false, reporters: ['dot'], - coverage: { - include: ['src/**/*'], - exclude: ['src/**/*.stories.{js,jsx,ts,tsx}', '**/*.d.ts'], - }, }, }); diff --git a/apps/web/.gitignore b/apps/web/.gitignore index d64785b3bf..37799ec24d 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -1,7 +1,3 @@ # next.js /.next /out - -# docker -.docker/* -!.docker/scripts diff --git a/apps/web/docker-compose.yml b/apps/web/docker-compose.yml deleted file mode 100644 index ec9f781565..0000000000 --- a/apps/web/docker-compose.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: roo-code-cloud - -services: - postgres: - container_name: roo-code-cloud-postgres - image: postgres:17.5 - ports: - - '5432:5432' - volumes: - - ./.docker/data/postgres:/var/lib/postgresql/data - - ./.docker/scripts/postgres:/docker-entrypoint-initdb.d - environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=password - - POSTGRES_DATABASES=roo_code_development,roo_code_test - healthcheck: - test: ['CMD-SHELL', 'pg_isready -U postgres -d roo_code_development'] - interval: 5s - timeout: 5s - retries: 5 - start_period: 30s - - clickhouse: - container_name: roo-code-cloud-clickhouse - image: clickhouse/clickhouse-server - ports: - - '8123:8123' - - '9000:9000' - volumes: - - ./.docker/data/clickhouse:/var/lib/clickhouse/ - - ./.docker/logs/clickhouse:/var/log/clickhouse-server/ - - ./.docker/scripts/clickhouse:/docker-entrypoint-initdb.d - environment: - - CLICKHOUSE_DB=default - - CLICKHOUSE_USER=default - - CLICKHOUSE_PASSWORD=password - healthcheck: - test: ['CMD-SHELL', 'wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1'] - interval: 5s - timeout: 5s - retries: 5 - start_period: 30s diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs index 5999738c76..00b53054bd 100644 --- a/apps/web/eslint.config.mjs +++ b/apps/web/eslint.config.mjs @@ -1,74 +1,4 @@ -import globals from 'globals'; - -import js from '@eslint/js'; -import eslintConfigPrettier from 'eslint-config-prettier'; -import tseslint from 'typescript-eslint'; -import pluginReactHooks from 'eslint-plugin-react-hooks'; -import pluginReact from 'eslint-plugin-react'; -import pluginNext from '@next/eslint-plugin-next'; -// import turboPlugin from 'eslint-plugin-turbo'; -import onlyWarn from 'eslint-plugin-only-warn'; +import { nextJsConfig } from '@roo-code-cloud/config-eslint/next-js'; /** @type {import("eslint").Linter.Config} */ -export default [ - js.configs.recommended, - eslintConfigPrettier, - ...tseslint.configs.recommended, - // { - // plugins: { - // turbo: turboPlugin, - // }, - // rules: { - // 'turbo/no-undeclared-env-vars': 'warn', - // }, - // }, - { - plugins: { - onlyWarn, - }, - }, - { - ignores: ['dist/**', '.next'], - }, - { - ...pluginReact.configs.flat.recommended, - languageOptions: { - ...pluginReact.configs.flat.recommended.languageOptions, - globals: { - ...globals.serviceworker, - }, - }, - }, - { - plugins: { - '@next/next': pluginNext, - }, - rules: { - ...pluginNext.configs.recommended.rules, - ...pluginNext.configs['core-web-vitals'].rules, - }, - }, - { - plugins: { - 'react-hooks': pluginReactHooks, - }, - settings: { react: { version: 'detect' } }, - rules: { - ...pluginReactHooks.configs.recommended.rules, - // React scope no longer necessary with new JSX transform. - 'react/react-in-jsx-scope': 'off', - }, - }, - { - rules: { - 'no-unused-vars': 'off', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - caughtErrorsIgnorePattern: '^_', - argsIgnorePattern: '^_', - }, - ], - }, - }, -]; +export default [...nextJsConfig]; diff --git a/apps/web/package.json b/apps/web/package.json index b9565e27ef..3b7651a975 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -2,20 +2,17 @@ "name": "@roo-code-cloud/web", "private": true, "scripts": { - "lint": "eslint .", - "check-types": "tsc --noEmit --pretty", + "lint": "next lint --max-warnings 0", + "check-types": "tsc --noEmit", "test": "dotenvx run -f .env.test -f .env -- vitest run", "dev": "next dev --turbopack", "build": "next build", "start": "next start", - "clean": "rimraf .next out coverage", + "clean": "rimraf .next .turbo out", "db:generate": "dotenvx run -f .env.development -- drizzle-kit generate", "db:migrate": "dotenvx run -f .env.development -- drizzle-kit migrate", "db:push": "dotenvx run -f .env.development -- drizzle-kit push", - "db:test:push": "dotenvx run -f .env.test -- drizzle-kit push", - "db:up": "docker compose up -d --wait && pnpm db:push --force && pnpm db:test:push --force", - "db:down": "docker compose down", - "db:reset": "pnpm db:down && rimraf .docker/data .docker/logs && pnpm db:up" + "db:test:push": "dotenvx run -f .env.test -- drizzle-kit push" }, "dependencies": { "@clerk/localizations": "^3.16.3", @@ -81,15 +78,16 @@ "@eslint/js": "^9.27.0", "@next/bundle-analyzer": "^15.3.3", "@next/eslint-plugin-next": "^15.3.3", + "@roo-code-cloud/config-eslint": "workspace:^", + "@roo-code-cloud/config-typescript": "workspace:^", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", "@types/node": "^22.15.27", "@types/pg": "^8.15.2", "@types/react": "^19.1.6", - "@vitejs/plugin-react": "^4.5.0", - "@vitest/coverage-v8": "^3.1.4", - "@vitest/expect": "^3.1.4", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.5.2", "drizzle-kit": "^0.31.1", "eslint-config-prettier": "^10.1.5", "eslint-plugin-only-warn": "^1.1.0", @@ -103,6 +101,6 @@ "tailwindcss-animate": "^1.0.7", "typescript-eslint": "^8.33.0", "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.1.4" + "vitest": "^3.2.3" } } diff --git a/apps/web/src/db/db.ts b/apps/web/src/db/db.ts index dde0bd73d8..16943eb346 100644 --- a/apps/web/src/db/db.ts +++ b/apps/web/src/db/db.ts @@ -4,11 +4,8 @@ import postgres from 'postgres'; import { Env } from '@/lib/server'; import * as schema from './schema'; -const pgClient = postgres(Env.DATABASE_URL, { prepare: false }); - -const client = drizzle({ client: pgClient, schema }); - -let testDb: typeof client | undefined = undefined; +const postgresClient = postgres(Env.DATABASE_URL, { prepare: false }); +const client = drizzle({ client: postgresClient, schema }); if (process.env.NODE_ENV === 'test') { if ( @@ -17,16 +14,12 @@ if (process.env.NODE_ENV === 'test') { ) { throw new Error('DATABASE_URL is not a test database'); } - - testDb = client; } -const disconnect = async () => { - await pgClient.end(); -}; +const disconnect = async () => postgresClient.end(); type DatabaseOrTransaction = | typeof client | Parameters[0]>[0]; -export { client, testDb, disconnect, type DatabaseOrTransaction }; +export { client, disconnect, type DatabaseOrTransaction }; diff --git a/apps/web/src/i18n/locale.ts b/apps/web/src/i18n/locale.ts index ee8716c846..808960fb5e 100644 --- a/apps/web/src/i18n/locale.ts +++ b/apps/web/src/i18n/locale.ts @@ -12,7 +12,9 @@ export const Locales: Record = { fr: 'Français', }; -export const getClerkLocale = (locale: string) => { +type ClerkLocalization = typeof enUS; + +export const getClerkLocale = (locale: string): ClerkLocalization => { if (!isLocale(locale)) { return enUS; } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 8562834e52..1e93d2beca 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,46 +1,16 @@ { + "extends": "@roo-code-cloud/config-typescript/nextjs.json", "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "removeComments": true, - "preserveConstEnums": true, - "strict": true, - "alwaysStrict": true, - "strictNullChecks": true, - "noUncheckedIndexedAccess": true, - "noImplicitAny": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "allowUnreachableCode": false, - "noFallthroughCasesInSwitch": true, - "target": "es2017", - "outDir": "out", - "sourceMap": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "allowJs": true, - "checkJs": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "jsx": "preserve", - "noEmit": true, - "isolatedModules": true, - "incremental": true, - "types": ["vitest/globals"], - "baseUrl": ".", - "paths": { "@/*": ["src/*"] }, - "plugins": [{ "name": "next" }] + "types": ["vitest/globals", "@testing-library/jest-dom"], + "paths": { "@/*": ["./src/*"] } }, - "exclude": ["./out/**/*", "./node_modules/**/*", ".next/**/*"], "include": [ - "**/*.mts", - "**/*.ts", - "**/*.tsx", "next-env.d.ts", - ".next/types/**/*.ts" - ] + "src/**/*.ts", + "src/**/*.tsx", + ".next/types/**/*.ts", + "drizzle.config.ts", + "vitest*.ts" + ], + "exclude": ["node_modules"] } diff --git a/apps/web/vitest-global-setup.ts b/apps/web/vitest-global-setup.ts deleted file mode 100644 index 366540e719..0000000000 --- a/apps/web/vitest-global-setup.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { sql } from 'drizzle-orm'; - -import { Env } from 'src/lib/server'; -import { testDb, disconnect } from 'src/db/server'; - -async function resetTestDatabase() { - const db = testDb; - - // Skip database reset if no database connection is available - if (!db) { - console.log('No database connection available, skipping database reset'); - return; - } - - try { - const tables = await db.execute<{ table_name: string }>(sql` - SELECT table_name - FROM information_schema.tables - WHERE table_schema = 'public' - AND table_type = 'BASE TABLE'; - `); - - const tableNames = tables.map((t) => t.table_name); - - for (const tableName of tableNames) { - await db.execute(sql`TRUNCATE TABLE "${sql.raw(tableName)}" CASCADE;`); - } - - console.log(`[${Env.DATABASE_URL}] TRUNCATE ${tableNames.join(', ')}`); - } catch (error) { - console.error('Error resetting database:', error); - throw error; - } -} - -export default async function () { - await resetTestDatabase(); - - return async () => { - await disconnect(); - }; -} diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts new file mode 100644 index 0000000000..1d0253416d --- /dev/null +++ b/apps/web/vitest.config.ts @@ -0,0 +1,41 @@ +import { resolve } from 'path'; + +import { defineConfig } from 'vitest/config'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': resolve(__dirname, './src'), + }, + }, + test: { + globals: true, + watch: false, + reporters: ['dot'], + projects: [ + { + extends: true, + test: { + name: 'server', + include: [ + 'src/**/*.test.{js,jsx,ts,tsx}', + '!src/{hooks,components}/**/*.test.{js,jsx,ts,tsx}', + ], + environment: 'node', + globalSetup: './vitest.setup.server.ts', + }, + }, + { + extends: true, + test: { + name: 'client', + include: ['src/{hooks,components}/**/*.test.{js,jsx,ts,tsx}'], + environment: 'jsdom', + setupFiles: './vitest.setup.client.ts', + }, + }, + ], + }, +}); diff --git a/apps/web/vitest-setup.ts b/apps/web/vitest.setup.client.ts similarity index 100% rename from apps/web/vitest-setup.ts rename to apps/web/vitest.setup.client.ts diff --git a/apps/web/vitest.setup.server.ts b/apps/web/vitest.setup.server.ts new file mode 100644 index 0000000000..2dcb4bd497 --- /dev/null +++ b/apps/web/vitest.setup.server.ts @@ -0,0 +1,38 @@ +import postgres from 'postgres'; +import { drizzle } from 'drizzle-orm/postgres-js'; +import { sql } from 'drizzle-orm'; + +let pgClient: ReturnType | undefined = undefined; + +async function resetTestDatabase() { + pgClient = postgres( + 'postgres://postgres:password@localhost:5432/roo_code_test', + { + prepare: false, + onnotice: () => {}, // Suppress NOTICE logs. + }, + ); + + const db = drizzle({ client: pgClient }); + + const tables = await db.execute<{ table_name: string }>(sql` + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + AND table_type = 'BASE TABLE'; + `); + + const tableNames = tables.map((t) => t.table_name); + + for (const tableName of tableNames) { + await db.execute(sql`TRUNCATE TABLE "${sql.raw(tableName)}" CASCADE;`); + } +} + +export default async function () { + await resetTestDatabase(); + + return async () => { + await pgClient?.end(); + }; +} diff --git a/apps/web/vitest.workspace.ts b/apps/web/vitest.workspace.ts deleted file mode 100644 index eaef921fba..0000000000 --- a/apps/web/vitest.workspace.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { defineWorkspace } from 'vitest/config'; -import react from '@vitejs/plugin-react'; -import tsconfigPaths from 'vite-tsconfig-paths'; - -export default defineWorkspace([ - // Server-side tests (node environment). - { - plugins: [react(), tsconfigPaths()], - test: { - name: 'server', - globals: true, - include: [ - 'src/**/*.test.{js,jsx,ts,tsx}', - '!src/hooks/**/*.test.{js,jsx,ts,tsx}', // Exclude hooks tests. - '!src/components/**/*.test.{js,jsx,ts,tsx}', // Exclude component tests. - ], - environment: 'node', - setupFiles: './vitest-setup.ts', - globalSetup: './vitest-global-setup.ts', - }, - }, - // Client-side tests (jsdom environment). - { - plugins: [react(), tsconfigPaths()], - test: { - name: 'client', - globals: true, - include: [ - 'src/hooks/**/*.test.{js,jsx,ts,tsx}', - 'src/components/**/*.test.{js,jsx,ts,tsx}', - ], - environment: 'jsdom', - setupFiles: './vitest-setup.ts', - // No globalSetup for client tests to avoid database connection issues. - }, - }, -]); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..15455d3fe0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,148 @@ +name: roo-code-cloud + +services: + base: + build: + context: ../../ + dockerfile: .docker/Dockerfile.base + image: roo-code-cloud-base + + postgres: + container_name: roo-code-cloud-postgres + image: postgres:17.5 + ports: + - "5432:5432" + volumes: + - ./.docker/data/postgres:/var/lib/postgresql/data + - ./.docker/scripts/postgres:/docker-entrypoint-initdb.d + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + - POSTGRES_DATABASES=roo_code_development,roo_code_test + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d roo_code_development"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 30s + + clickhouse: + container_name: roo-code-cloud-clickhouse + image: clickhouse/clickhouse-server + ports: + - "8123:8123" + - "9000:9000" + volumes: + - ./.docker/data/clickhouse:/var/lib/clickhouse/ + - ./.docker/logs/clickhouse:/var/log/clickhouse-server/ + - ./.docker/scripts/clickhouse:/docker-entrypoint-initdb.d + environment: + - CLICKHOUSE_DB=default + - CLICKHOUSE_USER=default + - CLICKHOUSE_PASSWORD=password + healthcheck: + test: + [ + "CMD-SHELL", + "wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1", + ] + interval: 5s + timeout: 5s + retries: 5 + start_period: 30s + + redis: + container_name: roo-code-cloud-redis + image: redis:7-alpine + ports: + - "6380:6379" + volumes: + - ./.docker/data/redis:/data + command: redis-server --appendonly yes + + roomote-dashboard: + container_name: roo-code-cloud-roomote-dashboard + build: + context: ../../ + dockerfile: .docker/Dockerfile.dashboard + image: roo-code-cloud-roomote-dashboard + ports: + - "3002:3002" + environment: + - REDIS_URL=redis://redis:6379 + - NODE_ENV=production + depends_on: + redis: + condition: service_started + + roomote-api: + container_name: roo-code-cloud-roomote-api + build: + context: ../../ + dockerfile: .docker/Dockerfile.api + image: roo-code-cloud-roomote-api + ports: + - "3001:3001" + environment: + - DATABASE_URL=postgresql://postgres:password@db:5432/cloud_agents + - REDIS_URL=redis://redis:6379 + - NODE_ENV=production + volumes: + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + + roomote-controller: + container_name: roo-code-cloud-roomote-controller + build: + context: ../../ + dockerfile: .docker/Dockerfile.controller + args: + - GH_TOKEN=${GH_TOKEN} + image: roo-code-cloud-roomote-controller + env_file: + - .env + environment: + - HOST_EXECUTION_METHOD=docker + - DATABASE_URL=postgresql://postgres:password@db:5432/cloud_agents + - REDIS_URL=redis://redis:6379 + - NODE_ENV=production + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /tmp/roomote/controller:/var/log/roomote + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + restart: unless-stopped + + roomote-worker: + build: + context: ../../ + dockerfile: .docker/Dockerfile.worker + args: + - GH_TOKEN=${GH_TOKEN} + image: roomote-worker + env_file: + - .env + environment: + - HOST_EXECUTION_METHOD=docker + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /tmp/roomote/worker:/var/log/roomote + stdin_open: true + tty: true + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + +networks: + default: + name: roo-code-cloud_default + driver: bridge diff --git a/package.json b/package.json index 9379725793..136c572d1a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,13 @@ "check-types": "turbo check-types --log-order grouped --output-logs new-only", "test": "turbo test --log-order grouped --output-logs new-only", "build": "turbo build --log-order grouped --output-logs new-only", - "clean": "turbo clean --log-order grouped --output-logs new-only && rimraf .turbo" + "clean": "turbo clean --log-order grouped --output-logs new-only && rimraf .turbo", + "services:up": "docker compose up postgres clickhouse redis roomote-dashboard -d --wait && pnpm db:push", + "services:down": "docker compose down postgres clickhouse redis roomote-dashboard", + "db:up": "docker compose up postgres clickhouse -d --wait && pnpm db:push", + "db:down": "docker compose down postgres clickhouse", + "db:push": "pnpm --filter @roo-code-cloud/web db:push --force && pnpm --filter @roo-code-cloud/web db:test:push --force", + "db:reset": "pnpm db:down && rimraf .docker/data .docker/logs && pnpm db:up" }, "devDependencies": { "@dotenvx/dotenvx": "^1.44.2", diff --git a/packages/config-eslint/base.js b/packages/config-eslint/base.js new file mode 100644 index 0000000000..cb7b805c79 --- /dev/null +++ b/packages/config-eslint/base.js @@ -0,0 +1,44 @@ +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import turboPlugin from 'eslint-plugin-turbo'; +import tseslint from 'typescript-eslint'; +import onlyWarn from 'eslint-plugin-only-warn'; + +/** + * A shared ESLint configuration for the repository. + * + * @type {import("eslint").Linter.Config[]} + * */ +export const config = [ + js.configs.recommended, + eslintConfigPrettier, + ...tseslint.configs.recommended, + { + plugins: { + turbo: turboPlugin, + }, + rules: { + 'turbo/no-undeclared-env-vars': 'off', + }, + }, + { + plugins: { + onlyWarn, + }, + }, + { + ignores: ['dist/**'], + }, + { + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + }, + }, +]; diff --git a/packages/config-eslint/next.js b/packages/config-eslint/next.js new file mode 100644 index 0000000000..8b2440c148 --- /dev/null +++ b/packages/config-eslint/next.js @@ -0,0 +1,22 @@ +import pluginNext from '@next/eslint-plugin-next'; + +import { reactConfig } from './react.js'; + +/** + * @type {import("eslint").Linter.Config[]} + */ +export const nextJsConfig = [ + ...reactConfig, + { + ignores: ['.next/**'], + }, + { + plugins: { + '@next/next': pluginNext, + }, + rules: { + ...pluginNext.configs.recommended.rules, + ...pluginNext.configs['core-web-vitals'].rules, + }, + }, +]; diff --git a/packages/config-eslint/package.json b/packages/config-eslint/package.json index a5d5e7c17b..649716b166 100644 --- a/packages/config-eslint/package.json +++ b/packages/config-eslint/package.json @@ -1,4 +1,22 @@ { "name": "@roo-code-cloud/config-eslint", - "private": true + "private": true, + "type": "module", + "exports": { + "./base": "./base.js", + "./react": "./react.js", + "./next-js": "./next.js" + }, + "devDependencies": { + "@eslint/js": "^9.22.0", + "@next/eslint-plugin-next": "^15.2.1", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.1", + "eslint-plugin-only-warn": "^1.1.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-turbo": "^2.4.4", + "globals": "^16.0.0", + "typescript-eslint": "^8.26.0" + } } diff --git a/packages/config-eslint/react.js b/packages/config-eslint/react.js new file mode 100644 index 0000000000..2cae96332a --- /dev/null +++ b/packages/config-eslint/react.js @@ -0,0 +1,38 @@ +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import typescriptEslint from 'typescript-eslint'; +import pluginReactHooks from 'eslint-plugin-react-hooks'; +import pluginReact from 'eslint-plugin-react'; +import globals from 'globals'; + +import { config } from './base.js'; + +/** + * @type {import("eslint").Linter.Config[]} + */ +export const reactConfig = [ + ...config, + js.configs.recommended, + eslintConfigPrettier, + ...typescriptEslint.configs.recommended, + { + ...pluginReact.configs.flat.recommended, + languageOptions: { + ...pluginReact.configs.flat.recommended.languageOptions, + globals: { + ...globals.serviceworker, + }, + }, + }, + { + plugins: { + 'react-hooks': pluginReactHooks, + }, + settings: { react: { version: 'detect' } }, + rules: { + ...pluginReactHooks.configs.recommended.rules, + // React scope no longer necessary with new JSX transform. + 'react/react-in-jsx-scope': 'off', + }, + }, +]; diff --git a/packages/config-typescript/base.json b/packages/config-typescript/base.json new file mode 100644 index 0000000000..5117f2a3d1 --- /dev/null +++ b/packages/config-typescript/base.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "incremental": false, + "isolatedModules": true, + "lib": ["es2022", "DOM", "DOM.Iterable"], + "module": "NodeNext", + "moduleDetection": "force", + "moduleResolution": "NodeNext", + "noUncheckedIndexedAccess": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "target": "ES2022" + } +} diff --git a/packages/config-typescript/cjs.json b/packages/config-typescript/cjs.json new file mode 100644 index 0000000000..6b30cbd0ed --- /dev/null +++ b/packages/config-typescript/cjs.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "module": "CommonJS", + "moduleResolution": "Node", + "esModuleInterop": true, + "target": "ES2022", + "lib": ["ES2022", "ESNext.Disposable", "DOM"], + "sourceMap": true, + "strict": true, + "skipLibCheck": true, + "useUnknownInCatchVariables": false + } +} diff --git a/packages/config-typescript/nextjs.json b/packages/config-typescript/nextjs.json new file mode 100644 index 0000000000..03c9a953bb --- /dev/null +++ b/packages/config-typescript/nextjs.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./base.json", + "compilerOptions": { + "plugins": [{ "name": "next" }], + "module": "ESNext", + "moduleResolution": "bundler", + "allowJs": true, + "jsx": "preserve", + "noEmit": true + } +} diff --git a/packages/config-typescript/package.json b/packages/config-typescript/package.json index 86a319a98a..5bdc715974 100644 --- a/packages/config-typescript/package.json +++ b/packages/config-typescript/package.json @@ -1,4 +1,7 @@ { "name": "@roo-code-cloud/config-typescript", - "private": true + "private": true, + "publishConfig": { + "access": "public" + } } diff --git a/packages/config-typescript/vscode-library.json b/packages/config-typescript/vscode-library.json new file mode 100644 index 0000000000..9de0aa75cf --- /dev/null +++ b/packages/config-typescript/vscode-library.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./base.json", + "compilerOptions": { + "types": ["vitest/globals"], + "outDir": "dist", + "module": "esnext", + "moduleResolution": "Bundler", + "noUncheckedIndexedAccess": false, + "useUnknownInCatchVariables": false + } +} diff --git a/packages/ipc/eslint.config.mjs b/packages/ipc/eslint.config.mjs new file mode 100644 index 0000000000..c072244298 --- /dev/null +++ b/packages/ipc/eslint.config.mjs @@ -0,0 +1,4 @@ +import { config } from "@roo-code-cloud/config-eslint/base" + +/** @type {import("eslint").Linter.Config} */ +export default [...config] diff --git a/packages/ipc/package.json b/packages/ipc/package.json new file mode 100644 index 0000000000..ebfbd641fe --- /dev/null +++ b/packages/ipc/package.json @@ -0,0 +1,22 @@ +{ + "name": "@roo-code-cloud/ipc", + "private": true, + "type": "module", + "exports": "./src/index.ts", + "scripts": { + "lint": "eslint src --ext=ts --max-warnings=0", + "check-types": "tsc --noEmit", + "clean": "rimraf .turbo" + }, + "dependencies": { + "@roo-code/types": "^1.26.0", + "node-ipc": "^12.0.0" + }, + "devDependencies": { + "@roo-code-cloud/config-eslint": "workspace:^", + "@roo-code-cloud/config-typescript": "workspace:^", + "@types/node": "20.x", + "@types/node-ipc": "^9.2.3", + "vitest": "^3.2.3" + } +} diff --git a/packages/ipc/src/index.ts b/packages/ipc/src/index.ts new file mode 100644 index 0000000000..1acebbac64 --- /dev/null +++ b/packages/ipc/src/index.ts @@ -0,0 +1,2 @@ +export * from './ipc-client.js'; +export * from './ipc-server.js'; diff --git a/packages/ipc/src/ipc-client.ts b/packages/ipc/src/ipc-client.ts new file mode 100644 index 0000000000..a99928d4e9 --- /dev/null +++ b/packages/ipc/src/ipc-client.ts @@ -0,0 +1,129 @@ +import EventEmitter from 'node:events'; +import * as crypto from 'node:crypto'; + +import ipc from 'node-ipc'; + +import { + type TaskCommand, + type IpcClientEvents, + type IpcMessage, + IpcOrigin, + IpcMessageType, + ipcMessageSchema, +} from '@roo-code/types'; + +export class IpcClient extends EventEmitter { + private readonly _socketPath: string; + private readonly _id: string; + private readonly _log: (...args: unknown[]) => void; + private _isConnected = false; + private _clientId?: string; + + constructor(socketPath: string, log = console.log) { + super(); + + this._socketPath = socketPath; + this._id = `roo-code-evals-${crypto.randomBytes(6).toString('hex')}`; + this._log = log; + + ipc.config.silent = true; + + ipc.connectTo(this._id, this.socketPath, () => { + ipc.of[this._id]?.on('connect', () => this.onConnect()); + ipc.of[this._id]?.on('disconnect', () => this.onDisconnect()); + ipc.of[this._id]?.on('message', (data) => this.onMessage(data)); + }); + } + + private onConnect() { + if (this._isConnected) { + return; + } + + this.log('[client#onConnect]'); + this._isConnected = true; + this.emit(IpcMessageType.Connect); + } + + private onDisconnect() { + if (!this._isConnected) { + return; + } + + this.log('[client#onDisconnect]'); + this._isConnected = false; + this.emit(IpcMessageType.Disconnect); + } + + private onMessage(data: unknown) { + if (typeof data !== 'object') { + this._log('[client#onMessage] invalid data', data); + return; + } + + const result = ipcMessageSchema.safeParse(data); + + if (!result.success) { + this.log('[client#onMessage] invalid payload', result.error, data); + return; + } + + const payload = result.data; + + if (payload.origin === IpcOrigin.Server) { + switch (payload.type) { + case IpcMessageType.Ack: + this._clientId = payload.data.clientId; + this.emit(IpcMessageType.Ack, payload.data); + break; + case IpcMessageType.TaskEvent: + this.emit(IpcMessageType.TaskEvent, payload.data); + break; + } + } + } + + private log(...args: unknown[]) { + this._log(...args); + } + + public sendCommand(command: TaskCommand) { + const message: IpcMessage = { + type: IpcMessageType.TaskCommand, + origin: IpcOrigin.Client, + clientId: this._clientId!, + data: command, + }; + + this.sendMessage(message); + } + + public sendMessage(message: IpcMessage) { + ipc.of[this._id]?.emit('message', message); + } + + public disconnect() { + try { + ipc.disconnect(this._id); + // @TODO: Should we set _disconnect here? + } catch (error) { + this.log('[client#disconnect] error disconnecting', error); + } + } + + public get socketPath() { + return this._socketPath; + } + + public get clientId() { + return this._clientId; + } + + public get isConnected() { + return this._isConnected; + } + + public get isReady() { + return this._isConnected && this._clientId !== undefined; + } +} diff --git a/packages/ipc/src/ipc-server.ts b/packages/ipc/src/ipc-server.ts new file mode 100644 index 0000000000..379fb01aae --- /dev/null +++ b/packages/ipc/src/ipc-server.ts @@ -0,0 +1,149 @@ +import EventEmitter from 'node:events'; +import { Socket } from 'node:net'; +import * as crypto from 'node:crypto'; + +import ipc from 'node-ipc'; + +import { + type IpcServerEvents, + type RooCodeIpcServer, + IpcOrigin, + IpcMessageType, + type IpcMessage, + ipcMessageSchema, +} from '@roo-code/types'; + +export class IpcServer + extends EventEmitter + implements RooCodeIpcServer +{ + private readonly _socketPath: string; + private readonly _log: (...args: unknown[]) => void; + private readonly _clients: Map; + + private _isListening = false; + + constructor(socketPath: string, log = console.log) { + super(); + + this._socketPath = socketPath; + this._log = log; + this._clients = new Map(); + } + + public listen() { + this._isListening = true; + + ipc.config.silent = true; + + ipc.serve(this.socketPath, () => { + ipc.server.on('connect', (socket) => this.onConnect(socket)); + ipc.server.on('socket.disconnected', (socket) => + this.onDisconnect(socket), + ); + ipc.server.on('message', (data) => this.onMessage(data)); + }); + + ipc.server.start(); + } + + private onConnect(socket: Socket) { + const clientId = crypto.randomBytes(6).toString('hex'); + this._clients.set(clientId, socket); + this.log( + `[server#onConnect] clientId = ${clientId}, # clients = ${this._clients.size}`, + ); + + this.send(socket, { + type: IpcMessageType.Ack, + origin: IpcOrigin.Server, + data: { clientId, pid: process.pid, ppid: process.ppid }, + }); + + this.emit(IpcMessageType.Connect, clientId); + } + + private onDisconnect(destroyedSocket: Socket) { + let disconnectedClientId: string | undefined; + + for (const [clientId, socket] of this._clients.entries()) { + if (socket === destroyedSocket) { + disconnectedClientId = clientId; + this._clients.delete(clientId); + break; + } + } + + this.log( + `[server#socket.disconnected] clientId = ${disconnectedClientId}, # clients = ${this._clients.size}`, + ); + + if (disconnectedClientId) { + this.emit(IpcMessageType.Disconnect, disconnectedClientId); + } + } + + private onMessage(data: unknown) { + if (typeof data !== 'object') { + this.log('[server#onMessage] invalid data', data); + return; + } + + const result = ipcMessageSchema.safeParse(data); + + if (!result.success) { + this.log( + '[server#onMessage] invalid payload', + result.error.format(), + data, + ); + return; + } + + const payload = result.data; + + if (payload.origin === IpcOrigin.Client) { + switch (payload.type) { + case IpcMessageType.TaskCommand: + this.emit(IpcMessageType.TaskCommand, payload.clientId, payload.data); + break; + default: + this.log( + `[server#onMessage] unhandled payload: ${JSON.stringify(payload)}`, + ); + break; + } + } + } + + private log(...args: unknown[]) { + this._log(...args); + } + + public broadcast(message: IpcMessage) { + // this.log("[server#broadcast] message =", message) + ipc.server.broadcast('message', message); + } + + public send(client: string | Socket, message: IpcMessage) { + // this.log("[server#send] message =", message) + + if (typeof client === 'string') { + const socket = this._clients.get(client); + + if (socket) { + ipc.server.emit(socket, 'message', message); + } + } else { + ipc.server.emit(client, 'message', message); + } + } + + public get socketPath() { + return this._socketPath; + } + + public get isListening() { + return this._isListening; + } +} diff --git a/packages/ipc/tsconfig.json b/packages/ipc/tsconfig.json new file mode 100644 index 0000000000..49f9f64bcd --- /dev/null +++ b/packages/ipc/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@roo-code-cloud/config-typescript/base.json", + "compilerOptions": { + "types": ["vitest/globals"] + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57809fbb3c..6dbe762f5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,87 @@ importers: specifier: ^5.8.3 version: 5.8.3 - apps/roomote: {} + apps/roomote: + dependencies: + '@bull-board/api': + specifier: ^6.10.1 + version: 6.10.1(@bull-board/ui@6.10.1) + '@bull-board/express': + specifier: ^6.10.1 + version: 6.10.1 + '@bull-board/ui': + specifier: ^6.10.1 + version: 6.10.1 + '@roo-code-cloud/ipc': + specifier: workspace:^ + version: link:../../packages/ipc + '@roo-code/types': + specifier: ^1.26.0 + version: 1.26.0 + bullmq: + specifier: ^5.37.0 + version: 5.54.3 + drizzle-orm: + specifier: ^0.44.1 + version: 0.44.2(@electric-sql/pglite@0.3.0)(@libsql/client-wasm@0.15.5)(@opentelemetry/api@1.9.0)(@types/pg@8.15.2)(pg@8.15.6)(postgres@3.4.7) + execa: + specifier: ^9.6.0 + version: 9.6.0 + express: + specifier: ^5.1.0 + version: 5.1.0 + ioredis: + specifier: ^5.4.3 + version: 5.6.1 + next: + specifier: ^15.3.3 + version: 15.3.3(@babel/core@7.27.3)(@opentelemetry/api@1.9.0)(@playwright/test@1.52.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + p-wait-for: + specifier: ^5.0.2 + version: 5.0.2 + postgres: + specifier: ^3.4.7 + version: 3.4.7 + react: + specifier: ^19.1.0 + version: 19.1.0 + react-dom: + specifier: ^19.1.0 + version: 19.1.0(react@19.1.0) + zod: + specifier: ^3.25.41 + version: 3.25.41 + devDependencies: + '@roo-code-cloud/config-eslint': + specifier: workspace:^ + version: link:../../packages/config-eslint + '@roo-code-cloud/config-typescript': + specifier: workspace:^ + version: link:../../packages/config-typescript + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 + '@types/node': + specifier: ^22.15.20 + version: 22.15.32 + '@types/react': + specifier: ^19.1.6 + version: 19.1.6 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.6(@types/react@19.1.6) + concurrently: + specifier: ^9.1.0 + version: 9.1.2 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.1 + tsx: + specifier: ^4.19.3 + version: 4.20.3 + vitest: + specifier: ^3.2.3 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.32)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) apps/web: dependencies: @@ -61,46 +141,46 @@ importers: version: 0.5.5(pino@9.7.0) '@radix-ui/react-accordion': specifier: ^1.2.11 - version: 1.2.11(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.11(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-checkbox': specifier: ^1.3.2 - version: 1.3.2(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.2(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dialog': specifier: ^1.1.14 - version: 1.1.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dropdown-menu': specifier: ^2.1.15 - version: 2.1.15(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-icons': specifier: ^1.3.2 version: 1.3.2(react@19.1.0) '@radix-ui/react-label': specifier: ^2.1.7 - version: 2.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-popover': specifier: ^1.1.14 - version: 1.1.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-separator': specifier: ^1.1.7 - version: 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slider': specifier: ^1.3.5 - version: 1.3.5(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.3.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-switch': specifier: ^1.2.5 - version: 1.2.5(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tabs': specifier: ^1.1.12 - version: 1.1.12(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.12(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-toast': specifier: ^1.2.14 - version: 1.2.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.2.7 - version: 1.2.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@roo-code/types': specifier: ^1.26.0 version: 1.26.0 @@ -130,7 +210,7 @@ importers: version: 2.1.1 cmdk: specifier: ^1.1.1 - version: 1.1.1(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.1(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -208,7 +288,7 @@ importers: version: 11.1.0 vaul: specifier: ^1.1.2 - version: 1.1.2(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 1.1.2(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) zod: specifier: ^3.25.41 version: 3.25.41 @@ -225,12 +305,18 @@ importers: '@next/eslint-plugin-next': specifier: ^15.3.3 version: 15.3.3 + '@roo-code-cloud/config-eslint': + specifier: workspace:^ + version: link:../../packages/config-eslint + '@roo-code-cloud/config-typescript': + specifier: workspace:^ + version: link:../../packages/config-typescript '@testing-library/jest-dom': specifier: ^6.6.3 version: 6.6.3 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.0) @@ -243,15 +329,12 @@ importers: '@types/react': specifier: ^19.1.6 version: 19.1.6 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.6(@types/react@19.1.6) '@vitejs/plugin-react': - specifier: ^4.5.0 - version: 4.5.0(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@vitest/coverage-v8': - specifier: ^3.1.4 - version: 3.1.4(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@vitest/expect': - specifier: ^3.1.4 - version: 3.1.4 + specifier: ^4.5.2 + version: 4.5.2(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) drizzle-kit: specifier: ^0.31.1 version: 0.31.1 @@ -292,15 +375,71 @@ importers: specifier: ^5.1.4 version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) vitest: - specifier: ^3.1.4 - version: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + specifier: ^3.2.3 + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) - packages/config-eslint: {} + packages/config-eslint: + devDependencies: + '@eslint/js': + specifier: ^9.22.0 + version: 9.29.0 + '@next/eslint-plugin-next': + specifier: ^15.2.1 + version: 15.3.3 + eslint: + specifier: ^9.27.0 + version: 9.29.0(jiti@2.4.2) + eslint-config-prettier: + specifier: ^10.1.1 + version: 10.1.5(eslint@9.29.0(jiti@2.4.2)) + eslint-plugin-only-warn: + specifier: ^1.1.0 + version: 1.1.0 + eslint-plugin-react: + specifier: ^7.37.4 + version: 7.37.5(eslint@9.29.0(jiti@2.4.2)) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.29.0(jiti@2.4.2)) + eslint-plugin-turbo: + specifier: ^2.4.4 + version: 2.5.3(eslint@9.29.0(jiti@2.4.2))(turbo@2.5.4) + globals: + specifier: ^16.0.0 + version: 16.2.0 + typescript-eslint: + specifier: ^8.26.0 + version: 8.33.0(eslint@9.29.0(jiti@2.4.2))(typescript@5.8.3) packages/config-typescript: {} packages/db: {} + packages/ipc: + dependencies: + '@roo-code/types': + specifier: ^1.26.0 + version: 1.26.0 + node-ipc: + specifier: ^12.0.0 + version: 12.0.0 + devDependencies: + '@roo-code-cloud/config-eslint': + specifier: workspace:^ + version: link:../config-eslint + '@roo-code-cloud/config-typescript': + specifier: workspace:^ + version: link:../config-typescript + '@types/node': + specifier: 20.x + version: 20.19.1 + '@types/node-ipc': + specifier: ^9.2.3 + version: 9.2.3 + vitest: + specifier: ^3.2.3 + version: 3.2.4(@types/debug@4.1.12)(@types/node@20.19.1)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + packages: '@adobe/css-tools@4.4.3': @@ -335,6 +474,10 @@ packages: resolution: {integrity: sha512-hyrN8ivxfvJ4i0fIJuV4EOlV0WDMz5Ui4StRTgVaAvWeiRCilXgwVvxJKtFQ3TKtHgJscB2YiXKGNJuVwhQMtA==} engines: {node: '>=6.9.0'} + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.27.3': resolution: {integrity: sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==} engines: {node: '>=6.9.0'} @@ -373,11 +516,20 @@ packages: resolution: {integrity: sha512-h/eKy9agOya1IGuLaZ9tEUgz+uIRXcbtOhRtUyyMf8JFmn1iT13vnl/IGVWSkdOCG/pC57U4S1jnAabAavTMwg==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.27.3': resolution: {integrity: sha512-xyYxRj6+tLNDTWi0KCBcZ9V7yg3/lwL9DWh9Uwh/RIVlIfFidggcgxKX3GCXwCiswwcGRawBKbEg2LG/Y8eJhw==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -406,13 +558,28 @@ packages: resolution: {integrity: sha512-lId/IfN/Ye1CIu8xG7oKBHXd2iNb2aW1ilPszzGcJug6M8RCKfVNcYhpI5+bMvFYjK7lXIM0R+a+6r8xhHp2FQ==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.27.4': + resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.27.3': resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} - engines: {node: '>=18'} + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + engines: {node: '>=6.9.0'} + + '@bull-board/api@6.10.1': + resolution: {integrity: sha512-VPkZa2XZI2Wk2MqK1XyiiS+tOhNan54mnm2fpv2KA0fdZ92mQqNjhKkOpsykhQv9XUEc8cCRlZqGxf67YCMJbQ==} + peerDependencies: + '@bull-board/ui': 6.10.1 + + '@bull-board/express@6.10.1': + resolution: {integrity: sha512-IZl+t6B8bGCHqd/8Mbvit9RXqndXBe2Kx7pYHq7PZRrEKjK9b643Uizlc+OVqmWpLSjpQMP96K1SO5/Nq24o+A==} + + '@bull-board/ui@6.10.1': + resolution: {integrity: sha512-b6z6MBid/0DEShAMFPjPVZoPSoWqRBHCvTknyaxr/m8gL2/C+QP7jlCXut+L7uTFbCj9qs+CreAP0x/VdLI/Ig==} '@clerk/backend@1.34.0': resolution: {integrity: sha512-9rZ8hQJVpX5KX2bEpiuVXfpjhojQCiqCWADJDdCI0PCeKxn58Ep0JPYiIcczg4VKUc3a7jve9vXylykG2XajLQ==} @@ -1038,6 +1205,9 @@ packages: cpu: [x64] os: [win32] + '@ioredis/commands@1.2.0': + resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1046,10 +1216,6 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -1100,6 +1266,36 @@ packages: resolution: {integrity: sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==} engines: {node: '>= 10'} + '@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] + '@next/bundle-analyzer@15.3.3': resolution: {integrity: sha512-9gddnjACK6yOa5IkmeFyzcwZh2rscsb6ZspTd7tymPYKQM96fJuKjn9HrRtPNKiMm7ExKNadAJqREmHdBgHZ9A==} @@ -1369,10 +1565,6 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@playwright/test@1.52.0': resolution: {integrity: sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==} engines: {node: '>=18'} @@ -1838,8 +2030,8 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@rolldown/pluginutils@1.0.0-beta.9': - resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==} + '@rolldown/pluginutils@1.0.0-beta.11': + resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==} '@rollup/plugin-commonjs@28.0.1': resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} @@ -2060,6 +2252,9 @@ packages: '@schummar/icu-type-parser@1.21.5': resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@sentry-internal/browser-utils@9.23.0': resolution: {integrity: sha512-hyN2Q6mh7ggw8sDVHeRyWz5LR6gjvf8zHSzQnMaF7QkeSyaeGM/SVSL4ODwqR9TRH7U2ku6nZFMbKhaBPV+Hfg==} engines: {node: '>=18'} @@ -2175,6 +2370,10 @@ packages: peerDependencies: webpack: '>=4.40.0' + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -2369,6 +2568,12 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -2402,6 +2607,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -2420,9 +2628,18 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/express-serve-static-core@5.0.6': + resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} + + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/js-cookie@2.2.7': resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} @@ -2435,6 +2652,9 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} @@ -2444,6 +2664,12 @@ packages: '@types/mysql@2.15.26': resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} + '@types/node-ipc@9.2.3': + resolution: {integrity: sha512-/MvSiF71fYf3+zwqkh/zkVkZj1hl1Uobre9EMFy08mqfJNAmpR0vmPgOUdEIDVgifxHj6G1vYMPLSBLLxoDACQ==} + + '@types/node@20.19.1': + resolution: {integrity: sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==} + '@types/node@22.15.27': resolution: {integrity: sha512-5fF+eu5mwihV2BeVtX5vijhdaZOfkQTATrePEaXTcKqI16LhJ7gi2/Vhd9OZM0UojcdmiOCVg5rrax+i1MdoQQ==} @@ -2462,9 +2688,26 @@ packages: '@types/pg@8.6.1': resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@19.1.6': + resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} + peerDependencies: + '@types/react': ^19.0.0 + '@types/react@19.1.6': resolution: {integrity: sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==} + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + '@types/shimmer@1.2.0': resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} @@ -2540,49 +2783,40 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - '@vitejs/plugin-react@4.5.0': - resolution: {integrity: sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==} + '@vitejs/plugin-react@4.5.2': + resolution: {integrity: sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 - '@vitest/coverage-v8@3.1.4': - resolution: {integrity: sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==} - peerDependencies: - '@vitest/browser': 3.1.4 - vitest: 3.1.4 - peerDependenciesMeta: - '@vitest/browser': - optional: true + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@3.1.4': - resolution: {integrity: sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==} - - '@vitest/mocker@3.1.4': - resolution: {integrity: sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==} + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.1.4': - resolution: {integrity: sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/runner@3.1.4': - resolution: {integrity: sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==} + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/snapshot@3.1.4': - resolution: {integrity: sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==} + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/spy@3.1.4': - resolution: {integrity: sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/utils@3.1.4': - resolution: {integrity: sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -2642,6 +2876,10 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2778,6 +3016,9 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} @@ -2799,6 +3040,10 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -2823,10 +3068,17 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bullmq@5.54.3: + resolution: {integrity: sha512-MVK2pOkB3hvrIcubwI8dS4qWHJLNKakKPpgRBTw91sIpPZArmvZ4t2hvryyEaJXJbAS/JHd6pKYOUd+RGRkWQQ==} + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -2922,10 +3174,21 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + 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: @@ -2973,9 +3236,30 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concurrently@9.1.2: + resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} + engines: {node: '>=18'} + hasBin: true + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -2983,6 +3267,17 @@ packages: copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3114,6 +3409,14 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + 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'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -3249,6 +3552,98 @@ packages: sqlite3: optional: true + drizzle-orm@0.44.2: + resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + drizzle-zod@0.7.1: resolution: {integrity: sha512-nZzALOdz44/AL2U005UlmMqaQ1qe5JfanvLujiTHiiT8+vZJTBFhj3pY4Vk+L6UWyKFfNmLhk602Hn4kCTynKQ==} peerDependencies: @@ -3265,6 +3660,10 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + easy-stack@1.0.1: + resolution: {integrity: sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==} + engines: {node: '>=6.0.0'} + ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} @@ -3272,6 +3671,14 @@ packages: resolution: {integrity: sha512-r6kEJXDKecVOCj2nLMuXK/FCPeurW33+3JRpfXVbjLja3XUYFfD9I/JBreH6sUyzcm3G/YQboBjMla6poKeSdA==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + electron-to-chromium@1.5.161: resolution: {integrity: sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA==} @@ -3284,6 +3691,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -3359,6 +3770,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -3450,6 +3864,14 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-pubsub@5.0.3: + resolution: {integrity: sha512-2QiHxshejKgJrYMzSI9MEHrvhmzxBL+eLyiM5IiyjDBySkgwS2+tdtnO3gbx8pEisu/yOFCIhfCb63gCEu0yBQ==} + engines: {node: '>=13.0.0'} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -3468,10 +3890,18 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.1: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -3526,14 +3956,25 @@ packages: picomatch: optional: true + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -3560,6 +4001,14 @@ packages: forwarded-parse@2.1.2: resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -3590,6 +4039,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-east-asian-width@1.3.0: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} @@ -3610,6 +4063,10 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} @@ -3628,15 +4085,15 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - glob@11.0.2: resolution: {integrity: sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==} engines: {node: 20 || >=22} hasBin: true + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} @@ -3734,6 +4191,10 @@ packages: html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -3750,6 +4211,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -3788,6 +4253,13 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} @@ -3805,6 +4277,14 @@ packages: intl-messageformat@10.7.16: resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} + ioredis@5.6.1: + resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} + engines: {node: '>=12.22.0'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -3918,6 +4398,9 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} @@ -3937,6 +4420,10 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} @@ -3949,6 +4436,10 @@ packages: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -3961,6 +4452,12 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -3971,33 +4468,19 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.1.1: resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} engines: {node: 20 || >=22} + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -4020,9 +4503,20 @@ packages: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} + js-message@1.0.7: + resolution: {integrity: sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==} + engines: {node: '>=0.6.0'} + + js-queue@2.0.2: + resolution: {integrity: sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==} + engines: {node: '>=1.0.0'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -4178,9 +4672,15 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + lodash.isboolean@3.0.3: resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} @@ -4219,6 +4719,9 @@ packages: loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} + lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -4241,6 +4744,10 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + 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 @@ -4252,13 +4759,6 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - map-obj@1.0.1: resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} engines: {node: '>=0.10.0'} @@ -4298,10 +4798,18 @@ packages: mdn-data@2.0.14: resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + meow@9.0.0: resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} engines: {node: '>=10'} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4380,10 +4888,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -4403,6 +4919,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + minimatch@8.0.4: resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} engines: {node: '>=16 || 14 >=14.17'} @@ -4430,6 +4950,11 @@ packages: resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} engines: {node: '>= 18'} + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + mkdirp@3.0.1: resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} engines: {node: '>=10'} @@ -4445,6 +4970,13 @@ 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.4: + resolution: {integrity: sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg==} + nano-css@5.6.2: resolution: {integrity: sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==} peerDependencies: @@ -4518,6 +5050,9 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -4527,9 +5062,20 @@ packages: encoding: optional: true + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-ipc@12.0.0: + resolution: {integrity: sha512-QHJ2gAJiqA3cM7cQiRjLsfCOBRB0TwQ6axYD4FSllQWipEbP6i7Se1dP8EzPKk5J1nCe27W69eqPmCoKyQ61Vg==} + engines: {node: '>=14'} + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -4545,6 +5091,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + nwsapi@2.2.20: resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} @@ -4587,6 +5137,10 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -4626,10 +5180,18 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + p-wait-for@5.0.2: + resolution: {integrity: sha512-lwx6u1CotQYPVju77R+D0vFomni/AqRfqLmqQ8hekklqZ6gAY9rONh7lBQ0uxWMkC2AuX9b2DVAl8To0NyP1JA==} + engines: {node: '>=12'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -4644,17 +5206,33 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -4666,6 +5244,10 @@ packages: resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} engines: {node: 20 || >=22} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -4825,6 +5407,13 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@5.0.0: resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} @@ -4842,6 +5431,10 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -4869,6 +5462,14 @@ packages: randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + react-dom@19.1.0: resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: @@ -4965,6 +5566,12 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@4.7.0: resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4991,6 +5598,17 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-info@3.1.0: + resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -5005,6 +5623,10 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -5062,6 +5684,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -5071,10 +5697,16 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -5124,6 +5756,10 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + serialize-error@8.1.0: resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} engines: {node: '>=10'} @@ -5131,6 +5767,10 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} @@ -5150,6 +5790,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.2: resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -5162,6 +5805,10 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} @@ -5278,6 +5925,17 @@ packages: resolution: {integrity: sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==} engines: {node: '>=6'} + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -5324,6 +5982,12 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -5342,6 +6006,10 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -5350,6 +6018,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + stripe@18.2.0: resolution: {integrity: sha512-RpOaGh5CLs3SYeVXw1CIQZNwPVADBJtgNyUgu+ZkIvu3u4pkZvNrlKr+WaLoNjSPQWef0dikxDS2AKHBl/l3bg==} engines: {node: '>=12.*'} @@ -5359,6 +6030,14 @@ packages: '@types/node': optional: true + strong-type@0.1.6: + resolution: {integrity: sha512-eJe5caH6Pi5oMMeQtIoBPpvNu/s4jiyb63u5tkHNnQXomK+puyQ5i+Z5iTLBr/xUz/pIcps0NSfzzFI34+gAXg==} + engines: {node: '>=12.0.0'} + + strong-type@1.1.0: + resolution: {integrity: sha512-X5Z6riticuH5GnhUyzijfDi1SoXas8ODDyN7K8lJeQK+Jfi4dKdoJGL4CXTskY/ATBcN+rz5lROGn1tAUkOX7g==} + engines: {node: '>=12.21.0'} + style-to-js@1.1.16: resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} @@ -5441,10 +6120,6 @@ packages: engines: {node: '>=10'} hasBin: true - test-exclude@7.0.1: - resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} - engines: {node: '>=18'} - thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} @@ -5452,6 +6127,9 @@ packages: resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} engines: {node: '>=10'} + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -5465,16 +6143,16 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} - tinypool@1.0.2: - resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} tldts-core@6.1.86: @@ -5491,6 +6169,10 @@ packages: toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -5506,6 +6188,10 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -5605,6 +6291,10 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5640,6 +6330,10 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -5658,9 +6352,17 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + unplugin@1.0.1: resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -5700,6 +6402,9 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true @@ -5711,6 +6416,10 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: @@ -5726,8 +6435,8 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} - vite-node@3.1.4: - resolution: {integrity: sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -5779,16 +6488,16 @@ packages: yaml: optional: true - vitest@3.1.4: - resolution: {integrity: sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.1.4 - '@vitest/ui': 3.1.4 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -5948,6 +6657,10 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -5967,10 +6680,26 @@ packages: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + zod@3.25.41: resolution: {integrity: sha512-8+sDJTGtCYIDBhdqDygp0ffj8kzziRKqAJPhpYObbElJ+3TRe/mnlnwH+/OMa3kKhueS4Drm5UMW00/u1p07zA==} @@ -6032,9 +6761,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.27.4': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.3 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.27.3': dependencies: - '@babel/parser': 7.27.3 + '@babel/parser': 7.27.5 '@babel/types': 7.27.3 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 @@ -6050,7 +6799,7 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.3 + '@babel/traverse': 7.27.4 '@babel/types': 7.27.3 transitivePeerDependencies: - supports-color @@ -6060,7 +6809,16 @@ snapshots: '@babel/core': 7.27.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.3 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color @@ -6077,18 +6835,27 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.27.3 + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + '@babel/parser@7.27.3': dependencies: '@babel/types': 7.27.3 - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.3)': + '@babel/parser@7.27.5': dependencies: - '@babel/core': 7.27.3 + '@babel/types': 7.27.3 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.3)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.27.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/runtime@7.27.3': {} @@ -6098,7 +6865,7 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.3 + '@babel/parser': 7.27.5 '@babel/types': 7.27.3 '@babel/traverse@7.27.3': @@ -6113,12 +6880,45 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.27.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.3 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/types': 7.27.3 + debug: 4.4.1 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/types@7.27.3': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@bcoe/v8-coverage@1.0.2': {} + '@babel/types@7.27.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bull-board/api@6.10.1(@bull-board/ui@6.10.1)': + dependencies: + '@bull-board/ui': 6.10.1 + redis-info: 3.1.0 + + '@bull-board/express@6.10.1': + dependencies: + '@bull-board/api': 6.10.1(@bull-board/ui@6.10.1) + '@bull-board/ui': 6.10.1 + ejs: 3.1.10 + express: 5.1.0 + transitivePeerDependencies: + - supports-color + + '@bull-board/ui@6.10.1': + dependencies: + '@bull-board/api': 6.10.1(@bull-board/ui@6.10.1) '@clerk/backend@1.34.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: @@ -6588,6 +7388,8 @@ snapshots: '@img/sharp-win32-x64@0.34.2': optional: true + '@ioredis/commands@1.2.0': {} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -6601,8 +7403,6 @@ snapshots: dependencies: minipass: 7.1.2 - '@istanbuljs/schema@0.1.3': {} - '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -6666,6 +7466,24 @@ snapshots: '@msgpack/msgpack@2.8.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 + '@next/bundle-analyzer@15.3.3': dependencies: webpack-bundle-analyzer: 4.10.1 @@ -6965,9 +7783,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@pkgjs/parseargs@0.11.0': - optional: true - '@playwright/test@1.52.0': dependencies: playwright: 1.52.0 @@ -6986,37 +7801,39 @@ snapshots: '@radix-ui/primitive@1.1.2': {} - '@radix-ui/react-accordion@1.2.11(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-accordion@1.2.11(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collapsible': 1.1.11(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-collection': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collapsible': 1.1.11(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-arrow@1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-checkbox@1.3.2(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-checkbox@1.3.2(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.6)(react@19.1.0) @@ -7024,32 +7841,35 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-collapsible@1.1.11(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collapsible@1.1.11(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-collection@1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.6)(react@19.1.0)': dependencies: @@ -7063,18 +7883,18 @@ snapshots: optionalDependencies: '@types/react': 19.1.6 - '@radix-ui/react-dialog@1.1.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) aria-hidden: 1.2.6 @@ -7083,6 +7903,7 @@ snapshots: react-remove-scroll: 2.7.0(@types/react@19.1.6)(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/react-direction@1.1.1(@types/react@19.1.6)(react@19.1.0)': dependencies: @@ -7090,31 +7911,33 @@ snapshots: optionalDependencies: '@types/react': 19.1.6 - '@radix-ui/react-dismissable-layer@1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-dropdown-menu@2.1.15(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-menu': 2.1.15(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/react-focus-guards@1.1.2(@types/react@19.1.6)(react@19.1.0)': dependencies: @@ -7122,15 +7945,16 @@ snapshots: optionalDependencies: '@types/react': 19.1.6 - '@radix-ui/react-focus-scope@1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/react-icons@1.3.2(react@19.1.0)': dependencies: @@ -7143,30 +7967,31 @@ snapshots: optionalDependencies: '@types/react': 19.1.6 - '@radix-ui/react-label@2.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-menu@2.1.15(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-menu@2.1.15(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) aria-hidden: 1.2.6 @@ -7175,20 +8000,21 @@ snapshots: react-remove-scroll: 2.7.0(@types/react@19.1.6)(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-popover@1.1.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popover@1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-focus-scope': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) aria-hidden: 1.2.6 @@ -7197,14 +8023,15 @@ snapshots: react-remove-scroll: 2.7.0(@types/react@19.1.6)(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-popper@1.2.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-popper@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@floating-ui/react-dom': 2.1.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-arrow': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.6)(react@19.1.0) @@ -7214,17 +8041,19 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-portal@1.1.9(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-presence@1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-presence@1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) @@ -7232,48 +8061,52 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-primitive@2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-roving-focus@1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-separator@1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-slider@1.3.5(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-slider@1.3.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/number': 1.1.1 '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.6)(react@19.1.0) @@ -7282,6 +8115,7 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/react-slot@1.2.3(@types/react@19.1.6)(react@19.1.0)': dependencies: @@ -7290,12 +8124,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.6 - '@radix-ui/react-switch@1.2.5(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-switch@1.2.5(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.6)(react@19.1.0) @@ -7303,59 +8137,63 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-tabs@1.1.12(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tabs@1.1.12(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-direction': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-roving-focus': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-toast@1.2.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-toast@1.2.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-collection': 1.1.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) - '@radix-ui/react-tooltip@1.2.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-context': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-dismissable-layer': 1.1.10(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-popper': 1.2.7(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-portal': 1.1.9(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-presence': 1.1.4(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': 1.2.3(@types/react@19.1.6)(react@19.1.0) '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.6)(react@19.1.0)': dependencies: @@ -7411,17 +8249,18 @@ snapshots: optionalDependencies: '@types/react': 19.1.6 - '@radix-ui/react-visually-hidden@1.2.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@radix-ui/rect@1.1.1': {} - '@rolldown/pluginutils@1.0.0-beta.9': {} + '@rolldown/pluginutils@1.0.0-beta.11': {} '@rollup/plugin-commonjs@28.0.1(rollup@4.35.0)': dependencies: @@ -7564,6 +8403,8 @@ snapshots: '@schummar/icu-type-parser@1.21.5': {} + '@sec-ant/readable-stream@0.4.1': {} + '@sentry-internal/browser-utils@9.23.0': dependencies: '@sentry/core': 9.23.0 @@ -7746,6 +8587,8 @@ snapshots: - encoding - supports-color + '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/utils@0.3.0': {} '@swc/counter@0.1.3': {} @@ -7876,7 +8719,7 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.3 '@testing-library/dom': 10.4.0 @@ -7884,6 +8727,7 @@ snapshots: react-dom: 19.1.0(react@19.1.0) optionalDependencies: '@types/react': 19.1.6 + '@types/react-dom': 19.1.6(@types/react@19.1.6) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: @@ -7912,9 +8756,18 @@ snapshots: dependencies: '@babel/types': 7.27.3 + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.15.32 + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.27 + '@types/node': 22.15.32 '@types/d3-array@3.2.1': {} @@ -7944,6 +8797,8 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 @@ -7964,10 +8819,25 @@ snapshots: '@types/estree@1.0.8': {} + '@types/express-serve-static-core@5.0.6': + dependencies: + '@types/node': 22.15.32 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.0.6 + '@types/serve-static': 1.15.8 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 + '@types/http-errors@2.0.5': {} + '@types/js-cookie@2.2.7': {} '@types/json-schema@7.0.15': {} @@ -7975,19 +8845,29 @@ snapshots: '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 22.15.27 + '@types/node': 22.15.32 '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 + '@types/mime@1.3.5': {} + '@types/minimist@1.2.5': {} '@types/ms@2.1.0': {} '@types/mysql@2.15.26': dependencies: - '@types/node': 22.15.27 + '@types/node': 22.15.32 + + '@types/node-ipc@9.2.3': + dependencies: + '@types/node': 22.15.32 + + '@types/node@20.19.1': + dependencies: + undici-types: 6.21.0 '@types/node@22.15.27': dependencies: @@ -8005,27 +8885,46 @@ snapshots: '@types/pg@8.15.2': dependencies: - '@types/node': 22.15.27 + '@types/node': 22.15.32 pg-protocol: 1.10.0 pg-types: 4.0.2 '@types/pg@8.6.1': dependencies: - '@types/node': 22.15.27 + '@types/node': 22.15.32 pg-protocol: 1.10.0 pg-types: 2.2.0 + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@19.1.6(@types/react@19.1.6)': + dependencies: + '@types/react': 19.1.6 + '@types/react@19.1.6': dependencies: csstype: 3.1.3 + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.15.32 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.15.32 + '@types/send': 0.17.5 + '@types/shimmer@1.2.0': {} '@types/stack-trace@0.0.33': {} '@types/tedious@4.0.14': dependencies: - '@types/node': 22.15.27 + '@types/node': 22.15.32 '@types/unist@2.0.11': {} @@ -8125,74 +9024,74 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.5.0(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@vitejs/plugin-react@4.5.2(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@babel/core': 7.27.3 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.3) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.3) - '@rolldown/pluginutils': 1.0.0-beta.9 + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.27.4) + '@rolldown/pluginutils': 1.0.0-beta.11 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 vite: 6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@3.1.4(vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@vitest/expect@3.2.4': dependencies: - '@ampproject/remapping': 2.3.0 - '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.1 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magic-string: 0.30.17 - magicast: 0.3.5 - std-env: 3.9.0 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.1.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@3.1.4': - dependencies: - '@vitest/spy': 3.1.4 - '@vitest/utils': 3.1.4 + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.4(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': dependencies: - '@vitest/spy': 3.1.4 + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: vite: 6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) - '@vitest/pretty-format@3.1.4': + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.5(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.1.4': + '@vitest/runner@3.2.4': dependencies: - '@vitest/utils': 3.1.4 + '@vitest/utils': 3.2.4 pathe: 2.0.3 + strip-literal: 3.0.0 - '@vitest/snapshot@3.1.4': + '@vitest/snapshot@3.2.4': dependencies: - '@vitest/pretty-format': 3.1.4 + '@vitest/pretty-format': 3.2.4 magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/spy@3.1.4': + '@vitest/spy@3.2.4': dependencies: - tinyspy: 3.0.2 + tinyspy: 4.0.3 - '@vitest/utils@3.1.4': + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 3.1.4 - loupe: 3.1.3 + '@vitest/pretty-format': 3.2.4 + loupe: 3.1.4 tinyrainbow: 2.0.0 '@webassemblyjs/ast@1.14.1': @@ -8281,6 +9180,11 @@ snapshots: dependencies: event-target-shim: 5.0.1 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-import-attributes@1.9.5(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -8428,6 +9332,8 @@ snapshots: async-function@1.0.0: {} + async@3.2.6: {} + atomic-sleep@1.0.0: {} available-typed-arrays@1.0.7: @@ -8442,6 +9348,20 @@ snapshots: binary-extensions@2.3.0: {} + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -8471,10 +9391,24 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bullmq@5.54.3: + dependencies: + cron-parser: 4.9.0 + ioredis: 5.6.1 + msgpackr: 1.11.4 + node-abort-controller: 3.1.1 + semver: 7.7.2 + tslib: 2.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + busboy@1.6.0: dependencies: streamsearch: 1.1.0 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -8571,14 +9505,28 @@ snapshots: client-only@0.0.1: {} + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} - cmdk@1.1.1(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + cluster-key-slot@1.1.2: {} + + cmdk@1.1.1(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-dialog': 1.1.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-id': 1.1.1(@types/react@19.1.6)(react@19.1.0) - '@radix-ui/react-primitive': 2.1.3(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -8619,14 +9567,50 @@ snapshots: concat-map@0.0.1: {} + concurrently@9.1.2: + dependencies: + chalk: 4.1.2 + lodash: 4.17.21 + rxjs: 7.8.2 + shell-quote: 1.8.3 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + convert-source-map@2.0.0: {} + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + cookie@1.0.2: {} copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 + copyfiles@2.4.1: + dependencies: + glob: 7.2.3 + minimatch: 3.1.2 + mkdirp: 1.0.4 + noms: 0.0.0 + through2: 2.0.5 + untildify: 4.0.0 + yargs: 16.2.0 + + core-util-is@1.0.3: {} + + cron-parser@4.9.0: + dependencies: + luxon: 3.6.1 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -8753,6 +9737,10 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + denque@2.1.0: {} + + depd@2.0.0: {} + dequal@2.0.3: {} detect-libc@2.0.4: {} @@ -8805,6 +9793,15 @@ snapshots: pg: 8.15.6 postgres: 3.4.7 + drizzle-orm@0.44.2(@electric-sql/pglite@0.3.0)(@libsql/client-wasm@0.15.5)(@opentelemetry/api@1.9.0)(@types/pg@8.15.2)(pg@8.15.6)(postgres@3.4.7): + optionalDependencies: + '@electric-sql/pglite': 0.3.0 + '@libsql/client-wasm': 0.15.5 + '@opentelemetry/api': 1.9.0 + '@types/pg': 8.15.2 + pg: 8.15.6 + postgres: 3.4.7 + drizzle-zod@0.7.1(drizzle-orm@0.43.1(@electric-sql/pglite@0.3.0)(@libsql/client-wasm@0.15.5)(@opentelemetry/api@1.9.0)(@types/pg@8.15.2)(pg@8.15.6)(postgres@3.4.7))(zod@3.25.41): dependencies: drizzle-orm: 0.43.1(@electric-sql/pglite@0.3.0)(@libsql/client-wasm@0.15.5)(@opentelemetry/api@1.9.0)(@types/pg@8.15.2)(pg@8.15.6)(postgres@3.4.7) @@ -8820,6 +9817,8 @@ snapshots: eastasianwidth@0.2.0: {} + easy-stack@1.0.1: {} + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 @@ -8831,6 +9830,12 @@ snapshots: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.2 + electron-to-chromium@1.5.161: {} emoji-regex@10.4.0: {} @@ -8839,6 +9844,8 @@ snapshots: emoji-regex@9.2.2: {} + encodeurl@2.0.0: {} + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -9025,6 +10032,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} eslint-config-prettier@10.1.5(eslint@9.29.0(jiti@2.4.2)): @@ -9145,10 +10154,17 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.7 + '@types/estree': 1.0.8 esutils@2.0.3: {} + etag@1.8.1: {} + + event-pubsub@5.0.3: + dependencies: + copyfiles: 2.4.1 + strong-type: 0.1.6 + event-target-shim@5.0.1: {} eventemitter3@4.0.7: {} @@ -9169,8 +10185,55 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + expect-type@1.2.1: {} + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: {} fast-copy@3.0.2: {} @@ -9217,14 +10280,33 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -9253,6 +10335,10 @@ snapshots: forwarded-parse@2.1.2: {} + forwarded@0.2.0: {} + + fresh@2.0.0: {} + fs.realpath@1.0.0: {} fsevents@2.3.2: @@ -9278,6 +10364,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-east-asian-width@1.3.0: {} get-intrinsic@1.3.0: @@ -9302,6 +10390,11 @@ snapshots: get-stream@6.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 @@ -9322,15 +10415,6 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - glob@11.0.2: dependencies: foreground-child: 3.3.1 @@ -9340,6 +10424,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.0 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + glob@9.3.5: dependencies: fs.realpath: 1.0.0 @@ -9438,6 +10531,14 @@ snapshots: html-url-attributes@3.0.1: {} + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.3 @@ -9461,6 +10562,8 @@ snapshots: human-signals@2.1.0: {} + human-signals@8.0.1: {} + husky@9.1.7: {} hyphenate-style-name@1.1.0: {} @@ -9491,6 +10594,13 @@ snapshots: indent-string@4.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + inline-style-parser@0.2.4: {} inline-style-prefixer@7.0.1: @@ -9512,6 +10622,22 @@ snapshots: '@formatjs/icu-messageformat-parser': 2.11.2 tslib: 2.8.1 + ioredis@5.6.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 + + ipaddr.js@1.9.1: {} + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -9616,6 +10742,8 @@ snapshots: is-potential-custom-element-name@1.0.1: {} + is-promise@4.0.0: {} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.7 @@ -9635,6 +10763,8 @@ snapshots: is-stream@2.0.1: {} + is-stream@4.0.1: {} + is-string@1.1.1: dependencies: call-bound: 1.0.4 @@ -9650,6 +10780,8 @@ snapshots: dependencies: which-typed-array: 1.1.19 + is-unicode-supported@2.1.0: {} + is-weakmap@2.0.2: {} is-weakref@1.1.1: @@ -9661,33 +10793,16 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + isarray@0.0.1: {} + + isarray@1.0.0: {} + isarray@2.0.5: {} isexe@2.0.0: {} isexe@3.1.1: {} - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.1 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -9697,16 +10812,17 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jackspeak@4.1.1: dependencies: '@isaacs/cliui': 8.0.2 + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + jest-worker@27.5.1: dependencies: '@types/node': 22.15.32 @@ -9724,8 +10840,16 @@ snapshots: js-cookie@3.0.5: {} + js-message@1.0.7: {} + + js-queue@2.0.2: + dependencies: + easy-stack: 1.0.1 + js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -9896,8 +11020,12 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.defaults@4.2.0: {} + lodash.includes@4.3.0: {} + lodash.isarguments@3.1.0: {} + lodash.isboolean@3.0.3: {} lodash.isinteger@4.0.4: {} @@ -9930,6 +11058,8 @@ snapshots: loupe@3.1.3: {} + loupe@3.1.4: {} + lower-case@2.0.2: dependencies: tslib: 2.8.1 @@ -9950,6 +11080,8 @@ snapshots: dependencies: react: 19.1.0 + luxon@3.6.1: {} + lz-string@1.5.0: {} magic-string@0.30.17: @@ -9960,16 +11092,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - magicast@0.3.5: - dependencies: - '@babel/parser': 7.27.3 - '@babel/types': 7.27.3 - source-map-js: 1.2.1 - - make-dir@4.0.0: - dependencies: - semver: 7.7.2 - map-obj@1.0.1: {} map-obj@4.3.0: {} @@ -10067,6 +11189,8 @@ snapshots: mdn-data@2.0.14: {} + media-typer@1.1.0: {} + meow@9.0.0: dependencies: '@types/minimist': 1.2.5 @@ -10082,6 +11206,8 @@ snapshots: type-fest: 0.18.1 yargs-parser: 20.2.9 + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -10226,10 +11352,16 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -10244,6 +11376,10 @@ snapshots: dependencies: brace-expansion: 1.1.11 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + minimatch@8.0.4: dependencies: brace-expansion: 2.0.1 @@ -10268,6 +11404,8 @@ snapshots: dependencies: minipass: 7.1.2 + mkdirp@1.0.4: {} + mkdirp@3.0.1: {} module-details-from-path@1.0.4: {} @@ -10276,6 +11414,22 @@ 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.4: + optionalDependencies: + msgpackr-extract: 3.0.3 + nano-css@5.6.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -10354,12 +11508,31 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 + node-abort-controller@3.1.1: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.0.4 + optional: true + + node-ipc@12.0.0: + dependencies: + event-pubsub: 5.0.3 + js-message: 1.0.7 + js-queue: 2.0.2 + strong-type: 1.1.0 + node-releases@2.0.19: {} + noms@0.0.0: + dependencies: + inherits: 2.0.4 + readable-stream: 1.0.34 + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 @@ -10380,6 +11553,11 @@ snapshots: dependencies: path-key: 3.1.1 + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + nwsapi@2.2.20: {} object-assign@4.1.1: {} @@ -10424,6 +11602,10 @@ snapshots: on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -10469,8 +11651,14 @@ snapshots: dependencies: p-limit: 3.1.0 + p-timeout@6.1.4: {} + p-try@2.2.0: {} + p-wait-for@5.0.2: + dependencies: + p-timeout: 6.1.4 + package-json-from-dist@1.0.1: {} parent-module@1.0.1: @@ -10494,14 +11682,22 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@4.0.0: {} + parse5@7.3.0: dependencies: entities: 6.0.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} + path-key@4.0.0: {} + path-parse@1.0.7: {} path-scurry@1.11.1: @@ -10514,6 +11710,8 @@ snapshots: lru-cache: 11.1.0 minipass: 7.1.2 + path-to-regexp@8.2.0: {} + pathe@2.0.3: {} pathval@2.0.0: {} @@ -10679,6 +11877,12 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + + process-nextick-args@2.0.1: {} + process-warning@5.0.0: {} process@0.11.10: {} @@ -10693,6 +11897,11 @@ snapshots: property-information@7.1.0: {} + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + proxy-from-env@1.1.0: {} pump@3.0.2: @@ -10716,6 +11925,15 @@ snapshots: dependencies: safe-buffer: 5.2.1 + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + react-dom@19.1.0(react@19.1.0): dependencies: react: 19.1.0 @@ -10834,6 +12052,23 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + readable-stream@1.0.34: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@4.7.0: dependencies: abort-controller: 3.0.0 @@ -10870,6 +12105,16 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + redis-errors@1.2.0: {} + + redis-info@3.1.0: + dependencies: + lodash: 4.17.21 + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -10907,6 +12152,8 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + require-directory@2.1.1: {} + require-from-string@2.0.2: {} require-in-the-middle@7.5.2: @@ -11006,6 +12253,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.41.1 fsevents: 2.3.3 + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + rrweb-cssom@0.8.0: {} rtl-css-js@1.16.1: @@ -11016,6 +12273,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -11024,6 +12285,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-push-apply@1.0.0: @@ -11064,6 +12327,22 @@ snapshots: semver@7.7.2: {} + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + serialize-error@8.1.0: dependencies: type-fest: 0.20.2 @@ -11072,6 +12351,15 @@ snapshots: dependencies: randombytes: 2.1.0 + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + server-only@0.0.1: {} set-function-length@1.2.2: @@ -11098,6 +12386,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} + sharp@0.34.2: dependencies: color: 4.2.3 @@ -11133,6 +12423,8 @@ snapshots: shebang-regex@3.0.0: {} + shell-quote@1.8.3: {} + shimmer@1.2.1: {} side-channel-list@1.0.0: @@ -11264,6 +12556,12 @@ snapshots: dependencies: type-fest: 0.7.1 + standard-as-callback@2.1.0: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + std-env@3.9.0: {} stop-iteration-iterator@1.1.0: @@ -11337,6 +12635,12 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@0.10.31: {} + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -11356,18 +12660,28 @@ snapshots: strip-final-newline@2.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 strip-json-comments@3.1.1: {} + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + stripe@18.2.0(@types/node@22.15.27): dependencies: qs: 6.14.0 optionalDependencies: '@types/node': 22.15.27 + strong-type@0.1.6: {} + + strong-type@1.1.0: {} + style-to-js@1.1.16: dependencies: style-to-object: 1.0.8 @@ -11440,18 +12754,17 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 - test-exclude@7.0.1: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 10.4.5 - minimatch: 9.0.5 - thread-stream@3.1.0: dependencies: real-require: 0.2.0 throttle-debounce@3.0.1: {} + through2@2.0.5: + dependencies: + readable-stream: 2.3.8 + xtend: 4.0.2 + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} @@ -11463,11 +12776,11 @@ snapshots: fdir: 6.4.5(picomatch@4.0.2) picomatch: 4.0.2 - tinypool@1.0.2: {} + tinypool@1.1.1: {} tinyrainbow@2.0.0: {} - tinyspy@3.0.2: {} + tinyspy@4.0.3: {} tldts-core@6.1.86: {} @@ -11481,6 +12794,8 @@ snapshots: toggle-selection@1.0.6: {} + toidentifier@1.0.1: {} + totalist@3.0.1: {} tough-cookie@5.1.2: @@ -11493,6 +12808,8 @@ snapshots: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + trim-lines@3.0.1: {} trim-newlines@3.0.1: {} @@ -11561,6 +12878,12 @@ snapshots: type-fest@4.41.0: {} + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -11615,6 +12938,8 @@ snapshots: undici-types@6.21.0: {} + unicorn-magic@0.3.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -11648,6 +12973,8 @@ snapshots: unist-util-is: 6.0.0 unist-util-visit-parents: 6.0.1 + unpipe@1.0.0: {} + unplugin@1.0.1: dependencies: acorn: 8.15.0 @@ -11655,6 +12982,8 @@ snapshots: webpack-sources: 3.3.0 webpack-virtual-modules: 0.5.0 + untildify@4.0.0: {} + update-browserslist-db@1.1.3(browserslist@4.25.0): dependencies: browserslist: 4.25.0 @@ -11691,6 +13020,8 @@ snapshots: dependencies: react: 19.1.0 + util-deprecate@1.0.2: {} + uuid@11.1.0: {} uuid@9.0.1: {} @@ -11700,9 +13031,11 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vaul@1.1.2(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + vary@1.1.2: {} + + vaul@1.1.2(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@radix-ui/react-dialog': 1.1.14(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@19.1.6(@types/react@19.1.6))(@types/react@19.1.6)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: 19.1.0 react-dom: 19.1.0(react@19.1.0) transitivePeerDependencies: @@ -11736,7 +13069,28 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-node@3.1.4(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + vite-node@3.2.4(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-node@3.2.4(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1 @@ -11757,6 +13111,27 @@ snapshots: - tsx - yaml + vite-node@3.2.4(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.3.5(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)): dependencies: debug: 4.4.1 @@ -11768,6 +13143,23 @@ snapshots: - supports-color - typescript + vite@6.3.5(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + esbuild: 0.25.5 + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.4 + rollup: 4.41.1 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 20.19.1 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 + terser: 5.43.1 + tsx: 4.20.3 + yaml: 2.8.0 + vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): dependencies: esbuild: 0.25.5 @@ -11785,28 +13177,90 @@ snapshots: tsx: 4.20.3 yaml: 2.8.0 - vitest@3.1.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + vite@6.3.5(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): dependencies: - '@vitest/expect': 3.1.4 - '@vitest/mocker': 3.1.4(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) - '@vitest/pretty-format': 3.1.4 - '@vitest/runner': 3.1.4 - '@vitest/snapshot': 3.1.4 - '@vitest/spy': 3.1.4 - '@vitest/utils': 3.1.4 + esbuild: 0.25.5 + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.4 + rollup: 4.41.1 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.15.32 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 + terser: 5.43.1 + tsx: 4.20.3 + yaml: 2.8.0 + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@20.19.1)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 chai: 5.2.0 debug: 4.4.1 expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 + picomatch: 4.0.2 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 - tinypool: 1.0.2 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.3.5(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@20.19.1)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 20.19.1 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.27)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.1 + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 tinyrainbow: 2.0.0 vite: 6.3.5(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) - vite-node: 3.1.4(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@22.15.27)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -11826,6 +13280,49 @@ snapshots: - tsx - yaml + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.32)(jiti@2.4.2)(jsdom@26.1.0)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.0 + debug: 4.4.1 + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.3.5(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@22.15.32)(jiti@2.4.2)(lightningcss@1.30.1)(terser@5.43.1)(tsx@4.20.3)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 22.15.32 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -11997,6 +13494,8 @@ snapshots: xtend@4.0.2: {} + y18n@5.0.8: {} + yallist@3.1.1: {} yallist@4.0.0: {} @@ -12007,8 +13506,32 @@ snapshots: yargs-parser@20.2.9: {} + yargs-parser@21.1.1: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} + yoctocolors@2.1.1: {} + zod@3.25.41: {} zwitch@2.0.4: {}