From 105302602acd6708e599704d3ba5b55139db7d2d Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:36:45 +0000 Subject: [PATCH 01/34] refactor(backend): rewrite using `hono` framework --- packages/backend/.env.example | 14 +- packages/backend/.gitignore | 73 +- packages/backend/.oxfmtrc.json | 4 + packages/backend/.oxlintrc.json | 15 + packages/backend/.prettierrc | 4 - packages/backend/README.md | 3 - packages/backend/eslint.config.ts | 67 - packages/backend/jest.config.ts | 42 - packages/backend/nest-cli.json | 8 - packages/backend/package.json | 114 +- packages/backend/prisma.config.ts | 10 +- .../20260120001931_init/migration.sql | 25 - .../20260215181900_user_model/migration.sql | 8 - .../migration.sql | 2 - .../migration.sql | 26 - .../migration.sql | 8 - .../migration.sql | 64 - .../migration.sql | 22 - .../migration.sql | 12 - .../20261007133401_init/migration.sql | 56 + packages/backend/prisma/schema.prisma | 25 +- packages/backend/src/app.module.ts | 14 - packages/backend/src/app.ts | 51 + .../backend/src/auth/auth.controller.spec.ts | 35 - packages/backend/src/auth/auth.controller.ts | 59 - packages/backend/src/auth/auth.module.ts | 29 - .../backend/src/auth/auth.service.spec.ts | 212 - packages/backend/src/auth/auth.service.ts | 101 - packages/backend/src/auth/dto/login.dto.ts | 5 - packages/backend/src/auth/dto/register.dto.ts | 5 - .../auth/entities/user-token-data.entity.ts | 5 - .../src/auth/entities/user-token.entity.ts | 5 - packages/backend/src/auth/guards/jwt.guard.ts | 34 - .../backend/src/auth/schemas/login.schema.ts | 12 - .../src/auth/schemas/register.schema.ts | 12 - .../auth/schemas/user-token-data.schema.ts | 7 - .../src/auth/schemas/user-token.schema.ts | 9 - .../backend/src/bot/bot.controller.spec.ts | 36 - packages/backend/src/bot/bot.controller.ts | 122 - packages/backend/src/bot/bot.module.ts | 17 - packages/backend/src/bot/bot.service.spec.ts | 609 - packages/backend/src/bot/bot.service.ts | 365 - packages/backend/src/bot/bot.utils.ts | 17 - .../backend/src/bot/dto/create-bot.dto.ts | 28 - packages/backend/src/bot/dto/get-bot.dto.ts | 63 - .../src/bot/dto/get-recommended-shards.dto.ts | 12 - .../backend/src/bot/dto/update-bot.dto.ts | 27 - .../src/clusters/clusters.controller.spec.ts | 36 - .../src/clusters/clusters.controller.ts | 320 - .../backend/src/clusters/clusters.module.ts | 16 - .../src/clusters/clusters.service.spec.ts | 37 - .../backend/src/clusters/clusters.service.ts | 328 - .../dto/cluster-bulk-action-result.dto.ts | 22 - .../src/clusters/dto/cluster-id-param.dto.ts | 12 - .../clusters/dto/get-aggregate-stats.dto.ts | 12 - .../src/clusters/dto/get-cluster-logs.dto.ts | 38 - .../src/clusters/dto/get-cluster-stats.dto.ts | 57 - .../src/clusters/dto/get-cluster.dto.ts | 18 - .../clusters/dto/sse-interval-query.dto.ts | 10 - .../backend/src/common/dto/api-error.dto.ts | 33 - .../filters/all-exceptions.filter.spec.ts | 78 - .../common/filters/all-exceptions.filter.ts | 112 - .../src/controllers/auth.controller.ts | 66 + .../backend/src/controllers/bot.controller.ts | 287 + .../src/controllers/cluster.controller.ts | 325 + .../backend/src/discord/discord.module.ts | 9 - .../src/discord/discord.service.spec.ts | 116 - .../backend/src/discord/discord.service.ts | 94 - packages/backend/src/docker/docker.module.ts | 26 - .../backend/src/docker/docker.service.spec.ts | 205 - packages/backend/src/docker/docker.service.ts | 496 - .../backend/src/exceptions/http-exceptions.ts | 39 + packages/backend/src/index.dto.ts | 22 - packages/backend/src/index.ts | 12 + packages/backend/src/lib/database.ts | 8 + packages/backend/src/lib/env.ts | 8 + packages/backend/src/logger/logger.module.ts | 9 - .../backend/src/logger/logger.service.spec.ts | 20 - packages/backend/src/logger/logger.service.ts | 47 - .../backend/src/logger/logging.interceptor.ts | 39 - packages/backend/src/main.ts | 78 - .../backend/src/middlewares/auth.middlware.ts | 28 + packages/backend/src/prisma/prisma.module.ts | 21 - .../backend/src/prisma/prisma.service.spec.ts | 26 - packages/backend/src/prisma/prisma.service.ts | 23 - .../src/repositories/bot.repository.ts | 108 + .../src/repositories/cluster.repository.ts | 92 + .../container-image.repository.ts | 66 + .../src/repositories/user.repository.ts | 17 + packages/backend/src/schemas/auth.schema.ts | 12 + packages/backend/src/schemas/bot.schema.ts | 73 + .../backend/src/schemas/cluster.schema.ts | 111 + .../backend/src/schemas/discord.schema.ts | 15 + packages/backend/src/services/auth.service.ts | 29 + packages/backend/src/services/bot.service.ts | 171 + .../backend/src/services/cluster.service.ts | 214 + .../backend/src/services/discord.service.ts | 67 + .../backend/src/services/docker.service.ts | 116 + packages/backend/tests/.gitkeep | 0 packages/backend/tsconfig.build.json | 4 - packages/backend/tsconfig.json | 51 +- packages/frontend/.oxlintrc.json | 3 +- packages/frontend/README.md | 38 - packages/prisma-client/.gitignore | 3 - packages/prisma-client/package.json | 21 - pnpm-lock.yaml | 11523 +++------------- 106 files changed, 4181 insertions(+), 14179 deletions(-) create mode 100644 packages/backend/.oxfmtrc.json create mode 100644 packages/backend/.oxlintrc.json delete mode 100644 packages/backend/.prettierrc delete mode 100644 packages/backend/README.md delete mode 100644 packages/backend/eslint.config.ts delete mode 100644 packages/backend/jest.config.ts delete mode 100644 packages/backend/nest-cli.json delete mode 100644 packages/backend/prisma/migrations/20260120001931_init/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260215181900_user_model/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260220213440_adding_cluster_state/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260223052940_setup_docker_image/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260223053032_setup_bot_token/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260223054709_bot_table_simplification/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260403075738_docker_image_child_of_bot/migration.sql delete mode 100644 packages/backend/prisma/migrations/20260513054314_use_numeric_cluster_ids/migration.sql create mode 100644 packages/backend/prisma/migrations/20261007133401_init/migration.sql delete mode 100644 packages/backend/src/app.module.ts create mode 100644 packages/backend/src/app.ts delete mode 100644 packages/backend/src/auth/auth.controller.spec.ts delete mode 100644 packages/backend/src/auth/auth.controller.ts delete mode 100644 packages/backend/src/auth/auth.module.ts delete mode 100644 packages/backend/src/auth/auth.service.spec.ts delete mode 100644 packages/backend/src/auth/auth.service.ts delete mode 100644 packages/backend/src/auth/dto/login.dto.ts delete mode 100644 packages/backend/src/auth/dto/register.dto.ts delete mode 100644 packages/backend/src/auth/entities/user-token-data.entity.ts delete mode 100644 packages/backend/src/auth/entities/user-token.entity.ts delete mode 100644 packages/backend/src/auth/guards/jwt.guard.ts delete mode 100644 packages/backend/src/auth/schemas/login.schema.ts delete mode 100644 packages/backend/src/auth/schemas/register.schema.ts delete mode 100644 packages/backend/src/auth/schemas/user-token-data.schema.ts delete mode 100644 packages/backend/src/auth/schemas/user-token.schema.ts delete mode 100644 packages/backend/src/bot/bot.controller.spec.ts delete mode 100644 packages/backend/src/bot/bot.controller.ts delete mode 100644 packages/backend/src/bot/bot.module.ts delete mode 100644 packages/backend/src/bot/bot.service.spec.ts delete mode 100644 packages/backend/src/bot/bot.service.ts delete mode 100644 packages/backend/src/bot/bot.utils.ts delete mode 100644 packages/backend/src/bot/dto/create-bot.dto.ts delete mode 100644 packages/backend/src/bot/dto/get-bot.dto.ts delete mode 100644 packages/backend/src/bot/dto/get-recommended-shards.dto.ts delete mode 100644 packages/backend/src/bot/dto/update-bot.dto.ts delete mode 100644 packages/backend/src/clusters/clusters.controller.spec.ts delete mode 100644 packages/backend/src/clusters/clusters.controller.ts delete mode 100644 packages/backend/src/clusters/clusters.module.ts delete mode 100644 packages/backend/src/clusters/clusters.service.spec.ts delete mode 100644 packages/backend/src/clusters/clusters.service.ts delete mode 100644 packages/backend/src/clusters/dto/cluster-bulk-action-result.dto.ts delete mode 100644 packages/backend/src/clusters/dto/cluster-id-param.dto.ts delete mode 100644 packages/backend/src/clusters/dto/get-aggregate-stats.dto.ts delete mode 100644 packages/backend/src/clusters/dto/get-cluster-logs.dto.ts delete mode 100644 packages/backend/src/clusters/dto/get-cluster-stats.dto.ts delete mode 100644 packages/backend/src/clusters/dto/get-cluster.dto.ts delete mode 100644 packages/backend/src/clusters/dto/sse-interval-query.dto.ts delete mode 100644 packages/backend/src/common/dto/api-error.dto.ts delete mode 100644 packages/backend/src/common/filters/all-exceptions.filter.spec.ts delete mode 100644 packages/backend/src/common/filters/all-exceptions.filter.ts create mode 100644 packages/backend/src/controllers/auth.controller.ts create mode 100644 packages/backend/src/controllers/bot.controller.ts create mode 100644 packages/backend/src/controllers/cluster.controller.ts delete mode 100644 packages/backend/src/discord/discord.module.ts delete mode 100644 packages/backend/src/discord/discord.service.spec.ts delete mode 100644 packages/backend/src/discord/discord.service.ts delete mode 100644 packages/backend/src/docker/docker.module.ts delete mode 100644 packages/backend/src/docker/docker.service.spec.ts delete mode 100644 packages/backend/src/docker/docker.service.ts create mode 100644 packages/backend/src/exceptions/http-exceptions.ts delete mode 100644 packages/backend/src/index.dto.ts create mode 100644 packages/backend/src/index.ts create mode 100644 packages/backend/src/lib/database.ts create mode 100644 packages/backend/src/lib/env.ts delete mode 100644 packages/backend/src/logger/logger.module.ts delete mode 100644 packages/backend/src/logger/logger.service.spec.ts delete mode 100644 packages/backend/src/logger/logger.service.ts delete mode 100644 packages/backend/src/logger/logging.interceptor.ts delete mode 100644 packages/backend/src/main.ts create mode 100644 packages/backend/src/middlewares/auth.middlware.ts delete mode 100644 packages/backend/src/prisma/prisma.module.ts delete mode 100644 packages/backend/src/prisma/prisma.service.spec.ts delete mode 100644 packages/backend/src/prisma/prisma.service.ts create mode 100644 packages/backend/src/repositories/bot.repository.ts create mode 100644 packages/backend/src/repositories/cluster.repository.ts create mode 100644 packages/backend/src/repositories/container-image.repository.ts create mode 100644 packages/backend/src/repositories/user.repository.ts create mode 100644 packages/backend/src/schemas/auth.schema.ts create mode 100644 packages/backend/src/schemas/bot.schema.ts create mode 100644 packages/backend/src/schemas/cluster.schema.ts create mode 100644 packages/backend/src/schemas/discord.schema.ts create mode 100644 packages/backend/src/services/auth.service.ts create mode 100644 packages/backend/src/services/bot.service.ts create mode 100644 packages/backend/src/services/cluster.service.ts create mode 100644 packages/backend/src/services/discord.service.ts create mode 100644 packages/backend/src/services/docker.service.ts create mode 100644 packages/backend/tests/.gitkeep delete mode 100644 packages/backend/tsconfig.build.json delete mode 100644 packages/frontend/README.md delete mode 100644 packages/prisma-client/.gitignore delete mode 100644 packages/prisma-client/package.json diff --git a/packages/backend/.env.example b/packages/backend/.env.example index 862319d..1411c3a 100644 --- a/packages/backend/.env.example +++ b/packages/backend/.env.example @@ -1,12 +1,4 @@ -DISCORD_BOT_TOKEN_ENV_NAME=DISCORD_BOT_TOKEN -TOTAL_SHARDS_ENV_NAME=TOTAL_SHARDS -SHARD_ID_LIST_ENV_NAME=SHARD_ID_LIST +DATABASE_URL="postgres://postgres:postgres@localhost:51214/template1?sslmode=disable&connection_limit=10&connect_timeout=0&max_idle_connection_lifetime=0&pool_timeout=0&socket_timeout=0" -DATABASE_URL=postgresql://pguser:pgpassword@hallmaster-postgres:5432/hallmaster - -# python3 -c "import random,string;print(''.join([random.choice(string.hexdigits[:16]) for _ in range(32)]))" -JWT_SECRET=XXX -JWT_EXPIRES_IN=12h # 15m, 1d, ... - -# This enables logging of all incoming requests -# LOG_REQUESTS=true +# openssl rand -base64 32 +SECRET="change_me" diff --git a/packages/backend/.gitignore b/packages/backend/.gitignore index 00a9233..87f64d8 100644 --- a/packages/backend/.gitignore +++ b/packages/backend/.gitignore @@ -1,58 +1,33 @@ -# compiled output -/dist -/node_modules -/build +# dev +.yarn/ +!.yarn/releases +.vscode/* +!.vscode/launch.json +!.vscode/*.code-snippets +.idea/workspace.xml +.idea/usage.statistics.xml +.idea/shelf + +# deps +node_modules/ + +# env +.env +.env.production -# Logs -logs +# logs +logs/ *.log npm-debug.log* -pnpm-debug.log* yarn-debug.log* yarn-error.log* +pnpm-debug.log* lerna-debug.log* -# OS +# misc .DS_Store +dist/ +/.alchemy +/.prisma-composer -# Tests -/coverage -/.nyc_output - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# temp directory -.temp -.tmp - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -/generated +prisma/generated/ diff --git a/packages/backend/.oxfmtrc.json b/packages/backend/.oxfmtrc.json new file mode 100644 index 0000000..55c15df --- /dev/null +++ b/packages/backend/.oxfmtrc.json @@ -0,0 +1,4 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": [] +} diff --git a/packages/backend/.oxlintrc.json b/packages/backend/.oxlintrc.json new file mode 100644 index 0000000..e73870c --- /dev/null +++ b/packages/backend/.oxlintrc.json @@ -0,0 +1,15 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["typescript", "unicorn", "oxc"], + "categories": { + "correctness": "error" + }, + "rules": {}, + "env": { + "builtin": true + }, + "options": { + "typeAware": true, + "typeCheck": true + } +} diff --git a/packages/backend/.prettierrc b/packages/backend/.prettierrc deleted file mode 100644 index 0981b7c..0000000 --- a/packages/backend/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "printWidth": 120 -} diff --git a/packages/backend/README.md b/packages/backend/README.md deleted file mode 100644 index 272f498..0000000 --- a/packages/backend/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Hallmaster backend - -NestJS application powered by Fastify for the Hallmaster backend. diff --git a/packages/backend/eslint.config.ts b/packages/backend/eslint.config.ts deleted file mode 100644 index a8bed08..0000000 --- a/packages/backend/eslint.config.ts +++ /dev/null @@ -1,67 +0,0 @@ -// @ts-check -import { defineConfig } from 'eslint/config'; -import eslint from '@eslint/js'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; -import eslintPluginJest from 'eslint-plugin-jest'; -import eslintPluginImport from 'eslint-plugin-import'; -import eslintPluginPrettier from 'eslint-plugin-prettier/recommended'; - -export default defineConfig( - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginImport.flatConfigs.recommended, - eslintPluginImport.flatConfigs.typescript, - eslintPluginPrettier, - eslintPluginJest.configs['flat/recommended'], - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - ecmaVersion: 5, - sourceType: 'module', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - settings: { - 'import/resolver': { - typescript: { - project: './tsconfig.json', - }, - }, - }, - }, - { - rules: { - '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-unsafe-argument': 'error', - '@typescript-eslint/consistent-type-imports': 'error', - '@typescript-eslint/no-unused-vars': [ - 'error', - { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, - ], - 'no-console': ['error', { allow: ['warn', 'error'] }], - 'import/order': [ - 'error', - { - groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], - 'newlines-between': 'always', - alphabetize: { order: 'asc', caseInsensitive: true }, - }, - ], - 'import/no-unresolved': 'off', - }, - }, - { - files: ['**/*.spec.ts'], - rules: { - '@typescript-eslint/unbound-method': 'off', - 'jest/unbound-method': 'error', - }, - }, -); diff --git a/packages/backend/jest.config.ts b/packages/backend/jest.config.ts deleted file mode 100644 index 86a40da..0000000 --- a/packages/backend/jest.config.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { JestConfigWithTsJest } from 'ts-jest'; - -const config: JestConfigWithTsJest = { - // Use the ts-jest preset for ESM - preset: 'ts-jest/presets/default-esm', - - // The test environment that will be used for testing - testEnvironment: 'node', - - // A list of paths to directories that Jest should use to search for files in - roots: ['/src'], - - // The glob patterns Jest uses to detect test files - testRegex: '.*\\.spec\\.ts$', - - // A map from regular expressions to paths to transformers - transform: { - '^.+\\.ts?$': [ - 'ts-jest', - { - useESM: true, - }, - ], - // '^.+\\.[tj]s$': 'ts-jest' // This is handled by the preset - }, - - // Helps Jest resolve TypeScript paths - moduleNameMapper: { - // This mapping is necessary to handle the .js extension in ESM imports - '^(\\.{1,2}/.*)\\.js$': '$1', - - // If you use path aliases in tsconfig.json, map them here - // Example: '@App/(.*)': '/src/$1' - }, - - // An array of file extensions your modules use - moduleFileExtensions: ['ts', 'js', 'json', 'node'], - - clearMocks: true, -}; - -export default config; diff --git a/packages/backend/nest-cli.json b/packages/backend/nest-cli.json deleted file mode 100644 index f9aa683..0000000 --- a/packages/backend/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/packages/backend/package.json b/packages/backend/package.json index 083a654..1f860ab 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,93 +1,41 @@ { "name": "@hallmaster/backend", - "version": "0.0.1", - "description": "Hallmaster backend", - "private": true, - "license": "UNLICENSED", "type": "module", - "exports": { - "./dto": "./src/index.dto.ts" - }, "scripts": { - "build": "nest build", - "start": "dotenv -e .env -- nest start", - "start:dev": "dotenv -e .env -- nest start --watch", - "start:debug": "dotenv -e .env -- nest start --debug --watch", - "start:prod": "prisma migrate deploy && node dist/src/main", - "lint": "eslint \"src/**/*.ts\"", - "lint:fix": "eslint --fix \"src/**/*.ts\"", - "format": "prettier --check src/", - "format:fix": "prettier --write src/", - "pretest": "pnpm build", - "test": "NODE_OPTIONS=--experimental-vm-modules jest", - "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch", - "test:cov": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", - "test:debug": "NODE_OPTIONS=--experimental-vm-modules node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "prisma:reset": "dotenv -e .env -- prisma migrate reset", - "prisma:migrate": "dotenv -e .env -- prisma migrate dev", - "prisma:generate": "DATABASE_URL=postgres://null:null@null:5432/null prisma generate", - "prisma:deploy": "dotenv -e .env -- prisma migrate deploy", - "prisma:studio": "dotenv -e .env -- prisma studio", - "prisma:format": "dotenv -e .env -- prisma format" + "dev": "tsx watch src/index.ts", + "build": "tsc", + "start": "node dist/index.js", + "lint": "oxlint", + "lint:fix": "oxlint --fix", + "format": "oxfmt", + "format:check": "oxfmt --check", + "prisma:dev": "prisma dev", + "prisma:migrate": "prisma migrate dev", + "prisma:reset": "prisma migrate reset", + "prisma:generate": "prisma generate" }, "dependencies": { - "@fastify/compress": "8.3.1", - "@hallmaster/docker.js": "0.0.28", - "@hallmaster/prisma-client": "workspace:*", - "@nestjs/common": "11.1.14", - "@nestjs/config": "4.0.3", - "@nestjs/core": "11.1.14", - "@nestjs/jwt": "11.0.2", - "@nestjs/platform-fastify": "11.1.14", - "@nestjs/swagger": "11.2.6", - "@prisma/adapter-pg": "7.4.1", - "@prisma/client": "7.4.1", - "argon2": "0.44.0", - "fastify": "5.7.4", - "nest-winston": "1.10.2", - "nestjs-zod": "5.1.1", - "pg": "8.18.0", - "prisma": "7.4.1", - "reflect-metadata": "0.2.2", - "rxjs": "7.8.2", - "swagger-themes": "1.4.3", - "winston": "3.19.0", - "zod": "4.3.6" + "@hallmaster/docker.js": "^0.0.28", + "@hono/node-server": "^2.1.1", + "@hono/standard-validator": "^0.4.0", + "@prisma/adapter-pg": "^7.10.0", + "@prisma/client": "^7.4.1", + "@scalar/hono-api-reference": "^0.12.6", + "argon2": "^0.45.1", + "dotenv": "^18.0.4", + "hono": "^4.13.9", + "hono-openapi": "^1.3.3", + "pg": "^8.23.0", + "zod": "^4.6.5" }, "devDependencies": { - "@eslint/eslintrc": "3.3.3", - "@eslint/js": "9.39.3", - "@jest/globals": "30.2.0", - "@nestjs/cli": "11.0.16", - "@nestjs/schematics": "11.0.9", - "@nestjs/testing": "11.1.14", - "@swc/cli": "0.6.0", - "@swc/core": "1.15.11", - "@types/jest": "30.0.0", - "@types/mocha": "^10.0.10", - "@types/node": "22.19.11", - "@types/pg": "8.16.0", - "@types/supertest": "6.0.3", - "dotenv": "17.3.1", - "dotenv-cli": "10.0.0", - "eslint": "9.39.3", - "eslint-config-prettier": "10.1.8", - "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-jest": "29.15.0", - "eslint-plugin-prettier": "5.5.5", - "globals": "16.5.0", - "jest": "29.7.0", - "jest-esm-transformer": "1.0.0", - "jest-mock-extended": "4.0.0", - "prettier": "3.8.1", - "source-map-support": "0.5.21", - "supertest": "7.2.2", - "ts-jest": "29.4.6", - "ts-loader": "9.5.4", - "ts-node": "10.9.2", - "tsconfig-paths": "4.2.0", - "typescript": "5.9.3", - "typescript-eslint": "8.56.0" + "@types/node": "^26.1.1", + "@types/pg": "^8.23.1", + "oxfmt": "^0.70.0", + "oxlint": "^1.85.0", + "oxlint-tsgolint": "^7.0.2003", + "prisma": "^7.10.0", + "tsx": "^4.23.0", + "typescript": "^7.0.2" } } diff --git a/packages/backend/prisma.config.ts b/packages/backend/prisma.config.ts index 8b89c80..69edbe9 100644 --- a/packages/backend/prisma.config.ts +++ b/packages/backend/prisma.config.ts @@ -1,12 +1,12 @@ -import { defineConfig, env } from 'prisma/config'; +import "dotenv/config"; +import { defineConfig } from "prisma/config"; export default defineConfig({ - schema: 'prisma/schema.prisma', + schema: "prisma/schema.prisma", migrations: { - path: 'prisma/migrations', - seed: 'tsx prisma/seed.ts', + path: "prisma/migrations", }, datasource: { - url: env('DATABASE_URL'), + url: process.env["DATABASE_URL"], }, }); diff --git a/packages/backend/prisma/migrations/20260120001931_init/migration.sql b/packages/backend/prisma/migrations/20260120001931_init/migration.sql deleted file mode 100644 index 6cd9a3f..0000000 --- a/packages/backend/prisma/migrations/20260120001931_init/migration.sql +++ /dev/null @@ -1,25 +0,0 @@ --- CreateEnum -CREATE TYPE "ClusterStatus" AS ENUM ('STARTING', 'RUNNING', 'STOPPED', 'ERROR'); - --- CreateTable -CREATE TABLE "Bot" ( - "id" TEXT NOT NULL, - "clusterNumber" INTEGER NOT NULL, - "shards" INTEGER NOT NULL, - - CONSTRAINT "Bot_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Cluster" ( - "id" TEXT NOT NULL, - "botId" TEXT NOT NULL, - "containerId" TEXT, - "status" "ClusterStatus" NOT NULL, - "shardIds" INTEGER[], - - CONSTRAINT "Cluster_pkey" PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "Cluster" ADD CONSTRAINT "Cluster_botId_fkey" FOREIGN KEY ("botId") REFERENCES "Bot"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/backend/prisma/migrations/20260215181900_user_model/migration.sql b/packages/backend/prisma/migrations/20260215181900_user_model/migration.sql deleted file mode 100644 index ed994c0..0000000 --- a/packages/backend/prisma/migrations/20260215181900_user_model/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "username" TEXT NOT NULL, - "password" TEXT NOT NULL -); - --- CreateIndex -CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); diff --git a/packages/backend/prisma/migrations/20260220213440_adding_cluster_state/migration.sql b/packages/backend/prisma/migrations/20260220213440_adding_cluster_state/migration.sql deleted file mode 100644 index 18ab9fc..0000000 --- a/packages/backend/prisma/migrations/20260220213440_adding_cluster_state/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterEnum -ALTER TYPE "ClusterStatus" ADD VALUE 'UPDATING'; diff --git a/packages/backend/prisma/migrations/20260223052940_setup_docker_image/migration.sql b/packages/backend/prisma/migrations/20260223052940_setup_docker_image/migration.sql deleted file mode 100644 index 5dbea53..0000000 --- a/packages/backend/prisma/migrations/20260223052940_setup_docker_image/migration.sql +++ /dev/null @@ -1,26 +0,0 @@ -/* - Warnings: - - - Added the required column `dockerImageId` to the `Bot` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Bot" ADD COLUMN "dockerImageId" TEXT NOT NULL; - --- CreateTable -CREATE TABLE "DockerImage" ( - "id" TEXT NOT NULL, - "servername" TEXT NOT NULL DEFAULT 'host.docker.internal:5000', - "image" TEXT NOT NULL, - "tag" TEXT NOT NULL DEFAULT 'latest', - "username" TEXT, - "password" TEXT, - - CONSTRAINT "DockerImage_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "DockerImage_servername_key" ON "DockerImage"("servername"); - --- AddForeignKey -ALTER TABLE "Bot" ADD CONSTRAINT "Bot_dockerImageId_fkey" FOREIGN KEY ("dockerImageId") REFERENCES "DockerImage"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/packages/backend/prisma/migrations/20260223053032_setup_bot_token/migration.sql b/packages/backend/prisma/migrations/20260223053032_setup_bot_token/migration.sql deleted file mode 100644 index b3db11a..0000000 --- a/packages/backend/prisma/migrations/20260223053032_setup_bot_token/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* - Warnings: - - - Added the required column `token` to the `Bot` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Bot" ADD COLUMN "token" TEXT NOT NULL; diff --git a/packages/backend/prisma/migrations/20260223054709_bot_table_simplification/migration.sql b/packages/backend/prisma/migrations/20260223054709_bot_table_simplification/migration.sql deleted file mode 100644 index 3db66c3..0000000 --- a/packages/backend/prisma/migrations/20260223054709_bot_table_simplification/migration.sql +++ /dev/null @@ -1,64 +0,0 @@ -/* - Warnings: - - - You are about to drop the `Bot` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `Cluster` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `DockerImage` table. If the table is not empty, all the data it contains will be lost. - -*/ --- DropForeignKey -ALTER TABLE "Bot" DROP CONSTRAINT "Bot_dockerImageId_fkey"; - --- DropForeignKey -ALTER TABLE "Cluster" DROP CONSTRAINT "Cluster_botId_fkey"; - --- DropTable -DROP TABLE "Bot"; - --- DropTable -DROP TABLE "Cluster"; - --- DropTable -DROP TABLE "DockerImage"; - --- CreateTable -CREATE TABLE "bot" ( - "id" TEXT NOT NULL, - "token" TEXT NOT NULL, - "total_shards" INTEGER NOT NULL, - "docker_image_id" TEXT NOT NULL, - - CONSTRAINT "bot_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "docker_image" ( - "id" TEXT NOT NULL, - "server_name" TEXT NOT NULL DEFAULT 'host.docker.internal:5000', - "image" TEXT NOT NULL, - "tag" TEXT NOT NULL DEFAULT 'latest', - "username" TEXT, - "password" TEXT, - - CONSTRAINT "docker_image_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "cluster" ( - "id" TEXT NOT NULL, - "bot_id" TEXT NOT NULL, - "container_id" TEXT, - "status" "ClusterStatus" NOT NULL, - "shard_ids" INTEGER[], - - CONSTRAINT "cluster_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "docker_image_server_name_key" ON "docker_image"("server_name"); - --- AddForeignKey -ALTER TABLE "bot" ADD CONSTRAINT "bot_docker_image_id_fkey" FOREIGN KEY ("docker_image_id") REFERENCES "docker_image"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "cluster" ADD CONSTRAINT "cluster_bot_id_fkey" FOREIGN KEY ("bot_id") REFERENCES "bot"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/backend/prisma/migrations/20260403075738_docker_image_child_of_bot/migration.sql b/packages/backend/prisma/migrations/20260403075738_docker_image_child_of_bot/migration.sql deleted file mode 100644 index cf0d121..0000000 --- a/packages/backend/prisma/migrations/20260403075738_docker_image_child_of_bot/migration.sql +++ /dev/null @@ -1,22 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `docker_image_id` on the `bot` table. All the data in the column will be lost. - - A unique constraint covering the columns `[bot_id]` on the table `docker_image` will be added. If there are existing duplicate values, this will fail. - - Added the required column `bot_id` to the `docker_image` table without a default value. This is not possible if the table is not empty. - -*/ --- DropForeignKey -ALTER TABLE "bot" DROP CONSTRAINT "bot_docker_image_id_fkey"; - --- AlterTable -ALTER TABLE "bot" DROP COLUMN "docker_image_id"; - --- AlterTable -ALTER TABLE "docker_image" ADD COLUMN "bot_id" TEXT NOT NULL; - --- CreateIndex -CREATE UNIQUE INDEX "docker_image_bot_id_key" ON "docker_image"("bot_id"); - --- AddForeignKey -ALTER TABLE "docker_image" ADD CONSTRAINT "docker_image_bot_id_fkey" FOREIGN KEY ("bot_id") REFERENCES "bot"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/backend/prisma/migrations/20260513054314_use_numeric_cluster_ids/migration.sql b/packages/backend/prisma/migrations/20260513054314_use_numeric_cluster_ids/migration.sql deleted file mode 100644 index 7982974..0000000 --- a/packages/backend/prisma/migrations/20260513054314_use_numeric_cluster_ids/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* - Warnings: - - - The primary key for the `cluster` table will be changed. If it partially fails, the table could be left without primary key constraint. - - Changed the type of `id` on the `cluster` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. - -*/ --- AlterTable -ALTER TABLE "cluster" DROP CONSTRAINT "cluster_pkey", -DROP COLUMN "id", -ADD COLUMN "id" INTEGER NOT NULL, -ADD CONSTRAINT "cluster_pkey" PRIMARY KEY ("bot_id", "id"); diff --git a/packages/backend/prisma/migrations/20261007133401_init/migration.sql b/packages/backend/prisma/migrations/20261007133401_init/migration.sql new file mode 100644 index 0000000..af33f25 --- /dev/null +++ b/packages/backend/prisma/migrations/20261007133401_init/migration.sql @@ -0,0 +1,56 @@ +-- CreateEnum +CREATE TYPE "ClusterStatus" AS ENUM ('STARTING', 'RUNNING', 'STOPPED', 'UPDATING', 'ERROR'); + +-- CreateTable +CREATE TABLE "User" ( + "username" TEXT NOT NULL, + "password_hash" TEXT NOT NULL +); + +-- CreateTable +CREATE TABLE "bot" ( + "id" TEXT NOT NULL, + "token" TEXT NOT NULL, + "total_shards" INTEGER NOT NULL, + + CONSTRAINT "bot_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "container_image" ( + "id" TEXT NOT NULL, + "server_name" TEXT NOT NULL DEFAULT 'host.docker.internal:5000', + "image" TEXT NOT NULL, + "tag" TEXT NOT NULL DEFAULT 'latest', + "username" TEXT, + "password" TEXT, + "bot_id" TEXT NOT NULL, + + CONSTRAINT "container_image_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "cluster" ( + "id" INTEGER NOT NULL, + "bot_id" TEXT NOT NULL, + "container_id" TEXT NOT NULL, + "status" "ClusterStatus" NOT NULL, + "shard_ids" INTEGER[], + + CONSTRAINT "cluster_pkey" PRIMARY KEY ("bot_id","id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "container_image_server_name_key" ON "container_image"("server_name"); + +-- CreateIndex +CREATE UNIQUE INDEX "container_image_bot_id_key" ON "container_image"("bot_id"); + +-- AddForeignKey +ALTER TABLE "container_image" ADD CONSTRAINT "container_image_bot_id_fkey" FOREIGN KEY ("bot_id") REFERENCES "bot"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "cluster" ADD CONSTRAINT "cluster_bot_id_fkey" FOREIGN KEY ("bot_id") REFERENCES "bot"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/backend/prisma/schema.prisma b/packages/backend/prisma/schema.prisma index c082878..1e47262 100644 --- a/packages/backend/prisma/schema.prisma +++ b/packages/backend/prisma/schema.prisma @@ -1,7 +1,6 @@ generator client { - provider = "prisma-client-js" - moduleFormat = "esm" - output = "../../prisma-client/generated" + provider = "prisma-client" + output = "generated/prisma" } datasource db { @@ -9,21 +8,21 @@ datasource db { } model User { - username String @unique - password String + username String @unique + passwordHash String @map("password_hash") } model Bot { - id String @id - token String - totalShards Int @map("total_shards") - clusters Cluster[] - dockerImage DockerImage? + id String @id + token String + totalShards Int @map("total_shards") + clusters Cluster[] + containerImage ContainerImage? @@map("bot") } -model DockerImage { +model ContainerImage { id String @id @default(uuid()) serverName String @unique @default("host.docker.internal:5000") @map("server_name") image String @@ -33,7 +32,7 @@ model DockerImage { botId String @unique @map("bot_id") bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) - @@map("docker_image") + @@map("container_image") } enum ClusterStatus { @@ -47,7 +46,7 @@ enum ClusterStatus { model Cluster { id Int botId String @map("bot_id") - containerId String? @map("container_id") + containerId String @map("container_id") status ClusterStatus shardIds Int[] @map("shard_ids") bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) diff --git a/packages/backend/src/app.module.ts b/packages/backend/src/app.module.ts deleted file mode 100644 index 32e0294..0000000 --- a/packages/backend/src/app.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; - -import { AuthModule } from './auth/auth.module.js'; -import { BotModule } from './bot/bot.module.js'; -import { ClustersModule } from './clusters/clusters.module.js'; -import { DockerModule } from './docker/docker.module.js'; -import { LoggerModule } from './logger/logger.module.js'; -import { PrismaModule } from './prisma/prisma.module.js'; - -@Module({ - imports: [LoggerModule, ConfigModule, PrismaModule, BotModule, ClustersModule, DockerModule, AuthModule], -}) -export class AppModule {} diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts new file mode 100644 index 0000000..7b51efa --- /dev/null +++ b/packages/backend/src/app.ts @@ -0,0 +1,51 @@ +import { Scalar } from "@scalar/hono-api-reference"; +import { Hono } from "hono"; +import { openAPIRouteHandler } from "hono-openapi"; +import { logger } from "hono/logger"; + +import authController from "#controllers/auth.controller"; +import botController from "#controllers/bot.controller"; +import clusterController from "#controllers/cluster.controller"; + +const app = new Hono(); + +app.use("/api/*", logger()); + +app.get( + "/openapi.json", + openAPIRouteHandler(app, { + documentation: { + info: { + title: "Hallmaster API Documentation", + version: "1.0.0", + }, + components: { + securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + }, + }, + }, + }, + }), +); + +app.get( + "/docs", + Scalar({ + theme: "saturn", + url: "/openapi.json", + agent: { disabled: true }, + mcp: { disabled: true }, + }), +); + +const routes = app + .route("/api/auth", authController) + .route("/api/bot", botController) + .route("/api/clusters", clusterController); + +export default app; +export type AppType = typeof routes; diff --git a/packages/backend/src/auth/auth.controller.spec.ts b/packages/backend/src/auth/auth.controller.spec.ts deleted file mode 100644 index 17a2946..0000000 --- a/packages/backend/src/auth/auth.controller.spec.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { AuthController } from './auth.controller.js'; -import { AuthService } from './auth.service.js'; -import { AuthGuard } from './guards/jwt.guard.js'; - -describe('AuthController', () => { - let controller: AuthController; - const authGuard: DeepMockProxy = mockDeep(); - const authService: DeepMockProxy = mockDeep(); - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - providers: [ - { - provide: AuthService, - useValue: authService, - }, - ], - }) - .overrideGuard(AuthGuard) - .useValue(authGuard) - .compile(); - - controller = module.get(AuthController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/packages/backend/src/auth/auth.controller.ts b/packages/backend/src/auth/auth.controller.ts deleted file mode 100644 index a1758c6..0000000 --- a/packages/backend/src/auth/auth.controller.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiConflictResponse, - ApiCreatedResponse, - ApiInternalServerErrorResponse, - ApiOkResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; - -import { ApiErrorZodDto } from '../common/dto/api-error.dto.js'; - -import { AuthService } from './auth.service.js'; -import { LoginDto } from './dto/login.dto.js'; -import { RegisterDto } from './dto/register.dto.js'; -import { UserTokenDto } from './entities/user-token.entity.js'; - -@Controller('auth') -@ApiTags('Authentication') -@ApiInternalServerErrorResponse({ - type: ApiErrorZodDto, - description: 'This service is temporary unavailable.', -}) -@ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The body of the request is invalid.', -}) -export class AuthController { - constructor(private readonly authService: AuthService) {} - - @Post('register') - @HttpCode(HttpStatus.CREATED) - @ApiCreatedResponse({ - type: UserTokenDto, - description: 'The Bearer token (JWT) for the new user.', - }) - @ApiConflictResponse({ - type: ApiErrorZodDto, - description: 'A user is already registered.', - }) - async register(@Body() body: RegisterDto): Promise { - return await this.authService.register(body); - } - - @Post('login') - @HttpCode(HttpStatus.OK) - @ApiOkResponse({ - type: UserTokenDto, - description: 'The user token used to communicate with the API.', - }) - @ApiUnauthorizedResponse({ - type: ApiErrorZodDto, - description: 'Either the email or the password is invalid.', - }) - async login(@Body() body: LoginDto): Promise { - return await this.authService.login(body); - } -} diff --git a/packages/backend/src/auth/auth.module.ts b/packages/backend/src/auth/auth.module.ts deleted file mode 100644 index 3ef05fd..0000000 --- a/packages/backend/src/auth/auth.module.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { JwtModule, JwtSignOptions } from '@nestjs/jwt'; - -import { PrismaModule } from '../prisma/prisma.module.js'; - -import { AuthController } from './auth.controller.js'; -import { AuthService } from './auth.service.js'; -import { AuthGuard } from './guards/jwt.guard.js'; - -@Module({ - imports: [ - PrismaModule, - JwtModule.registerAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: (config: ConfigService) => ({ - secret: config.getOrThrow('JWT_SECRET'), - signOptions: { - expiresIn: config.getOrThrow('JWT_EXPIRES_IN'), - }, - }), - }), - ], - controllers: [AuthController], - providers: [AuthService, AuthGuard], - exports: [AuthService, AuthGuard], -}) -export class AuthModule {} diff --git a/packages/backend/src/auth/auth.service.spec.ts b/packages/backend/src/auth/auth.service.spec.ts deleted file mode 100644 index bf0424a..0000000 --- a/packages/backend/src/auth/auth.service.spec.ts +++ /dev/null @@ -1,212 +0,0 @@ -import type { Prisma } from '@hallmaster/prisma-client'; -import { beforeEach, describe, expect, it, jest } from '@jest/globals'; -import { ConflictException, UnauthorizedException } from '@nestjs/common'; -import { JwtModule, JwtService } from '@nestjs/jwt'; -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { PrismaService } from '../prisma/prisma.service.js'; - -import { AuthService } from './auth.service.js'; -import type { RegisterDto } from './dto/register.dto.js'; - -describe('authService', () => { - let service: AuthService; - let jwtService: JwtService; - const prismaService: DeepMockProxy = mockDeep(); - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [ - JwtModule.register({ - secret: 'secret', - signOptions: { - expiresIn: '1m', - }, - }), - ], - providers: [ - AuthService, - { - provide: PrismaService, - useValue: prismaService, - }, - ], - }).compile(); - - service = module.get(AuthService); - jwtService = module.get(JwtService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - it('should create a user', async () => { - const credentials: RegisterDto = { - username: 'bob', - password: 'ilikealice', - }; - - prismaService.user.count.mockResolvedValueOnce(0); - - prismaService.user.create.mockResolvedValueOnce({ - username: credentials.username, - } as Prisma.UserGetPayload); - - const hashPasswordMock = jest - .spyOn(AuthService, 'hashPassword') - .mockImplementationOnce((password) => Promise.resolve(`${password}-hashed`)); - - const signAsyncMock = jest.spyOn(jwtService, 'signAsync'); - - const { token } = await service.register(credentials); - - expect(prismaService.user.count).toHaveBeenCalledTimes(1); - - expect(hashPasswordMock).toHaveBeenCalledWith(credentials.password); - expect(hashPasswordMock).toHaveBeenCalledTimes(1); - - expect(prismaService.user.create).toHaveBeenCalledWith({ - select: { - username: true, - }, - data: { - username: credentials.username, - password: `${credentials.password}-hashed`, - }, - }); - expect(prismaService.user.create).toHaveBeenCalledTimes(1); - - expect(signAsyncMock).toHaveBeenCalledWith({ - username: credentials.username, - }); - expect(signAsyncMock).toHaveBeenCalledTimes(1); - - const verifyAsyncMock = jest.spyOn(jwtService, 'verifyAsync'); - - const data = await service.verifyToken(token); - - expect(verifyAsyncMock).toHaveBeenCalledWith(token); - expect(verifyAsyncMock).toHaveBeenCalledTimes(1); - - expect(data.username).toStrictEqual(credentials.username); - }); - - it('should not create a user', async () => { - const credentials: RegisterDto = { - username: 'bob', - password: 'ilikealice', - }; - - prismaService.user.count.mockResolvedValueOnce(1); - - await expect(service.register(credentials)).rejects.toBeInstanceOf(ConflictException); - - expect(prismaService.user.count).toHaveBeenCalledTimes(1); - }); - - it('should sign in a user', async () => { - const credentials: RegisterDto = { - username: 'bob', - password: 'ilikealice', - }; - - prismaService.user.findFirst.mockResolvedValueOnce({ - username: credentials.username, - password: `${credentials.password}-hashed`, - } as Prisma.UserGetPayload); - - const verifyPasswordMock = jest - .spyOn(AuthService, 'verifyPassword') - .mockImplementationOnce((hash: string, password: string) => Promise.resolve(hash === `${password}-hashed`)); - - const signAsyncMock = jest.spyOn(jwtService, 'signAsync'); - - const { token } = await service.login(credentials); - - expect(prismaService.user.findFirst).toHaveBeenCalledWith({ - select: { - username: true, - password: true, - }, - where: { - username: credentials.username, - }, - }); - expect(prismaService.user.findFirst).toHaveBeenCalledTimes(1); - - expect(verifyPasswordMock).toHaveBeenCalledWith(`${credentials.password}-hashed`, credentials.password); - expect(verifyPasswordMock).toHaveBeenCalledTimes(1); - - expect(signAsyncMock).toHaveBeenCalledWith({ - username: credentials.username, - }); - expect(signAsyncMock).toHaveBeenCalledTimes(1); - - const verifyAsyncMock = jest.spyOn(jwtService, 'verifyAsync'); - - const data = await service.verifyToken(token); - - expect(verifyAsyncMock).toHaveBeenCalledWith(token); - expect(verifyAsyncMock).toHaveBeenCalledTimes(1); - - expect(data.username).toStrictEqual(credentials.username); - }); - - it('should sign in a user (user not found)', async () => { - const credentials: RegisterDto = { - username: 'bob', - password: 'ilikealice', - }; - - prismaService.user.findFirst.mockResolvedValueOnce(null); - - await expect(service.login(credentials)).rejects.toBeInstanceOf(UnauthorizedException); - - expect(prismaService.user.findFirst).toHaveBeenCalledWith({ - select: { - username: true, - password: true, - }, - where: { - username: credentials.username, - }, - }); - expect(prismaService.user.findFirst).toHaveBeenCalledTimes(1); - }); - - it('should sign in a user (passwords do not match)', async () => { - const credentials: RegisterDto = { - username: 'bob', - password: 'ilikealice', - }; - - prismaService.user.findFirst.mockResolvedValueOnce({ - username: credentials.username, - password: `not-${credentials.password}-hashed`, - } as Prisma.UserGetPayload); - - const verifyPasswordMock = jest - .spyOn(AuthService, 'verifyPassword') - .mockImplementationOnce((hash: string, password: string) => Promise.resolve(hash === `${password}-hashed`)); - - await expect(service.login(credentials)).rejects.toBeInstanceOf(UnauthorizedException); - - expect(prismaService.user.findFirst).toHaveBeenCalledWith({ - select: { - username: true, - password: true, - }, - where: { - username: credentials.username, - }, - }); - expect(prismaService.user.findFirst).toHaveBeenCalledTimes(1); - - expect(verifyPasswordMock).toHaveBeenCalledWith(`not-${credentials.password}-hashed`, credentials.password); - expect(verifyPasswordMock).toHaveBeenCalledTimes(1); - }); -}); diff --git a/packages/backend/src/auth/auth.service.ts b/packages/backend/src/auth/auth.service.ts deleted file mode 100644 index d0261a3..0000000 --- a/packages/backend/src/auth/auth.service.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { randomBytes } from 'node:crypto'; - -import { ConflictException, Injectable, UnauthorizedException } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; -import { hash, argon2id, verify } from 'argon2'; - -import { PrismaService } from '../prisma/prisma.service.js'; - -import { LoginDto } from './dto/login.dto.js'; -import { RegisterDto } from './dto/register.dto.js'; -import { UserTokenDataDto } from './entities/user-token-data.entity.js'; -import { UserTokenDto } from './entities/user-token.entity.js'; - -/* https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id */ -const OWASP_CONFIGS = [ - { m: 47104, t: 1, p: 1 } /* DO NOT USE WITH ARGON2I */, - { m: 19456, t: 2, p: 1 } /* DO NOT USE WITH ARGON2I */, - { m: 12288, t: 3, p: 1 }, - { m: 9216, t: 4, p: 1 }, - { m: 7168, t: 5, p: 1 }, -]; - -@Injectable() -export class AuthService { - private static CONFIG = OWASP_CONFIGS[2]; - - constructor( - private readonly prismaService: PrismaService, - private readonly jwtService: JwtService, - ) {} - - static async hashPassword(password: string) { - return await hash(password, { - type: argon2id, - memoryCost: AuthService.CONFIG.m, - timeCost: AuthService.CONFIG.t, - parallelism: AuthService.CONFIG.p, - hashLength: 32, - salt: randomBytes(16), - }); - } - - static async verifyPassword(hash: string, password: string) { - return await verify(hash, password); - } - - async register(dto: RegisterDto): Promise { - const users = await this.prismaService.user.count(); - if (users > 0) { - throw new ConflictException(); - } - - const hashedPassword = await AuthService.hashPassword(dto.password); - - const { username } = await this.prismaService.user.create({ - select: { - username: true, - }, - data: { - username: dto.username, - password: hashedPassword, - }, - }); - - const token = await this.jwtService.signAsync({ - username: username, - }); - - return { token: token }; - } - - async login(dto: LoginDto): Promise { - const user = await this.prismaService.user.findFirst({ - select: { - username: true, - password: true, - }, - where: { - username: dto.username, - }, - }); - - if (null === user) { - throw new UnauthorizedException(); - } - - const passwordMatch = await AuthService.verifyPassword(user.password, dto.password); - if (!passwordMatch) { - throw new UnauthorizedException(); - } - - const token = await this.jwtService.signAsync({ - username: user.username, - }); - return { token }; - } - - async verifyToken(token: string): Promise { - return await this.jwtService.verifyAsync(token); - } -} diff --git a/packages/backend/src/auth/dto/login.dto.ts b/packages/backend/src/auth/dto/login.dto.ts deleted file mode 100644 index 3c2f806..0000000 --- a/packages/backend/src/auth/dto/login.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; - -import { loginSchema } from '../schemas/login.schema.js'; - -export class LoginDto extends createZodDto(loginSchema) {} diff --git a/packages/backend/src/auth/dto/register.dto.ts b/packages/backend/src/auth/dto/register.dto.ts deleted file mode 100644 index 4f95994..0000000 --- a/packages/backend/src/auth/dto/register.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; - -import { registerSchema } from '../schemas/register.schema.js'; - -export class RegisterDto extends createZodDto(registerSchema) {} diff --git a/packages/backend/src/auth/entities/user-token-data.entity.ts b/packages/backend/src/auth/entities/user-token-data.entity.ts deleted file mode 100644 index 9a6ea4e..0000000 --- a/packages/backend/src/auth/entities/user-token-data.entity.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; - -import { userTokenDataSchema } from '../schemas/user-token-data.schema.js'; - -export class UserTokenDataDto extends createZodDto(userTokenDataSchema) {} diff --git a/packages/backend/src/auth/entities/user-token.entity.ts b/packages/backend/src/auth/entities/user-token.entity.ts deleted file mode 100644 index 3f88a75..0000000 --- a/packages/backend/src/auth/entities/user-token.entity.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; - -import { userTokenSchema } from '../schemas/user-token.schema.js'; - -export class UserTokenDto extends createZodDto(userTokenSchema) {} diff --git a/packages/backend/src/auth/guards/jwt.guard.ts b/packages/backend/src/auth/guards/jwt.guard.ts deleted file mode 100644 index ec67cd9..0000000 --- a/packages/backend/src/auth/guards/jwt.guard.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'; -import { FastifyRequest } from 'fastify'; - -import { AuthService } from '../auth.service.js'; - -@Injectable() -export class AuthGuard implements CanActivate { - constructor(private readonly authService: AuthService) {} - - private static extractBearerToken(request: FastifyRequest): string { - const authHeader = request.headers.authorization ?? ''; - const [bearer, token] = authHeader.split(' '); - - if (bearer !== 'Bearer' || !token) throw new UnauthorizedException(); - return token; - } - - async canActivate(context: ExecutionContext) { - const request = context.switchToHttp().getRequest(); - const token = AuthGuard.extractBearerToken(request); - - try { - const payload = await this.authService.verifyToken(token); - - Object.defineProperty(request, 'user', { - configurable: true, - value: payload, - }); - } catch { - throw new UnauthorizedException(); - } - return true; - } -} diff --git a/packages/backend/src/auth/schemas/login.schema.ts b/packages/backend/src/auth/schemas/login.schema.ts deleted file mode 100644 index 1290b3d..0000000 --- a/packages/backend/src/auth/schemas/login.schema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from 'zod'; - -export const loginSchema = z.object({ - username: z.string().min(1).meta({ - description: "The user's username.", - }), - password: z.string().min(8).meta({ - description: "The user's password.", - }), -}); - -export type Login = z.infer; diff --git a/packages/backend/src/auth/schemas/register.schema.ts b/packages/backend/src/auth/schemas/register.schema.ts deleted file mode 100644 index a4bd993..0000000 --- a/packages/backend/src/auth/schemas/register.schema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from 'zod'; - -export const registerSchema = z.object({ - username: z.string().min(1).meta({ - description: "The user's username.", - }), - password: z.string().min(8).meta({ - description: "The user's password.", - }), -}); - -export type Register = z.infer; diff --git a/packages/backend/src/auth/schemas/user-token-data.schema.ts b/packages/backend/src/auth/schemas/user-token-data.schema.ts deleted file mode 100644 index 1711ced..0000000 --- a/packages/backend/src/auth/schemas/user-token-data.schema.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from 'zod'; - -export const userTokenDataSchema = z.object({ - username: z.string().meta({ description: "The authenticated user's username." }), -}); - -export type UserTokenData = z.infer; diff --git a/packages/backend/src/auth/schemas/user-token.schema.ts b/packages/backend/src/auth/schemas/user-token.schema.ts deleted file mode 100644 index 44839d6..0000000 --- a/packages/backend/src/auth/schemas/user-token.schema.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { z } from 'zod'; - -export const userTokenSchema = z.object({ - token: z.jwt().meta({ - description: 'The user session token.', - }), -}); - -export type UserToken = z.infer; diff --git a/packages/backend/src/bot/bot.controller.spec.ts b/packages/backend/src/bot/bot.controller.spec.ts deleted file mode 100644 index 60fba34..0000000 --- a/packages/backend/src/bot/bot.controller.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { AuthGuard } from '../auth/guards/jwt.guard.js'; - -import { BotController } from './bot.controller.js'; -import { BotService } from './bot.service.js'; - -describe('BotController', () => { - let controller: BotController; - const authGuard: DeepMockProxy = mockDeep(); - const botService: DeepMockProxy = mockDeep(); - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [BotController], - providers: [ - { - provide: BotService, - useValue: botService, - }, - ], - }) - .overrideGuard(AuthGuard) - .useValue(authGuard) - .compile(); - - controller = module.get(BotController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/packages/backend/src/bot/bot.controller.ts b/packages/backend/src/bot/bot.controller.ts deleted file mode 100644 index 58dd887..0000000 --- a/packages/backend/src/bot/bot.controller.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Patch, Post, UseGuards } from '@nestjs/common'; -import { - ApiAcceptedResponse, - ApiBadRequestResponse, - ApiBearerAuth, - ApiConflictResponse, - ApiCreatedResponse, - ApiFailedDependencyResponse, - ApiNoContentResponse, - ApiNotFoundResponse, - ApiOkResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; - -import { AuthGuard } from '../auth/guards/jwt.guard.js'; -import { ApiErrorZodDto } from '../common/dto/api-error.dto.js'; - -import { BotService } from './bot.service.js'; -import { CreateBotZodDto } from './dto/create-bot.dto.js'; -import { GetBotZodDto } from './dto/get-bot.dto.js'; -import { GetRecommendedShardsZodDto } from './dto/get-recommended-shards.dto.js'; -import { UpdateBotZodDto } from './dto/update-bot.dto.js'; - -@ApiTags('Bot') -@Controller('bot') -@ApiBearerAuth('jwt') -@UseGuards(AuthGuard) -@ApiUnauthorizedResponse({ - type: ApiErrorZodDto, - description: 'This route is protected by an Authorization header that is either not provided or invalid.', -}) -export class BotController { - constructor(private readonly botService: BotService) {} - - @Get('recommended-shards') - @ApiOkResponse({ - description: 'The recommended number of shards from the Discord API.', - type: GetRecommendedShardsZodDto, - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'No bot created beforehand.', - }) - @ApiFailedDependencyResponse({ - type: ApiErrorZodDto, - description: 'The Discord bot token is invalid or got an invalid response from the Discord API.', - }) - getRecommendedShards() { - return this.botService.getRecommendedShards(); - } - - @Post() - @HttpCode(HttpStatus.CREATED) - @ApiCreatedResponse({ - description: 'The bot informations have been retrieve correctly and it has been created in database successfully.', - type: GetBotZodDto, - }) - @ApiConflictResponse({ - type: ApiErrorZodDto, - description: 'The bot is already created.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The request body is invalid, or the Docker image could not be pulled.', - }) - @ApiFailedDependencyResponse({ - type: ApiErrorZodDto, - description: 'The Discord bot token is invalid or got an invalid response from the Discord API.', - }) - create(@Body() createBotDto: CreateBotZodDto) { - return this.botService.create(createBotDto); - } - - @Get() - @ApiOkResponse({ - description: 'The registered bot.', - type: GetBotZodDto, - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'No bot created beforehand.', - }) - findOne() { - return this.botService.findOne(); - } - - @Patch() - @HttpCode(HttpStatus.ACCEPTED) - @ApiAcceptedResponse({ - description: 'Updated bot information.', - type: GetBotZodDto, - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'No bot to update.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The request body or cluster layout is invalid, or the Docker image could not be pulled.', - }) - @ApiFailedDependencyResponse({ - type: ApiErrorZodDto, - description: 'The Discord bot token is invalid or got an invalid response from the Discord API.', - }) - update(@Body() updateBotDto: UpdateBotZodDto) { - return this.botService.update(updateBotDto); - } - - @Delete() - @HttpCode(HttpStatus.NO_CONTENT) - @ApiNoContentResponse({ - description: 'The bot is deleted.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'No bot to update.', - }) - async remove() { - await this.botService.remove(); - } -} diff --git a/packages/backend/src/bot/bot.module.ts b/packages/backend/src/bot/bot.module.ts deleted file mode 100644 index 4ba22a2..0000000 --- a/packages/backend/src/bot/bot.module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { AuthModule } from '../auth/auth.module.js'; -import { ClustersModule } from '../clusters/clusters.module.js'; -import { DiscordModule } from '../discord/discord.module.js'; -import { DockerModule } from '../docker/docker.module.js'; -import { PrismaModule } from '../prisma/prisma.module.js'; - -import { BotController } from './bot.controller.js'; -import { BotService } from './bot.service.js'; - -@Module({ - imports: [AuthModule, PrismaModule, ClustersModule, DockerModule, DiscordModule], - controllers: [BotController], - providers: [BotService], -}) -export class BotModule {} diff --git a/packages/backend/src/bot/bot.service.spec.ts b/packages/backend/src/bot/bot.service.spec.ts deleted file mode 100644 index be61f46..0000000 --- a/packages/backend/src/bot/bot.service.spec.ts +++ /dev/null @@ -1,609 +0,0 @@ -import { jest } from '@jest/globals'; -import { BadRequestException, ConflictException, NotFoundException } from '@nestjs/common'; -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/client'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { ClustersService } from '../clusters/clusters.service.js'; -import { DiscordService } from '../discord/discord.service.js'; -import { DockerService } from '../docker/docker.service.js'; -import type { CreateBotDto, UpdateBotDto } from '../index.dto.js'; -import { PrismaService } from '../prisma/prisma.service.js'; - -import { BotService } from './bot.service.js'; - -const HALLMASTER_BOT_ID = '1352006130926096504'; -const DISCORD_BOT_TOKEN = `${Buffer.from(HALLMASTER_BOT_ID).toString('base64')}.YYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ`; - -const MOCK_DOCKER_IMAGE = { - serverName: 'host.docker.internal:5000', - image: 'hallmaster/discord-bot', - tag: 'latest', - username: null, -}; - -const EXPECTED_DOCKER_IMAGE = { - image: 'host.docker.internal:5000/hallmaster/discord-bot:latest', - username: null, -}; - -const mockDiscordGatewayBotResponse = (status = 200, body: unknown = { shards: 1 }) => - ({ - ok: status >= 200 && status < 300, - status, - statusText: status === 401 ? 'Unauthorized' : 'OK', - json: () => Promise.resolve(body), - }) as Response; - -const DISCORD_USER = { - id: HALLMASTER_BOT_ID, - username: 'HallMaster', - global_name: 'HallMaster Bot', - discriminator: '0', - avatar: 'abc123', - banner: 'a_def456', - accent_color: 5793266, -}; - -const EXPECTED_DISCORD_PROFILE = { - name: 'HallMaster', - displayName: 'HallMaster Bot', - discriminator: '0', - avatarUrl: `https://cdn.discordapp.com/avatars/${HALLMASTER_BOT_ID}/abc123.png`, - bannerUrl: `https://cdn.discordapp.com/banners/${HALLMASTER_BOT_ID}/a_def456.gif`, - accentColor: 5793266, -}; - -const mockDiscordUserResponse = (body: unknown = DISCORD_USER) => - ({ ok: true, status: 200, statusText: 'OK', json: () => Promise.resolve(body) }) as Response; - -describe('BotService', () => { - let service: BotService; - const originalFetch = globalThis.fetch; - const fetchMock = jest.fn(); - - const prismaService: DeepMockProxy = mockDeep(); - const clustersService: DeepMockProxy = mockDeep(); - const dockerService: DeepMockProxy = mockDeep(); - - beforeAll(() => { - globalThis.fetch = fetchMock; - }); - - afterAll(() => { - globalThis.fetch = originalFetch; - }); - - beforeEach(async () => { - fetchMock.mockReset(); - fetchMock.mockResolvedValue(mockDiscordGatewayBotResponse()); - dockerService.verifyImage.mockResolvedValue(); - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - BotService, - { provide: PrismaService, useValue: prismaService }, - { provide: ClustersService, useValue: clustersService }, - { provide: DockerService, useValue: dockerService }, - DiscordService, - ], - }).compile(); - - service = module.get(BotService); - }); - - it('should create the bot without layout', async () => { - const body: CreateBotDto = { - token: DISCORD_BOT_TOKEN, - dockerImage: { - image: 'host.docker.internal:5000/hallmaster/discord-bot:latest', - username: null, - password: null, - }, - }; - - (prismaService.bot.create as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - const result = await service.create(body); - - expect(result).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 0, - layout: [], - dockerImage: EXPECTED_DOCKER_IMAGE, - }); - }); - - it('should throw ConflictException when bot already exists', async () => { - const body: CreateBotDto = { - token: DISCORD_BOT_TOKEN, - dockerImage: { - image: 'host.docker.internal:5000/hallmaster/discord-bot:latest', - username: null, - password: null, - }, - }; - - prismaService.bot.create.mockRejectedValueOnce( - new PrismaClientKnownRequestError('Conflict', { - code: 'P2002', - clientVersion: '', - }), - ); - - await expect(service.create(body)).rejects.toBeInstanceOf(ConflictException); - }); - - it('should reject creation when Discord rejects the token', async () => { - const body: CreateBotDto = { - token: DISCORD_BOT_TOKEN, - dockerImage: { - image: 'host.docker.internal:5000/hallmaster/discord-bot:latest', - username: null, - password: null, - }, - }; - - fetchMock.mockResolvedValueOnce(mockDiscordGatewayBotResponse(401)); - - await expect(service.create(body)).rejects.toThrow('Invalid Discord bot token.'); - expect(prismaService.bot.create).not.toHaveBeenCalled(); - }); - - it('should reject creation when the Docker image cannot be pulled', async () => { - const body: CreateBotDto = { - token: DISCORD_BOT_TOKEN, - dockerImage: { - image: 'host.docker.internal:5000/hallmaster/discord-bot:latest', - username: null, - password: null, - }, - }; - - dockerService.verifyImage.mockRejectedValueOnce(new BadRequestException('Unable to pull the Docker image.')); - - await expect(service.create(body)).rejects.toBeInstanceOf(BadRequestException); - expect(prismaService.bot.create).not.toHaveBeenCalled(); - }); - - it('should find the bot with its Discord profile', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 6, - token: DISCORD_BOT_TOKEN, - clusters: [ - { id: 0, status: 'RUNNING', shardIds: [0, 1, 2] }, - { id: 1, status: 'RUNNING', shardIds: [3, 4, 5] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - fetchMock.mockResolvedValueOnce(mockDiscordUserResponse()); - - const data = await service.findOne(); - - expect(data).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 6, - layout: [ - { id: 0, shardIds: [0, 1, 2] }, - { id: 1, shardIds: [3, 4, 5] }, - ], - dockerImage: EXPECTED_DOCKER_IMAGE, - discord: EXPECTED_DISCORD_PROFILE, - }); - }); - - it('should find the bot without layout', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - token: DISCORD_BOT_TOKEN, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - fetchMock.mockResolvedValueOnce(mockDiscordUserResponse()); - - const data = await service.findOne(); - - expect(data).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 0, - layout: [], - dockerImage: EXPECTED_DOCKER_IMAGE, - discord: EXPECTED_DISCORD_PROFILE, - }); - }); - - it('should return a null Discord profile when Discord is unreachable', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - token: DISCORD_BOT_TOKEN, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - fetchMock.mockRejectedValueOnce(new Error('network down')); - - const data = await service.findOne(); - - expect(data.discord).toBeNull(); - }); - - it('should throw NotFoundException when no bot exists', async () => { - prismaService.bot.findFirst.mockResolvedValueOnce(null); - - await expect(service.findOne()).rejects.toBeInstanceOf(NotFoundException); - }); - - it('should update the bot layout (grow shards)', async () => { - const body: UpdateBotDto = { - layout: [ - { id: 0, shardIds: [0, 1, 2] }, - { id: 1, shardIds: [3, 4, 5] }, - { id: 2, shardIds: [6, 7, 8] }, - { shardIds: [9] }, - ], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 7, - clusters: [ - { id: 0, status: 'RUNNING', shardIds: [0, 1, 2] }, - { id: 1, status: 'RUNNING', shardIds: [3, 4, 5] }, - { id: 2, status: 'RUNNING', shardIds: [6] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - clustersService.remove.mockResolvedValue(); - - (prismaService.bot.update as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 10, - clusters: [ - { id: 0, status: 'UPDATING', shardIds: [0, 1, 2] }, - { id: 1, status: 'UPDATING', shardIds: [3, 4, 5] }, - { id: 2, status: 'UPDATING', shardIds: [6, 7, 8] }, - { id: 3, status: 'UPDATING', shardIds: [9] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - clustersService.start.mockResolvedValue(); - - const data = await service.update(body); - - expect(clustersService.remove).toHaveBeenCalledWith(0); - expect(clustersService.remove).toHaveBeenCalledWith(1); - expect(clustersService.remove).toHaveBeenCalledWith(2); - expect(clustersService.remove).toHaveBeenCalledTimes(3); - - expect(clustersService.start).toHaveBeenCalledWith(0); - expect(clustersService.start).toHaveBeenCalledWith(1); - expect(clustersService.start).toHaveBeenCalledWith(2); - expect(clustersService.start).toHaveBeenCalledWith(3); - expect(clustersService.start).toHaveBeenCalledTimes(4); - - expect(data).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 10, - layout: [ - { id: 0, shardIds: [0, 1, 2] }, - { id: 1, shardIds: [3, 4, 5] }, - { id: 2, shardIds: [6, 7, 8] }, - { id: 3, shardIds: [9] }, - ], - dockerImage: EXPECTED_DOCKER_IMAGE, - }); - }); - - it('should set initial layout via update', async () => { - const body: UpdateBotDto = { - layout: [{ shardIds: [0, 1, 2] }, { shardIds: [3, 4, 5] }, { shardIds: [6] }], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - (prismaService.bot.update as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 7, - clusters: [ - { id: 0, status: 'UPDATING', shardIds: [0, 1, 2] }, - { id: 1, status: 'UPDATING', shardIds: [3, 4, 5] }, - { id: 2, status: 'UPDATING', shardIds: [6] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - clustersService.start.mockResolvedValue(); - - const data = await service.update(body); - - expect(clustersService.remove).not.toHaveBeenCalled(); - - expect(clustersService.start).toHaveBeenCalledWith(0); - expect(clustersService.start).toHaveBeenCalledWith(1); - expect(clustersService.start).toHaveBeenCalledWith(2); - expect(clustersService.start).toHaveBeenCalledTimes(3); - - expect(data).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 7, - layout: [ - { id: 0, shardIds: [0, 1, 2] }, - { id: 1, shardIds: [3, 4, 5] }, - { id: 2, shardIds: [6] }, - ], - dockerImage: EXPECTED_DOCKER_IMAGE, - }); - }); - - it('should update the bot layout only (same shards)', async () => { - const body: UpdateBotDto = { - layout: [ - { id: 0, shardIds: [0, 1, 2, 3, 4] }, - { id: 1, shardIds: [5, 6] }, - ], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 7, - clusters: [ - { id: 0, status: 'STOPPED', shardIds: [0, 1, 2] }, - { id: 1, status: 'RUNNING', shardIds: [3, 4, 5] }, - { id: 2, status: 'RUNNING', shardIds: [6] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - clustersService.remove.mockResolvedValue(); - - (prismaService.bot.update as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 7, - clusters: [ - { id: 0, status: 'UPDATING', shardIds: [0, 1, 2, 3, 4] }, - { id: 1, status: 'UPDATING', shardIds: [5, 6] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - clustersService.start.mockResolvedValue(); - - const data = await service.update(body); - - expect(clustersService.remove).toHaveBeenCalledTimes(3); - - expect(clustersService.start).toHaveBeenCalledWith(0); - expect(clustersService.start).toHaveBeenCalledWith(1); - expect(clustersService.start).toHaveBeenCalledTimes(2); - - expect(data).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 7, - layout: [ - { id: 0, shardIds: [0, 1, 2, 3, 4] }, - { id: 1, shardIds: [5, 6] }, - ], - dockerImage: EXPECTED_DOCKER_IMAGE, - }); - }); - - it('should reject layout with duplicate shard IDs', async () => { - const body: UpdateBotDto = { - layout: [{ shardIds: [0, 1] }, { shardIds: [1, 2] }], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - await expect(service.update(body)).rejects.toBeInstanceOf(BadRequestException); - }); - - it('should reject layout with out-of-range shard IDs', async () => { - const body: UpdateBotDto = { - layout: [{ shardIds: [0, 5] }], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - await expect(service.update(body)).rejects.toBeInstanceOf(BadRequestException); - }); - - it('should reject layout with an empty cluster', async () => { - const body: UpdateBotDto = { - layout: [{ shardIds: [0, 1] }, { shardIds: [] }, { shardIds: [2] }], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - await expect(service.update(body)).rejects.toBeInstanceOf(BadRequestException); - }); - - it('should reject layout referencing a non-existent cluster ID', async () => { - const body: UpdateBotDto = { - layout: [ - { id: 0, shardIds: [0, 1] }, - { id: 98, shardIds: [2] }, - ], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 2, - clusters: [{ id: 0, status: 'RUNNING', shardIds: [0, 1] }], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - await expect(service.update(body)).rejects.toBeInstanceOf(BadRequestException); - }); - - it('should reject layout with duplicate cluster IDs', async () => { - const body: UpdateBotDto = { - layout: [ - { id: 0, shardIds: [0, 1] }, - { id: 0, shardIds: [2] }, - ], - }; - - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 2, - clusters: [{ id: 0, status: 'RUNNING', shardIds: [0, 1] }], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - await expect(service.update(body)).rejects.toBeInstanceOf(BadRequestException); - }); - - it('should throw NotFoundException when updating non-existent bot', async () => { - const body: UpdateBotDto = { - layout: [{ shardIds: [0, 1, 2] }, { shardIds: [3, 4] }], - }; - - prismaService.bot.findFirst.mockResolvedValueOnce(null); - - await expect(service.update(body)).rejects.toBeInstanceOf(NotFoundException); - }); - - it('should reject update when Discord rejects the new token', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - token: DISCORD_BOT_TOKEN, - dockerImage: MOCK_DOCKER_IMAGE, - }); - - fetchMock.mockResolvedValueOnce(mockDiscordGatewayBotResponse(401)); - - await expect(service.update({ token: 'a-new-but-invalid-token' })).rejects.toThrow('Invalid Discord bot token.'); - expect(prismaService.bot.update).not.toHaveBeenCalled(); - }); - - it('should not call Discord when the token is unchanged in update', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - token: DISCORD_BOT_TOKEN, - dockerImage: MOCK_DOCKER_IMAGE, - }); - - (prismaService.bot.update as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - await service.update({ token: DISCORD_BOT_TOKEN }); - - expect(fetchMock).not.toHaveBeenCalled(); - }); - - it('should reject update when the new Docker image cannot be pulled', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - (prismaService.dockerImage.findUniqueOrThrow as jest.Mock).mockResolvedValueOnce({ - ...MOCK_DOCKER_IMAGE, - password: null, - }); - - dockerService.verifyImage.mockRejectedValueOnce(new BadRequestException('Unable to pull the Docker image.')); - - await expect( - service.update({ dockerImage: { image: 'host.docker.internal:5000/hallmaster/discord-bot:broken' } }), - ).rejects.toBeInstanceOf(BadRequestException); - expect(prismaService.bot.update).not.toHaveBeenCalled(); - }); - - it('should not verify Docker image when it is unchanged in update', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - (prismaService.bot.update as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 0, - clusters: [], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - dockerService.verifyImage.mockClear(); - - await service.update({ layout: [] }); - - expect(dockerService.verifyImage).not.toHaveBeenCalled(); - }); - - it('should remove the bot and stop all clusters', async () => { - (prismaService.bot.findFirst as jest.Mock).mockResolvedValueOnce({ - id: HALLMASTER_BOT_ID, - totalShards: 5, - clusters: [ - { id: 0, status: 'STOPPED', shardIds: [0, 1, 2] }, - { id: 1, status: 'RUNNING', shardIds: [3, 4] }, - ], - dockerImage: MOCK_DOCKER_IMAGE, - }); - - prismaService.bot.delete.mockResolvedValueOnce({} as never); - clustersService.stopByCluster.mockResolvedValue(); - - const data = await service.remove(); - - expect(clustersService.stopByCluster).toHaveBeenCalledTimes(2); - - expect(data).toStrictEqual({ - id: HALLMASTER_BOT_ID, - shards: 5, - layout: [ - { id: 0, shardIds: [0, 1, 2] }, - { id: 1, shardIds: [3, 4] }, - ], - dockerImage: EXPECTED_DOCKER_IMAGE, - }); - }); - - it('should throw NotFoundException when removing non-existent bot', async () => { - prismaService.bot.findFirst.mockResolvedValueOnce(null); - - await expect(service.remove()).rejects.toBeInstanceOf(NotFoundException); - }); -}); diff --git a/packages/backend/src/bot/bot.service.ts b/packages/backend/src/bot/bot.service.ts deleted file mode 100644 index 08abb75..0000000 --- a/packages/backend/src/bot/bot.service.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common'; -import { PrismaClientKnownRequestError } from '@prisma/client/runtime/client'; - -import { ClustersService } from '../clusters/clusters.service.js'; -import { DiscordService, type DiscordProfile } from '../discord/discord.service.js'; -import { DockerService } from '../docker/docker.service.js'; -import { PrismaService } from '../prisma/prisma.service.js'; - -import { formatDockerImage } from './bot.utils.js'; -import { CreateBotZodDto } from './dto/create-bot.dto.js'; -import { GetBotZodDto } from './dto/get-bot.dto.js'; -import { UpdateBotZodDto } from './dto/update-bot.dto.js'; - -type LayoutInput = NonNullable; - -@Injectable() -export class BotService { - constructor( - private readonly prismaService: PrismaService, - private readonly clustersService: ClustersService, - private readonly dockerService: DockerService, - private readonly discordService: DiscordService, - ) {} - - private parseDockerImage(image: string): { serverName: string; image: string; tag: string } { - const [serverName, ...path] = image.trim().split('/'); - const [imageName, tag] = path.join('/').split(':'); - return { serverName, image: imageName, tag }; - } - - private static readonly BOT_SELECT = { - id: true, - totalShards: true, - clusters: true, - dockerImage: { - select: { - serverName: true, - image: true, - tag: true, - username: true, - }, - }, - } as const; - - private getBotId(token: string): string { - return Buffer.from(token.split('.')[0], 'base64').toString('ascii'); - } - - private validateLayout(layout: LayoutInput, existingClusterIds: Set): void { - const providedIds = new Set(); - for (const [index, cluster] of layout.entries()) { - if (cluster.shardIds.length === 0) { - throw new BadRequestException(`Cluster at index ${index} is empty.`); - } - if (cluster.id !== undefined) { - if (providedIds.has(cluster.id)) { - throw new BadRequestException(`Duplicate cluster ID ${cluster.id} in layout.`); - } - if (!existingClusterIds.has(cluster.id)) { - throw new BadRequestException(`Cluster ID ${cluster.id} does not exist.`); - } - providedIds.add(cluster.id); - } - } - - const allShardIds = layout.flatMap((c) => c.shardIds); - const uniqueShardIds = new Set(allShardIds); - const totalShards = allShardIds.length; - - if (uniqueShardIds.size !== totalShards) { - throw new BadRequestException('Duplicate shard IDs found in layout.'); - } - - for (const shardId of allShardIds) { - if (shardId < 0 || shardId >= totalShards) { - throw new BadRequestException(`Shard ID ${shardId} is out of range [0, ${totalShards - 1}].`); - } - } - } - - private assignClusterIds(layout: LayoutInput): Array<{ id: number; shardIds: number[] }> { - const sorted = [...layout].sort((a, b) => Math.min(...a.shardIds) - Math.min(...b.shardIds)); - - const usedIds = new Set(); - for (const cluster of sorted) { - if (cluster.id !== undefined) usedIds.add(cluster.id); - } - - let candidate = 0; - return sorted.map((cluster) => { - if (cluster.id !== undefined) { - return { id: cluster.id, shardIds: cluster.shardIds }; - } - while (usedIds.has(candidate)) candidate++; - const id = candidate++; - usedIds.add(id); - return { id, shardIds: cluster.shardIds }; - }); - } - - private async fetchDiscordProfile(token: string): Promise { - try { - return await this.discordService.getCurrentUser(token); - } catch { - return null; - } - } - - async getRecommendedShards(): Promise<{ shards: number }> { - const bot = await this.prismaService.bot.findFirst({ - select: { token: true }, - }); - - if (bot === null) { - throw new NotFoundException('No bot created.'); - } - - return await this.discordService.getGatewayBot(bot.token); - } - - async create(createBotDto: CreateBotZodDto): Promise { - await this.discordService.getGatewayBot(createBotDto.token); - - const { serverName, image, tag } = this.parseDockerImage(createBotDto.dockerImage.image); - - await this.dockerService.verifyImage({ - serverName, - image, - tag, - username: createBotDto.dockerImage.username ?? null, - password: createBotDto.dockerImage.password ?? null, - }); - - try { - const bot = await this.prismaService.bot.create({ - data: { - id: this.getBotId(createBotDto.token), - totalShards: 0, - token: createBotDto.token, - dockerImage: { - create: { - image: image, - tag: tag, - serverName: serverName, - username: createBotDto.dockerImage.username, - password: createBotDto.dockerImage.password, - }, - }, - }, - select: BotService.BOT_SELECT, - }); - - return { - id: bot.id, - shards: bot.totalShards, - layout: bot.clusters.map((c) => ({ id: c.id, shardIds: c.shardIds })), - dockerImage: formatDockerImage(bot.dockerImage), - }; - } catch (e) { - if (e instanceof PrismaClientKnownRequestError && 'P2002' === e.code) { - throw new ConflictException(); - } - throw e; - } - } - - async findOne(): Promise { - const bot = await this.prismaService.bot.findFirst({ - select: { ...BotService.BOT_SELECT, token: true }, - }); - - if (null === bot) { - throw new NotFoundException(); - } - - return { - id: bot.id, - shards: bot.totalShards, - layout: bot.clusters.map((c) => ({ id: c.id, shardIds: c.shardIds })), - dockerImage: formatDockerImage(bot.dockerImage), - discord: await this.fetchDiscordProfile(bot.token), - }; - } - - private buildDockerImageUpdate( - input: UpdateBotZodDto['dockerImage'], - current: { - serverName: string; - image: string; - tag: string; - username: string | null; - } | null, - ): - | { - serverName?: string; - image?: string; - tag?: string; - username?: string | null; - password?: string | null; - } - | undefined { - if (!input) { - return undefined; - } - - const update: { - serverName?: string; - image?: string; - tag?: string; - username?: string | null; - password?: string | null; - } = {}; - - if (input.image !== undefined) { - const { serverName, image, tag } = this.parseDockerImage(input.image); - if (current === null || serverName !== current.serverName || image !== current.image || tag !== current.tag) { - update.serverName = serverName; - update.image = image; - update.tag = tag; - } - } - - if (input.username !== undefined && input.username !== current?.username) { - update.username = input.username; - } - - if (input.password !== undefined) { - update.password = input.password; - } - - return Object.keys(update).length > 0 ? update : undefined; - } - - async update(updateBotDto: UpdateBotZodDto): Promise { - const bot = await this.prismaService.bot.findFirst({ - select: { - ...BotService.BOT_SELECT, - token: true, - }, - }); - - if (null === bot) { - throw new NotFoundException(); - } - - const existingClusterIds = new Set(bot.clusters.map((c) => c.id)); - - const layoutInput: LayoutInput = - updateBotDto.layout ?? bot.clusters.map((c) => ({ id: c.id, shardIds: c.shardIds })); - - this.validateLayout(layoutInput, existingClusterIds); - - const resolvedLayout = this.assignClusterIds(layoutInput); - const totalShards = resolvedLayout.flatMap((c) => c.shardIds).length; - - const sortedLayout = resolvedLayout - .map((c) => ({ id: c.id, shardIds: [...c.shardIds].sort((a, b) => a - b) })) - .sort((a, b) => a.id - b.id); - const oldLayout = bot.clusters - .map((c) => ({ id: c.id, shardIds: [...c.shardIds].sort((a, b) => a - b) })) - .sort((a, b) => a.id - b.id); - const layoutChanged = JSON.stringify(sortedLayout) !== JSON.stringify(oldLayout); - - const newToken = updateBotDto.token !== undefined && updateBotDto.token !== bot.token ? updateBotDto.token : null; - const tokenChanged = newToken !== null; - - if (newToken !== null) { - await this.discordService.getGatewayBot(newToken); - } - - const dockerImageUpdate = this.buildDockerImageUpdate(updateBotDto.dockerImage, bot.dockerImage); - const dockerImageChanged = dockerImageUpdate !== undefined; - - if (dockerImageUpdate !== undefined) { - const currentImage = await this.prismaService.dockerImage.findUniqueOrThrow({ - where: { botId: bot.id }, - select: { serverName: true, image: true, tag: true, username: true, password: true }, - }); - await this.dockerService.verifyImage({ - serverName: dockerImageUpdate.serverName ?? currentImage.serverName, - image: dockerImageUpdate.image ?? currentImage.image, - tag: dockerImageUpdate.tag ?? currentImage.tag, - username: dockerImageUpdate.username !== undefined ? dockerImageUpdate.username : currentImage.username, - password: dockerImageUpdate.password !== undefined ? dockerImageUpdate.password : currentImage.password, - }); - } - - const shouldForceRestart = layoutChanged || tokenChanged || dockerImageChanged; - - const oldStatusById = new Map(bot.clusters.map((c) => [c.id, c.status])); - - for (const cluster of bot.clusters) { - await this.clustersService.remove(cluster.id); - } - - const newBot = await this.prismaService.bot.update({ - data: { - totalShards, - ...(newToken !== null && { token: newToken }), - ...(dockerImageChanged && { - dockerImage: { update: dockerImageUpdate }, - }), - clusters: { - createMany: { - data: resolvedLayout.map((cluster) => ({ - id: cluster.id, - status: shouldForceRestart || oldStatusById.get(cluster.id) !== 'STOPPED' ? 'UPDATING' : 'STOPPED', - shardIds: cluster.shardIds, - })), - }, - }, - }, - where: { - id: bot.id, - }, - select: BotService.BOT_SELECT, - }); - - for (const cluster of newBot.clusters) { - if (cluster.status !== 'STOPPED') { - await this.clustersService.start(cluster.id); - } - } - - return { - id: newBot.id, - shards: newBot.totalShards, - layout: newBot.clusters.map((c) => ({ id: c.id, shardIds: c.shardIds })), - dockerImage: formatDockerImage(newBot.dockerImage), - }; - } - - async remove(): Promise { - const bot = await this.prismaService.bot.findFirst({ - select: BotService.BOT_SELECT, - }); - if (null === bot) { - throw new NotFoundException(); - } - - try { - await this.prismaService.bot.delete({ - where: { - id: bot.id, - }, - }); - } catch (e) { - if (e instanceof PrismaClientKnownRequestError && 'P2025' === e.code) { - throw new NotFoundException(); - } - throw e; - } - - for (const cluster of bot.clusters) { - await this.clustersService.stopByCluster(cluster); - } - - return { - id: bot.id, - shards: bot.totalShards, - layout: bot.clusters.map((c) => ({ id: c.id, shardIds: c.shardIds })), - dockerImage: formatDockerImage(bot.dockerImage), - }; - } -} diff --git a/packages/backend/src/bot/bot.utils.ts b/packages/backend/src/bot/bot.utils.ts deleted file mode 100644 index 7250088..0000000 --- a/packages/backend/src/bot/bot.utils.ts +++ /dev/null @@ -1,17 +0,0 @@ -export function formatDockerImage( - dockerImage: { - serverName: string; - image: string; - tag: string; - username: string | null; - } | null, -) { - if (dockerImage === null) { - throw new Error('Docker image is required but was not found.'); - } - - return { - image: `${dockerImage.serverName}/${dockerImage.image}:${dockerImage.tag}`, - username: dockerImage.username, - }; -} diff --git a/packages/backend/src/bot/dto/create-bot.dto.ts b/packages/backend/src/bot/dto/create-bot.dto.ts deleted file mode 100644 index 9bf35c7..0000000 --- a/packages/backend/src/bot/dto/create-bot.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const CreateBotDockerImageSchema = z.object({ - image: z.string().meta({ - description: - 'The Docker image to use, following this format: hostname/repo/image:tag. (example: hallmaster/discord-bot:tag)', - }), - username: z.string().optional().meta({ - description: 'The username used to authenticate to a remote Docker registry.', - }), - password: z.string().optional().meta({ - description: 'The password used to authenticate to a remote Docker registry.', - }), -}); - -export const CreateBotSchema = z.object({ - token: z.string().meta({ - description: 'The token of the Discord bot.', - }), - dockerImage: CreateBotDockerImageSchema.meta({ - description: 'The information required to pull the Discord bot Docker image.', - }), -}); - -export class CreateBotZodDto extends createZodDto(CreateBotSchema) {} - -export type CreateBotDto = z.infer; diff --git a/packages/backend/src/bot/dto/get-bot.dto.ts b/packages/backend/src/bot/dto/get-bot.dto.ts deleted file mode 100644 index efab6ca..0000000 --- a/packages/backend/src/bot/dto/get-bot.dto.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const GetBotDockerImageSchema = z.object({ - image: z.string().meta({ - description: 'The full Docker image URI (serverName/image:tag).', - }), - username: z.string().nullable().meta({ - description: 'The username used to authenticate to the Docker registry.', - }), -}); - -export const GetBotLayoutClusterSchema = z.object({ - id: z.number().int().nonnegative().meta({ - description: 'The cluster ID (sequential per bot).', - }), - shardIds: z.array(z.number().nonnegative()).meta({ - description: 'The shard IDs assigned to that cluster.', - }), -}); - -export const GetBotDiscordSchema = z.object({ - name: z.string().meta({ - description: 'The Discord username of the bot.', - }), - displayName: z.string().nullable().meta({ - description: 'The Discord display name (global name) of the bot, or null if none is set.', - }), - discriminator: z.string().meta({ - description: 'The Discord discriminator.', - }), - avatarUrl: z.string().nullable().meta({ - description: 'URL of the bot avatar on the Discord CDN, or null if none is set.', - }), - bannerUrl: z.string().nullable().meta({ - description: 'URL of the bot banner on the Discord CDN, or null if none is set.', - }), - accentColor: z.number().int().nullable().meta({ - description: 'The bot profile accent color as an integer (0xRRGGBB), usable as a fallback when no banner is set.', - }), -}); - -export const GetBotSchema = z.object({ - id: z.string().meta({ - description: 'The bot ID.', - }), - shards: z.number().nonnegative().meta({ - description: 'The total number of shards for the bot.', - }), - layout: z.array(GetBotLayoutClusterSchema).meta({ - description: 'The cluster layout. Each element is a cluster with its ID and assigned shard IDs.', - }), - dockerImage: GetBotDockerImageSchema.meta({ - description: 'The Docker image configuration associated with the bot.', - }), - discord: GetBotDiscordSchema.nullable().optional().meta({ - description: "The bot's Discord profile.", - }), -}); - -export class GetBotZodDto extends createZodDto(GetBotSchema) {} - -export type GetBotDto = z.infer; diff --git a/packages/backend/src/bot/dto/get-recommended-shards.dto.ts b/packages/backend/src/bot/dto/get-recommended-shards.dto.ts deleted file mode 100644 index 8b31ced..0000000 --- a/packages/backend/src/bot/dto/get-recommended-shards.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const GetRecommendedShardsSchema = z.object({ - shards: z.int().positive().meta({ - description: 'The recommended number of shards for the bot.', - }), -}); - -export class GetRecommendedShardsZodDto extends createZodDto(GetRecommendedShardsSchema) {} - -export type GetRecommendedShardsDto = z.infer; diff --git a/packages/backend/src/bot/dto/update-bot.dto.ts b/packages/backend/src/bot/dto/update-bot.dto.ts deleted file mode 100644 index 57fca7a..0000000 --- a/packages/backend/src/bot/dto/update-bot.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -import { CreateBotDockerImageSchema, CreateBotSchema } from './create-bot.dto.js'; - -export const LayoutClusterSchema = z.object({ - id: z.number().int().nonnegative().optional().meta({ - description: 'The cluster ID. Omit to create a new cluster (next available ID will be assigned).', - }), - shardIds: z.array(z.number().nonnegative()).min(1).meta({ - description: 'The shard IDs assigned to that cluster.', - }), -}); - -export const UpdateBotSchema = CreateBotSchema.extend({ - layout: z.array(LayoutClusterSchema).min(1).meta({ - description: - 'The cluster layout. Each element references an existing cluster by ID (or omits it to create a new one) along with its shard IDs.', - }), - dockerImage: CreateBotDockerImageSchema.partial().meta({ - description: 'Partial Docker image configuration. Any provided field will be updated.', - }), -}).partial(); - -export class UpdateBotZodDto extends createZodDto(UpdateBotSchema) {} - -export type UpdateBotDto = z.infer; diff --git a/packages/backend/src/clusters/clusters.controller.spec.ts b/packages/backend/src/clusters/clusters.controller.spec.ts deleted file mode 100644 index 0f1c854..0000000 --- a/packages/backend/src/clusters/clusters.controller.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { AuthGuard } from '../auth/guards/jwt.guard.js'; - -import { ClustersController } from './clusters.controller.js'; -import { ClustersService } from './clusters.service.js'; - -describe('ClustersController', () => { - let controller: ClustersController; - const authGuard: DeepMockProxy = mockDeep(); - const clusterService: DeepMockProxy = mockDeep(); - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [ClustersController], - providers: [ - { - provide: ClustersService, - useValue: clusterService, - }, - ], - }) - .overrideGuard(AuthGuard) - .useValue(authGuard) - .compile(); - - controller = module.get(ClustersController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/packages/backend/src/clusters/clusters.controller.ts b/packages/backend/src/clusters/clusters.controller.ts deleted file mode 100644 index ce60991..0000000 --- a/packages/backend/src/clusters/clusters.controller.ts +++ /dev/null @@ -1,320 +0,0 @@ -import { Readable } from 'node:stream'; - -import { - Controller, - Delete, - Get, - HttpCode, - HttpStatus, - MessageEvent, - Param, - Post, - Query, - Sse, - UseGuards, -} from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiBearerAuth, - ApiInternalServerErrorResponse, - ApiNoContentResponse, - ApiNotFoundResponse, - ApiOkResponse, - ApiProduces, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; -import { Observable, timer, exhaustMap, map, from } from 'rxjs'; - -import { AuthGuard } from '../auth/guards/jwt.guard.js'; -import { ApiErrorZodDto } from '../common/dto/api-error.dto.js'; - -import { ClustersService } from './clusters.service.js'; -import { ClusterBulkActionResultZodDto } from './dto/cluster-bulk-action-result.dto.js'; -import { ClusterIdParamZodDto } from './dto/cluster-id-param.dto.js'; -import { GetAggregateStatsZodDto } from './dto/get-aggregate-stats.dto.js'; -import { GetClusterLogsQueryZodDto, GetClusterLogsZodDto } from './dto/get-cluster-logs.dto.js'; -import { GetClusterStatsDto, GetClusterStatsZodDto } from './dto/get-cluster-stats.dto.js'; -import { GetClusterZodDto } from './dto/get-cluster.dto.js'; -import { SseIntervalQueryZodDto } from './dto/sse-interval-query.dto.js'; - -@ApiTags('Clusters') -@Controller('clusters') -@ApiBearerAuth('jwt') -@UseGuards(AuthGuard) -@ApiUnauthorizedResponse({ - type: ApiErrorZodDto, - description: 'This route is protected by an Authorization header that is either not provided or invalid.', -}) -export class ClustersController { - constructor(private readonly clustersService: ClustersService) {} - - @Get() - @ApiOkResponse({ - description: 'The list of clusters.', - type: GetClusterZodDto, - isArray: true, - }) - findAll() { - return this.clustersService.findAll(); - } - - @Sse('stream') - @ApiOkResponse({ - description: 'SSE stream that emits the full list of clusters at a configurable interval (default 5s).', - type: GetClusterZodDto, - isArray: true, - }) - @ApiProduces('text/event-stream') - streamAll(@Query() query: SseIntervalQueryZodDto): Observable { - return timer(0, query.interval * 1000).pipe( - exhaustMap(() => from(this.clustersService.findAll())), - map((clusters) => ({ data: clusters }) as MessageEvent), - ); - } - - @Post('start') - @HttpCode(HttpStatus.OK) - @ApiOkResponse({ - type: ClusterBulkActionResultZodDto, - description: - 'Start every cluster in parallel. Already-running clusters are no-ops. The response lists clusters that succeeded and those that failed, with the failure reason.', - }) - async startAll() { - return await this.clustersService.startAll(); - } - - @Post('stop') - @HttpCode(HttpStatus.OK) - @ApiOkResponse({ - type: ClusterBulkActionResultZodDto, - description: - 'Stop every cluster in parallel. Already-stopped clusters are no-ops. The response lists clusters that succeeded and those that failed, with the failure reason.', - }) - async stopAll() { - return await this.clustersService.stopAll(); - } - - @Post('restart') - @HttpCode(HttpStatus.OK) - @ApiOkResponse({ - type: ClusterBulkActionResultZodDto, - description: - 'Restart every cluster in parallel (stop + start). Stopped clusters are simply started. The response lists clusters that succeeded and those that failed, with the failure reason.', - }) - async restartAll() { - return await this.clustersService.restartAll(); - } - - @Sse('stats/stream') - @ApiOkResponse({ - description: - 'SSE stream that emits the stats of every running cluster at a configurable interval (default 5s). Clusters whose stats failed to fetch are omitted.', - type: GetAggregateStatsZodDto, - }) - @ApiProduces('text/event-stream') - streamAggregateStats(@Query() query: SseIntervalQueryZodDto): Observable { - return this.clustersService - .streamAggregateStats(query.interval) - .pipe(map((stats) => ({ data: stats }) as MessageEvent)); - } - - @Get(':id') - @ApiOkResponse({ - description: 'The cluster based on the given ID.', - type: GetClusterZodDto, - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - findOne(@Param() { id }: ClusterIdParamZodDto) { - return this.clustersService.findOne(id); - } - - @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) - @ApiNoContentResponse({ - description: 'The cluster with the given ID has been deleted.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'Unable to stop or remove the cluster container.', - }) - async remove(@Param() { id }: ClusterIdParamZodDto) { - await this.clustersService.remove(id); - } - - @Post(':id/start') - @HttpCode(HttpStatus.NO_CONTENT) - @ApiNoContentResponse({ - description: 'The cluster with the given ID is starting.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The cluster ID is unresolved, or the bot has no Docker image configured.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'Unable to provision or start the cluster container.', - }) - @ApiInternalServerErrorResponse({ - type: ApiErrorZodDto, - description: 'An unexpected error occurred while starting the cluster.', - }) - async start(@Param() { id }: ClusterIdParamZodDto) { - await this.clustersService.start(id); - } - - @Post(':id/stop') - @HttpCode(HttpStatus.NO_CONTENT) - @ApiNoContentResponse({ - description: 'The cluster with the given ID has been stopped.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'Unable to stop the cluster container.', - }) - async stop(@Param() { id }: ClusterIdParamZodDto) { - await this.clustersService.stop(id); - } - - @Post(':id/restart') - @HttpCode(HttpStatus.NO_CONTENT) - @ApiNoContentResponse({ - description: 'The cluster with the given ID is restarting.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The cluster ID is unresolved, or the bot has no Docker image configured.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'Unable to restart the cluster container.', - }) - @ApiInternalServerErrorResponse({ - type: ApiErrorZodDto, - description: 'An unexpected error occurred while restarting the cluster.', - }) - async restart(@Param() { id }: ClusterIdParamZodDto) { - await this.clustersService.restart(id); - } - - @Get(':id/logs') - @ApiProduces('text/plain') - @ApiOkResponse({ - type: GetClusterLogsZodDto, - description: 'The logs of the cluster.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The cluster has no bound container.', - }) - async getLogs(@Param() { id }: ClusterIdParamZodDto, @Query() query: GetClusterLogsQueryZodDto) { - return await this.clustersService.logs( - id, - query.since ? new Date(query.since) : undefined, - query.until ? new Date(query.until) : undefined, - query.tail, - ); - } - - @Sse(':id/logs/stream') - @ApiProduces('text/event-stream') - @UseGuards(AuthGuard) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The cluster has no bound container.', - }) - streamLogs(@Param() { id }: ClusterIdParamZodDto): Observable { - return new Observable((subscriber) => { - let stream: Readable | undefined; - - this.clustersService - .streamLogs(id) - .then((s) => { - stream = s; - - stream.on('data', (log: { content: string; stream: string; timestamp?: string }) => { - subscriber.next({ data: log } as MessageEvent); - }); - - stream.on('end', () => subscriber.complete()); - stream.on('error', (err) => subscriber.error(err)); - }) - .catch((err) => subscriber.error(err)); - - return () => { - stream?.destroy(); - }; - }); - } - - @Get(':id/stats') - @ApiOkResponse({ - type: GetClusterStatsZodDto, - description: 'The stats of the cluster.', - }) - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The requested cluster has no bound container or its not running.', - }) - async getStats(@Param() { id }: ClusterIdParamZodDto) { - return await this.clustersService.stats(id); - } - - @Sse(':id/stats/stream') - @ApiOkResponse({ - description: 'SSE stream that emits the stats of the cluster.', - type: GetClusterStatsZodDto, - }) - @ApiProduces('text/event-stream') - @ApiNotFoundResponse({ - type: ApiErrorZodDto, - description: 'The ID points to an unresolved cluster.', - }) - @ApiBadRequestResponse({ - type: ApiErrorZodDto, - description: 'The requested cluster has no bound container or its not running.', - }) - streamStats(@Param() { id }: ClusterIdParamZodDto): Observable { - return new Observable((subscriber) => { - let stream: Readable | undefined; - - this.clustersService - .streamStats(id) - .then((s) => { - stream = s; - - stream.on('data', (stats: GetClusterStatsDto) => subscriber.next({ data: stats } as MessageEvent)); - stream.on('end', () => subscriber.complete()); - stream.on('error', (err) => subscriber.error(err)); - }) - .catch((err) => subscriber.error(err)); - - return () => { - stream?.destroy(); - }; - }); - } -} diff --git a/packages/backend/src/clusters/clusters.module.ts b/packages/backend/src/clusters/clusters.module.ts deleted file mode 100644 index aacf456..0000000 --- a/packages/backend/src/clusters/clusters.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { AuthModule } from '../auth/auth.module.js'; -import { DockerModule } from '../docker/docker.module.js'; -import { PrismaModule } from '../prisma/prisma.module.js'; - -import { ClustersController } from './clusters.controller.js'; -import { ClustersService } from './clusters.service.js'; - -@Module({ - imports: [AuthModule, PrismaModule, DockerModule], - controllers: [ClustersController], - providers: [ClustersService], - exports: [ClustersService], -}) -export class ClustersModule {} diff --git a/packages/backend/src/clusters/clusters.service.spec.ts b/packages/backend/src/clusters/clusters.service.spec.ts deleted file mode 100644 index 1f38947..0000000 --- a/packages/backend/src/clusters/clusters.service.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { DockerService } from '../docker/docker.service.js'; -import { PrismaService } from '../prisma/prisma.service.js'; - -import { ClustersService } from './clusters.service.js'; - -describe('ClustersService', () => { - let service: ClustersService; - const prismaService: DeepMockProxy = mockDeep(); - const dockerService: DeepMockProxy = mockDeep(); - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - ClustersService, - { - provide: PrismaService, - useValue: prismaService, - }, - { - provide: DockerService, - useValue: dockerService, - }, - ], - }).compile(); - - service = module.get(ClustersService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/packages/backend/src/clusters/clusters.service.ts b/packages/backend/src/clusters/clusters.service.ts deleted file mode 100644 index 3531cf8..0000000 --- a/packages/backend/src/clusters/clusters.service.ts +++ /dev/null @@ -1,328 +0,0 @@ -import type { Readable } from 'node:stream'; - -import type { Cluster } from '@hallmaster/prisma-client'; -import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; -import { Observable } from 'rxjs'; - -import { DockerService } from '../docker/docker.service.js'; -import { PrismaService } from '../prisma/prisma.service.js'; - -import { ClusterBulkActionResultDto } from './dto/cluster-bulk-action-result.dto.js'; -import { GetAggregateStatsDto } from './dto/get-aggregate-stats.dto.js'; -import { GetClusterStatsDto } from './dto/get-cluster-stats.dto.js'; - -@Injectable() -export class ClustersService { - constructor( - private readonly prismaService: PrismaService, - private readonly dockerService: DockerService, - ) {} - - private async getFullResource(id: number) { - const resource = await this.prismaService.cluster.findFirst({ - select: { - bot: true, - containerId: true, - id: true, - shardIds: true, - status: true, - botId: true, - }, - where: { id }, - }); - - if (null === resource) { - throw new NotFoundException(); - } - - return resource; - } - - async findAll() { - const clusters = await this.prismaService.cluster.findMany({ - select: { - id: true, - shardIds: true, - status: true, - botId: true, - containerId: true, - }, - orderBy: { id: 'asc' }, - }); - - return await Promise.all( - clusters.map(async (c) => ({ - id: c.id, - shardIds: c.shardIds, - status: await this.dockerService.reconcileClusterStatus(c), - })), - ); - } - - async findOne(id: number) { - const resource = await this.prismaService.cluster.findFirst({ - select: { - id: true, - shardIds: true, - status: true, - botId: true, - containerId: true, - }, - where: { id }, - }); - - if (null === resource) { - throw new NotFoundException(); - } - - return { - id: resource.id, - shardIds: resource.shardIds, - status: await this.dockerService.reconcileClusterStatus(resource), - }; - } - - async remove(id: number) { - const resource = await this.getFullResource(id); - - await this.dockerService.remove({ - id: resource.id, - containerId: resource.containerId, - botId: resource.botId, - shardIds: resource.shardIds, - status: resource.status, - }); - } - - async start(id: number) { - const resource = await this.getFullResource(id); - - await this.dockerService.start(resource.bot, { - id: resource.id, - containerId: resource.containerId, - botId: resource.botId, - shardIds: resource.shardIds, - status: resource.status, - }); - } - - async stop(id: number) { - const resource = await this.getFullResource(id); - - await this.dockerService.stop({ - id: resource.id, - containerId: resource.containerId, - botId: resource.botId, - shardIds: resource.shardIds, - status: resource.status, - }); - } - - async stopByCluster(cluster: Cluster) { - await this.dockerService.stop(cluster); - } - - async restart(id: number) { - const resource = await this.getFullResource(id); - - await this.dockerService.restart(resource.bot, { - id: resource.id, - containerId: resource.containerId, - botId: resource.botId, - shardIds: resource.shardIds, - status: resource.status, - }); - } - - async streamLogs(id: number, tail: number | 'all' = 0): Promise { - const resource = await this.getFullResource(id); - if (null === resource.containerId) { - throw new BadRequestException('The cluster has no container ID.'); - } - return await this.dockerService.streamContainerLogs(resource.containerId, tail); - } - - async logs(id: number, since?: Date, until?: Date, tail?: number | 'all') { - const resource = await this.getFullResource(id); - - if (null === resource.containerId) { - throw new BadRequestException('The cluster has no container ID.'); - } - - return await this.dockerService.getContainerLogs(resource.containerId, since, until, tail); - } - - async stats(id: number) { - const resource = await this.getFullResource(id); - - if (null === resource.containerId) { - throw new BadRequestException('The cluster has no container ID.'); - } - - if (resource.status !== 'RUNNING') { - throw new BadRequestException('Unable to gather stats on a non-running cluster.'); - } - - return await this.dockerService.getContainerStats(resource.containerId); - } - - async streamStats(id: number): Promise { - const resource = await this.getFullResource(id); - - if (null === resource.containerId) { - throw new BadRequestException('The cluster has no container ID.'); - } - - if (resource.status !== 'RUNNING') { - throw new BadRequestException('Unable to gather stats on a non-running cluster.'); - } - - return await this.dockerService.streamContainerStats(resource.containerId); - } - - async startAll(): Promise { - return await this.runBulkAction((id) => this.start(id)); - } - - async stopAll(): Promise { - return await this.runBulkAction((id) => this.stop(id)); - } - - async restartAll(): Promise { - return await this.runBulkAction((id) => this.restart(id)); - } - - private async runBulkAction(action: (id: number) => Promise): Promise { - const clusters = await this.prismaService.cluster.findMany({ - select: { id: true }, - orderBy: { id: 'asc' }, - }); - - const settled = await Promise.allSettled(clusters.map((c) => action(c.id))); - - const succeeded: number[] = []; - const failed: Array<{ id: number; reason: string }> = []; - - settled.forEach((result, index) => { - const id = clusters[index].id; - if (result.status === 'fulfilled') { - succeeded.push(id); - } else { - const reason = result.reason instanceof Error ? result.reason.message : String(result.reason); - failed.push({ id, reason }); - } - }); - - return { succeeded, failed }; - } - - async aggregateStats(): Promise { - const clusters = await this.prismaService.cluster.findMany({ - where: { status: 'RUNNING', containerId: { not: null } }, - select: { id: true, containerId: true }, - }); - - const runnable = clusters.filter((c): c is { id: number; containerId: string } => c.containerId !== null); - - const results = await Promise.allSettled( - runnable.map(async (c) => ({ - id: c.id, - stats: await this.dockerService.getContainerStats(c.containerId), - })), - ); - - const aggregate: GetAggregateStatsDto = {}; - for (const result of results) { - if (result.status === 'fulfilled') { - aggregate[result.value.id] = result.value.stats; - } - } - - return aggregate; - } - - streamAggregateStats(intervalSeconds: number): Observable { - return new Observable((subscriber) => { - const streams = new Map(); - const latest = new Map(); - let closed = false; - - const removeStream = (id: number) => { - streams.get(id)?.destroy(); - streams.delete(id); - latest.delete(id); - }; - - const addStream = async (id: number, containerId: string) => { - if (streams.has(id)) { - return; - } - - try { - const stream = await this.dockerService.streamContainerStats(containerId); - - if (closed) { - stream.destroy(); - return; - } - - streams.set(id, stream); - stream.on('data', (stats: GetClusterStatsDto) => latest.set(id, stats)); - stream.on('error', () => removeStream(id)); - stream.on('end', () => removeStream(id)); - } catch { - // the next reconcile tick will retry opening this stream - } - }; - - const reconcile = async () => { - let clusters: Array<{ id: number; containerId: string | null }>; - try { - clusters = await this.prismaService.cluster.findMany({ - where: { status: 'RUNNING', containerId: { not: null } }, - select: { id: true, containerId: true }, - }); - } catch { - return; - } - - const running = new Map( - clusters - .filter((c): c is { id: number; containerId: string } => c.containerId !== null) - .map((c) => [c.id, c.containerId] as const), - ); - - for (const id of [...streams.keys()]) { - if (!running.has(id)) { - removeStream(id); - } - } - - for (const [id, containerId] of running) { - void addStream(id, containerId); - } - }; - - const emit = () => { - const snapshot: GetAggregateStatsDto = {}; - for (const [id, stats] of latest) { - snapshot[id] = stats; - } - subscriber.next(snapshot); - }; - - void reconcile(); - const reconcileTimer = setInterval(() => void reconcile(), intervalSeconds * 1000); - const emitTimer = setInterval(emit, intervalSeconds * 1000); - - return () => { - closed = true; - clearInterval(reconcileTimer); - clearInterval(emitTimer); - for (const id of [...streams.keys()]) { - removeStream(id); - } - }; - }); - } -} diff --git a/packages/backend/src/clusters/dto/cluster-bulk-action-result.dto.ts b/packages/backend/src/clusters/dto/cluster-bulk-action-result.dto.ts deleted file mode 100644 index a8a300b..0000000 --- a/packages/backend/src/clusters/dto/cluster-bulk-action-result.dto.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const ClusterBulkActionResultSchema = z.object({ - succeeded: z.array(z.number().int().nonnegative()).meta({ - description: 'IDs of clusters where the action completed successfully.', - }), - failed: z - .array( - z.object({ - id: z.number().int().nonnegative(), - reason: z.string(), - }), - ) - .meta({ - description: 'Clusters where the action failed, along with the failure reason.', - }), -}); - -export class ClusterBulkActionResultZodDto extends createZodDto(ClusterBulkActionResultSchema) {} - -export type ClusterBulkActionResultDto = z.infer; diff --git a/packages/backend/src/clusters/dto/cluster-id-param.dto.ts b/packages/backend/src/clusters/dto/cluster-id-param.dto.ts deleted file mode 100644 index 7f0dc72..0000000 --- a/packages/backend/src/clusters/dto/cluster-id-param.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const ClusterIdParamSchema = z.object({ - id: z.coerce.number().int().nonnegative().meta({ - description: 'The cluster ID (sequential per bot, starting at 0).', - }), -}); - -export class ClusterIdParamZodDto extends createZodDto(ClusterIdParamSchema) {} - -export type ClusterIdParamDto = z.infer; diff --git a/packages/backend/src/clusters/dto/get-aggregate-stats.dto.ts b/packages/backend/src/clusters/dto/get-aggregate-stats.dto.ts deleted file mode 100644 index 6ce1e14..0000000 --- a/packages/backend/src/clusters/dto/get-aggregate-stats.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -import { GetClusterStatsSchema } from './get-cluster-stats.dto.js'; - -export const GetAggregateStatsSchema = z.record(z.coerce.number().int().nonnegative(), GetClusterStatsSchema).meta({ - description: 'Stats of every running cluster, keyed by cluster ID. Clusters whose stats failed to fetch are omitted.', -}); - -export class GetAggregateStatsZodDto extends createZodDto(GetAggregateStatsSchema) {} - -export type GetAggregateStatsDto = z.infer; diff --git a/packages/backend/src/clusters/dto/get-cluster-logs.dto.ts b/packages/backend/src/clusters/dto/get-cluster-logs.dto.ts deleted file mode 100644 index 8fc2b42..0000000 --- a/packages/backend/src/clusters/dto/get-cluster-logs.dto.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const GetClusterLogsQuerySchema = z.object({ - since: z.iso.datetime().optional().meta({ - description: 'At which point in time does the logs collection start.', - }), - until: z.iso.datetime().optional().meta({ - description: 'At which point in time does the logs collection end.', - }), - tail: z.coerce.number().positive().min(1).or(z.literal('all')).optional().default('all').meta({ - description: "How many logs to fetch, from latest to oldest. 'all' gets all the logs.", - }), -}); - -export class GetClusterLogsQueryZodDto extends createZodDto(GetClusterLogsQuerySchema) {} - -export type GetClusterLogsQueryDto = z.infer; - -export const GetClusterLogSchema = z.object({ - content: z.string().meta({ - description: 'The content of a log line.', - }), - stream: z.union([z.literal('STDOUT'), z.literal('STDERR')]).meta({ - description: 'The stream that the log was published into.', - }), - timestamp: z.string().optional().meta({ - description: 'RFC3339Nano timestamp emitted by the Docker daemon for that log line.', - }), -}); - -export const GetClusterLogsSchema = z.array(GetClusterLogSchema).meta({ - description: 'Logs (line by line) of a container.', -}); - -export class GetClusterLogsZodDto extends createZodDto(GetClusterLogsSchema) {} - -export type GetClusterLogsDto = z.infer; diff --git a/packages/backend/src/clusters/dto/get-cluster-stats.dto.ts b/packages/backend/src/clusters/dto/get-cluster-stats.dto.ts deleted file mode 100644 index 0f921dd..0000000 --- a/packages/backend/src/clusters/dto/get-cluster-stats.dto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -const GetClusterStatsProcessesUsageSchema = z.object({ - usage: z.number().meta({ - description: 'The number of processes running in the cluster.', - }), - percentage: z.number().meta({ - description: 'The number of running processes in the cluster over the limit.', - }), -}); - -const GetClusterStatsMemoryUsageSchema = z.object({ - usage: z.number().meta({ - description: 'The number of bytes used in memory.', - }), - percentage: z.number().meta({ - description: 'The percentage of the available memory used by the cluster.', - }), -}); - -const GetClusterStatsNetworkSchema = z.object({ - interface: z.string().meta({ - description: 'The networking interface name.', - }), - transmitted: z.number().meta({ - description: 'The number of bytes transmitted by the interface.', - }), - received: z.number().meta({ - description: 'The number of bytes received by the interface.', - }), -}); - -const GetClusterStatsDiskSchema = z.object({ - read: z.number().nullable().meta({ - description: 'The number of bytes read from disk. The value is null if no disk interface is detected.', - }), - write: z.number().nullable().meta({ - description: 'The number of bytes written to disk. The value is null if no disk interface is detected.', - }), -}); - -export const GetClusterStatsSchema = z.object({ - cpuPercentage: z.number().meta({ - description: 'The percentage of CPU utilization.', - }), - processes: GetClusterStatsProcessesUsageSchema, - memory: GetClusterStatsMemoryUsageSchema, - disk: GetClusterStatsDiskSchema, - networks: z.array(GetClusterStatsNetworkSchema).meta({ - description: 'The usage of each detected networking interface in the cluster.', - }), -}); - -export class GetClusterStatsZodDto extends createZodDto(GetClusterStatsSchema) {} - -export type GetClusterStatsDto = z.infer; diff --git a/packages/backend/src/clusters/dto/get-cluster.dto.ts b/packages/backend/src/clusters/dto/get-cluster.dto.ts deleted file mode 100644 index ae5f4b0..0000000 --- a/packages/backend/src/clusters/dto/get-cluster.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const GetClusterSchema = z.object({ - id: z.number().int().nonnegative().meta({ - description: 'The cluster ID (sequential per bot, starting at 0).', - }), - shardIds: z.array(z.number()).meta({ - description: 'The shard IDs associated to that cluster.', - }), - status: z.enum(['STARTING', 'RUNNING', 'STOPPED', 'ERROR']).meta({ - description: 'The current status of the cluster.', - }), -}); - -export class GetClusterZodDto extends createZodDto(GetClusterSchema) {} - -export type GetClusterDto = z.infer; diff --git a/packages/backend/src/clusters/dto/sse-interval-query.dto.ts b/packages/backend/src/clusters/dto/sse-interval-query.dto.ts deleted file mode 100644 index cfb88b6..0000000 --- a/packages/backend/src/clusters/dto/sse-interval-query.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const SseIntervalQuerySchema = z.object({ - interval: z.coerce.number().positive().min(1).max(120).optional().default(5).meta({ - description: 'Refresh interval in seconds (1–120). Defaults to 5.', - }), -}); - -export class SseIntervalQueryZodDto extends createZodDto(SseIntervalQuerySchema) {} diff --git a/packages/backend/src/common/dto/api-error.dto.ts b/packages/backend/src/common/dto/api-error.dto.ts deleted file mode 100644 index 004fda2..0000000 --- a/packages/backend/src/common/dto/api-error.dto.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import { z } from 'zod'; - -export const ApiErrorIssueSchema = z.object({ - path: z.array(z.union([z.string(), z.number()])).meta({ - description: 'Path to the field that failed validation (e.g. ["interval"]).', - }), - message: z.string().meta({ - description: 'A human-readable reason the field is invalid (e.g. "Expected number").', - }), - code: z.string().meta({ - description: 'The Zod issue code (e.g. "invalid_type").', - }), -}); - -export const ApiErrorSchema = z.object({ - statusCode: z.number().int().meta({ - description: 'The HTTP status code of the error.', - }), - error: z.string().meta({ - description: 'The short reason phrase associated with the status code (e.g. "Bad Request").', - }), - message: z.string().meta({ - description: 'A human-readable description of what went wrong.', - }), - issues: z.array(ApiErrorIssueSchema).optional().meta({ - description: 'Field-level validation errors. Present only when the request failed schema validation.', - }), -}); - -export class ApiErrorZodDto extends createZodDto(ApiErrorSchema) {} - -export type ApiErrorDto = z.infer; diff --git a/packages/backend/src/common/filters/all-exceptions.filter.spec.ts b/packages/backend/src/common/filters/all-exceptions.filter.spec.ts deleted file mode 100644 index 7166007..0000000 --- a/packages/backend/src/common/filters/all-exceptions.filter.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { ArgumentsHost } from '@nestjs/common'; -import { BadRequestException, NotFoundException } from '@nestjs/common'; - -import { AllExceptionsFilter } from './all-exceptions.filter.js'; - -describe('AllExceptionsFilter', () => { - const filter = new AllExceptionsFilter(); - - const capture = (exception: unknown): { status: number; body: unknown } => { - let status = 0; - let body: unknown; - const reply = { - status: (code: number) => { - status = code; - return { send: (payload: unknown) => (body = payload) }; - }, - }; - const host = { - switchToHttp: () => ({ getResponse: () => reply }), - } as unknown as ArgumentsHost; - - filter.catch(exception, host); - return { status, body }; - }; - - it('normalizes a standard HttpException to { statusCode, error, message }', () => { - const { status, body } = capture(new NotFoundException()); - - expect(status).toBe(404); - expect(body).toEqual({ statusCode: 404, error: 'Not Found', message: 'Not Found' }); - }); - - it('keeps the custom message of an HttpException thrown with a string', () => { - const { body } = capture(new BadRequestException('The cluster has no container ID.')); - - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: 'The cluster has no container ID.', - }); - }); - - it('exposes Zod validation issues under `issues`', () => { - // nestjs-zod throws a BadRequestException whose payload has `errors` and no `error`. - const exception = new BadRequestException({ - statusCode: 400, - message: 'Validation failed', - errors: [{ path: ['interval'], message: 'Expected number', code: 'invalid_type' }], - }); - - const { status, body } = capture(exception); - - expect(status).toBe(400); - expect(body).toEqual({ - statusCode: 400, - error: 'Bad Request', - message: 'Validation failed', - issues: [{ path: ['interval'], message: 'Expected number', code: 'invalid_type' }], - }); - }); - - it('omits `issues` entirely for non-validation errors', () => { - const { body } = capture(new NotFoundException()); - - expect(body).not.toHaveProperty('issues'); - }); - - it('maps an unknown (non-HTTP) error to a generic 500 without leaking internals', () => { - const { status, body } = capture(new Error('bimbamboom database exploded')); - - expect(status).toBe(500); - expect(body).toEqual({ - statusCode: 500, - error: 'Internal Server Error', - message: 'Internal server error', - }); - }); -}); diff --git a/packages/backend/src/common/filters/all-exceptions.filter.ts b/packages/backend/src/common/filters/all-exceptions.filter.ts deleted file mode 100644 index af94605..0000000 --- a/packages/backend/src/common/filters/all-exceptions.filter.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus, Logger } from '@nestjs/common'; -import { FastifyReply } from 'fastify'; - -import { ApiErrorDto } from '../dto/api-error.dto.js'; - -/** - * Normalizes every thrown exception into a single {@link ApiErrorDto} shape - * (`{ statusCode, error, message }`), so clients get one predictable, typed - * error body regardless of the source (Nest `HttpException`, Zod validation - * failures, or unexpected errors). - */ -@Catch() -export class AllExceptionsFilter implements ExceptionFilter { - private readonly logger = new Logger(AllExceptionsFilter.name); - - catch(exception: unknown, host: ArgumentsHost): void { - const reply = host.switchToHttp().getResponse(); - const body = AllExceptionsFilter.normalize(exception); - - if (body.statusCode >= 500) { - this.logger.error(`${body.statusCode} ${body.message}`, exception instanceof Error ? exception.stack : undefined); - } - - reply.status(body.statusCode).send(body); - } - - private static normalize(exception: unknown): ApiErrorDto { - if (exception instanceof HttpException) { - const statusCode = exception.getStatus(); - const response = exception.getResponse(); - const issues = AllExceptionsFilter.extractIssues(response); - - return { - statusCode, - error: AllExceptionsFilter.extractError(response, statusCode), - message: AllExceptionsFilter.extractMessage(response, exception.message), - ...(issues && { issues }), - }; - } - - return { - statusCode: HttpStatus.INTERNAL_SERVER_ERROR, - error: 'Internal Server Error', - message: 'Internal server error', - }; - } - - // Zod validation failures - private static extractIssues(response: string | object): ApiErrorDto['issues'] { - if (typeof response !== 'object') { - return undefined; - } - - const errors: unknown = (response as { errors?: unknown }).errors; - if (!Array.isArray(errors) || errors.length === 0) { - return undefined; - } - - const issues = (errors as unknown[]) - .filter((issue): issue is Record => typeof issue === 'object' && issue !== null) - .map((issue) => ({ - path: Array.isArray(issue.path) - ? (issue.path as unknown[]).filter( - (p): p is string | number => typeof p === 'string' || typeof p === 'number', - ) - : [], - message: typeof issue.message === 'string' ? issue.message : 'Invalid value', - code: typeof issue.code === 'string' ? issue.code : 'invalid', - })); - - return issues.length > 0 ? issues : undefined; - } - - private static extractMessage(response: string | object, fallback: string): string { - if (typeof response === 'string') { - return response; - } - - const message = (response as { message?: unknown }).message; - if (Array.isArray(message)) { - return message.join('; '); - } - if (typeof message === 'string') { - return message; - } - - return fallback; - } - - private static extractError(response: string | object, statusCode: number): string { - if (typeof response === 'object') { - const error = (response as { error?: unknown }).error; - if (typeof error === 'string') { - return error; - } - } - - return AllExceptionsFilter.reasonPhrase(statusCode); - } - - private static reasonPhrase(statusCode: number): string { - const name: unknown = (HttpStatus as Record)[statusCode]; - if (typeof name !== 'string') { - return 'Error'; - } - - return name - .split('_') - .map((word) => word.charAt(0) + word.slice(1).toLowerCase()) - .join(' '); - } -} diff --git a/packages/backend/src/controllers/auth.controller.ts b/packages/backend/src/controllers/auth.controller.ts new file mode 100644 index 0000000..fb730fc --- /dev/null +++ b/packages/backend/src/controllers/auth.controller.ts @@ -0,0 +1,66 @@ +import { UserSchema, UserTokenResponseSchema } from "#schemas/auth.schema.js"; +import { AuthService } from "#services/auth.service"; +import { Hono } from "hono"; +import { describeRoute, resolver, validator } from "hono-openapi"; + +const tags = ["Authentication"]; + +const authController = new Hono() + .post( + "/register", + describeRoute({ + tags, + description: "Register a new account", + responses: { + 201: { + description: "Created", + content: { + "application/json": { schema: resolver(UserTokenResponseSchema) }, + }, + }, + 409: { + description: "Conflict", + content: { + "text/plain": { example: "A user is already registered" }, + }, + }, + }, + }), + validator("json", UserSchema), + async (c) => { + const data = c.req.valid("json"); + const token = await AuthService.register(data); + + return c.json(token, 201); + }, + ) + .post( + "/login", + describeRoute({ + tags, + description: "Log in the user", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(UserTokenResponseSchema) }, + }, + }, + 401: { + description: "Unauthorized", + content: { + "text/plain": { example: "Invalid username or password" }, + }, + }, + }, + }), + validator("json", UserSchema), + async (c) => { + const data = c.req.valid("json"); + const token = await AuthService.login(data); + + return c.json(token); + }, + ); + +export default authController; diff --git a/packages/backend/src/controllers/bot.controller.ts b/packages/backend/src/controllers/bot.controller.ts new file mode 100644 index 0000000..bc2da9c --- /dev/null +++ b/packages/backend/src/controllers/bot.controller.ts @@ -0,0 +1,287 @@ +import { BotSchema, ContainerImageSchema, CreateBotSchema } from "#schemas/bot.schema"; +import { DiscordUserSchema } from "#schemas/discord.schema"; +import BotService from "#services/bot.service"; +import { Hono } from "hono"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import authMiddlewares from "../middlewares/auth.middlware"; + +const tags = ["Bot"]; + +const botController = new Hono() + .use(...authMiddlewares) + .post( + "/", + describeRoute({ + tags, + description: "Register a bot", + responses: { + 201: { + description: "Created", + content: { + "application/json": { schema: resolver(BotSchema) }, + }, + }, + 409: { + description: "Conflict", + content: { + "text/plain": { + example: "A bot is already registered", + }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + examples: { + "Unreachable or timed out": { value: "Discord API is unreachable or timed out" }, + "Invalid token": { value: "Invalid Discord bot token" }, + Unexpected: { value: "Discord API returned an unexpected response body" }, + }, + }, + }, + }, + }, + }), + validator("json", CreateBotSchema), + async (c) => { + const data = c.req.valid("json"); + const bot = await BotService.create(data); + + return c.json(bot, 201); + }, + ) + .get( + "/", + describeRoute({ + tags, + description: "Get the registered bot", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(BotSchema) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Bot not found" }, + }, + }, + }, + }), + async (c) => { + const bot = await BotService.get(); + + return c.json(bot); + }, + ) + .put( + "/", + describeRoute({ + tags, + description: "Update bot", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(BotSchema) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Bot not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Invalid response from the Discord API", + }, + }, + }, + }, + }), + validator("json", CreateBotSchema), + async (c) => { + const data = c.req.valid("json"); + const bot = await BotService.update(data); + + return c.json(bot, 200); + }, + ) + .delete( + "/", + describeRoute({ + tags, + description: "Delete bot", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(BotSchema) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Bot not found" }, + }, + }, + }, + }), + async (c) => { + const bot = await BotService.delete(); + + return c.json(bot, 200); + }, + ) + .get( + "/profile", + describeRoute({ + tags, + description: "Get bot Discord profile", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(DiscordUserSchema) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Bot not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + examples: { + "Unreachable or timed out": { value: "Discord API is unreachable or timed out" }, + "Invalid token": { value: "Invalid Discord bot token" }, + Unexpected: { value: "Discord API returned an unexpected response body" }, + }, + }, + }, + }, + }, + }), + async (c) => { + const profile = await BotService.discordProfile(); + + return c.json(profile, 200); + }, + ) + .get( + "/container-image", + describeRoute({ + tags, + description: "Get the registered container image", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(ContainerImageSchema.omit({ password: true })) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Container image not found" }, + }, + }, + }, + }), + async (c) => { + const containerImage = await BotService.getContainerImage(); + + return c.json(containerImage, 200); + }, + ) + .post( + "/container-image", + describeRoute({ + tags, + description: "Register a container image", + responses: { + 201: { + description: "Created", + content: { + "application/json": { schema: resolver(ContainerImageSchema.omit({ password: true })) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Bot not found" }, + }, + }, + 409: { + description: "Conflict", + content: { + "text/plain": { + example: "A container image is already registered", + }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, + }, + }), + validator("json", ContainerImageSchema), + async (c) => { + const data = c.req.valid("json"); + const containerImage = await BotService.createContainerImage(data); + + return c.json(containerImage, 201); + }, + ) + .put( + "/container-image", + describeRoute({ + tags, + description: "Update container image", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(ContainerImageSchema.omit({ password: true })) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { example: "Container image not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, + }, + }), + validator("json", ContainerImageSchema), + async (c) => { + const data = c.req.valid("json"); + const containerImage = await BotService.updateContainerImage(data); + + return c.json(containerImage, 200); + }, + ); + +export default botController; diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts new file mode 100644 index 0000000..0c0c887 --- /dev/null +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -0,0 +1,325 @@ +import authMiddlewares from "#middlewares/auth.middlware"; +import { + ClusterIdParamSchema, + ClusterLogsQuerySchema, + ClusterLogsSchema, + ClustersBulkActionResultSchema, + ClusterSchema, + ClustersSchema, + ClustersStatsSchema, + ClusterStatsSchema, + SseIntervalQuerySchema, + UpdateClustersSchema, +} from "#schemas/cluster.schema"; +import ClusterService from "#services/cluster.service"; +import { Hono } from "hono"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { streamSSE } from "hono/streaming"; + +const tags = ["Clusters"]; + +const clusterController = new Hono() + .use(...authMiddlewares) + .get( + "/", + describeRoute({ + tags, + description: "Get clusters", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(ClustersSchema) }, + }, + }, + 404: { + description: "No cluster found", + content: { + "application/json": { example: "Clusters not found" }, + }, + }, + }, + }), + async (c) => { + const clusters = await ClusterService.getAll(); + + return c.json(clusters, 200); + }, + ) + .put( + "/", + describeRoute({ + tags, + description: "Update cluster layout", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(ClustersSchema) }, + }, + }, + 404: { + description: "Not Found", + content: { + "text/plain": { + examples: { + "Bot not found": { value: "Bot not found" }, + "Container image not found": { value: "Container image not found" }, + }, + }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, + }, + }), + validator("json", UpdateClustersSchema), + async (c) => { + const layout = c.req.valid("json"); + const clusters = await ClusterService.updateAll(layout); + + return c.json(clusters, 200); + }, + ) + .get( + "/sse", + describeRoute({ + tags, + description: "Steam clusters", + responses: { + 200: { + description: "Server-Sent Events listing clusters", + content: { + "text/event-stream": { schema: resolver(ClustersSchema) }, + }, + }, + }, + }), + validator("query", SseIntervalQuerySchema), + async (c) => { + const query = c.req.valid("query"); + + return streamSSE(c, async (stream) => { + while (!stream.aborted) { + const clusters = await ClusterService.getAll(); + await stream.writeSSE({ + data: JSON.stringify(clusters), + }); + await stream.sleep(query.interval * 1000); + } + }); + }, + ) + .get( + "/stats/sse", + describeRoute({ + tags, + description: "Stream clusters stats", + responses: { + 200: { + description: "Server-Sent Events listing clusters stats", + content: { + "text/event-stream": { schema: resolver(ClustersStatsSchema) }, + }, + }, + }, + }), + validator("query", SseIntervalQuerySchema), + ) + .on( + "POST", + ["/start", "/stop", "/restart"], + describeRoute({ + tags, + description: + "Start, stop or restart every cluster (clusters already in the requested state are ignored)", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(ClustersBulkActionResultSchema) }, + }, + }, + 404: { + description: "Not found", + content: { + "text/plain": { example: "Cluster not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, + }, + }), + ) + .get( + "/:id", + describeRoute({ + tags, + description: "Get a cluster", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(ClusterSchema) }, + }, + }, + 404: { + description: "Cluster not found", + }, + }, + }), + validator("param", ClusterIdParamSchema), + async (c) => { + const param = c.req.valid("param"); + const cluster = ClusterService.get(param.id); + + return c.json(cluster, 200); + }, + ) + .delete( + "/:id", + describeRoute({ + tags, + description: "Delete a cluster", + responses: { + 200: { + description: "Deleted cluster", + content: { + "application/json": { schema: resolver(ClusterSchema) }, + }, + }, + 404: { + description: "Cluster not found", + }, + }, + }), + validator("param", ClusterIdParamSchema), + async (c) => { + const param = c.req.valid("param"); + const cluster = await ClusterService.remove(param.id); + + return c.json(cluster, 200); + }, + ) + .get( + "/:id/logs", + describeRoute({ + tags, + description: "Get a cluster logs", + responses: { + 200: { + description: "Logs of the cluster", + content: { + "application/json": { schema: resolver(ClusterLogsSchema) }, + }, + }, + 404: { + description: "Cluster not found", + }, + }, + }), + validator("param", ClusterIdParamSchema), + validator("query", ClusterLogsQuerySchema), + ) + .get( + "/:id/logs/sse", + describeRoute({ + tags, + description: "Stream cluster logs", + responses: { + 200: { + description: "Server-Sent Events with cluster logs", + content: { + "text/event-stream": { schema: resolver(ClusterLogsSchema) }, + }, + }, + 404: { + description: "Cluster not found", + }, + }, + }), + validator("param", ClusterIdParamSchema), + ) + .get( + "/:id/stats", + describeRoute({ + tags, + description: "Get cluster stats", + responses: { + 200: { + description: "Cluster stats", + content: { + "application/json": { schema: resolver(ClusterStatsSchema) }, + }, + }, + 404: { + description: "Cluster not found", + }, + }, + }), + validator("param", ClusterIdParamSchema), + ) + .get( + "/:id/stats/sse", + describeRoute({ + tags, + description: "Stream cluster stats", + responses: { + 200: { + description: "Server-Sent Events with cluster stats", + content: { + "text/event-stream": { schema: resolver(ClusterStatsSchema) }, + }, + }, + 404: { + description: "Cluster not found", + }, + }, + }), + validator("param", ClusterIdParamSchema), + ) + .on( + "POST", + ["/:id/start", "/:id/stop", "/:id/restart"], + describeRoute({ + tags, + description: "Start, stop or restart a cluster", + responses: { + 204: { + description: "OK", + content: { + "application/json": { schema: resolver(ClusterSchema) }, + }, + }, + 404: { + description: "Not found", + content: { + "text/plain": { example: "Cluster not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, + }, + }), + validator("param", ClusterIdParamSchema), + ); + +export default clusterController; diff --git a/packages/backend/src/discord/discord.module.ts b/packages/backend/src/discord/discord.module.ts deleted file mode 100644 index 8b71b7e..0000000 --- a/packages/backend/src/discord/discord.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { DiscordService } from './discord.service.js'; - -@Module({ - providers: [DiscordService], - exports: [DiscordService], -}) -export class DiscordModule {} diff --git a/packages/backend/src/discord/discord.service.spec.ts b/packages/backend/src/discord/discord.service.spec.ts deleted file mode 100644 index f5275ad..0000000 --- a/packages/backend/src/discord/discord.service.spec.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { jest } from '@jest/globals'; -import { HttpException, HttpStatus } from '@nestjs/common'; - -import { DiscordService } from './discord.service.js'; - -const TOKEN = 'a-bot-token'; -const BOT_ID = '1352006130926096504'; - -const jsonResponse = (status: number, body: unknown): Response => - ({ - ok: status >= 200 && status < 300, - status, - statusText: status === 401 ? 'Unauthorized' : 'OK', - json: () => Promise.resolve(body), - }) as Response; - -describe('DiscordService', () => { - const service = new DiscordService(); - const originalFetch = globalThis.fetch; - const fetchMock = jest.fn(); - - beforeAll(() => { - globalThis.fetch = fetchMock; - }); - - afterAll(() => { - globalThis.fetch = originalFetch; - }); - - beforeEach(() => { - fetchMock.mockReset(); - }); - - describe('getGatewayBot', () => { - it('returns the recommended shard count', async () => { - fetchMock.mockResolvedValueOnce(jsonResponse(200, { shards: 4 })); - - await expect(service.getGatewayBot(TOKEN)).resolves.toEqual({ shards: 4 }); - }); - - it('throws a 424 on a 401 (invalid token)', async () => { - fetchMock.mockResolvedValueOnce(jsonResponse(401, {})); - - await expect(service.getGatewayBot(TOKEN)).rejects.toMatchObject({ - status: HttpStatus.FAILED_DEPENDENCY, - message: 'Invalid Discord bot token.', - }); - }); - - it('throws a 424 when the Discord API errors', async () => { - fetchMock.mockResolvedValueOnce(jsonResponse(500, {})); - - await expect(service.getGatewayBot(TOKEN)).rejects.toMatchObject({ status: HttpStatus.FAILED_DEPENDENCY }); - }); - - it('throws a 424 when the shard count is missing', async () => { - fetchMock.mockResolvedValueOnce(jsonResponse(200, {})); - - await expect(service.getGatewayBot(TOKEN)).rejects.toBeInstanceOf(HttpException); - }); - - it('maps a network failure / timeout to a 424', async () => { - fetchMock.mockRejectedValueOnce(new Error('timeout')); - - await expect(service.getGatewayBot(TOKEN)).rejects.toMatchObject({ status: HttpStatus.FAILED_DEPENDENCY }); - }); - }); - - describe('getCurrentUser', () => { - it('maps the user and builds CDN URLs (png avatar, animated gif banner)', async () => { - fetchMock.mockResolvedValueOnce( - jsonResponse(200, { - id: BOT_ID, - username: 'HallMaster', - global_name: 'HallMaster Bot', - discriminator: '0', - avatar: 'abc123', - banner: 'a_def456', - accent_color: 5793266, - }), - ); - - await expect(service.getCurrentUser(TOKEN)).resolves.toEqual({ - name: 'HallMaster', - displayName: 'HallMaster Bot', - discriminator: '0', - avatarUrl: `https://cdn.discordapp.com/avatars/${BOT_ID}/abc123.png`, - bannerUrl: `https://cdn.discordapp.com/banners/${BOT_ID}/a_def456.gif`, - accentColor: 5793266, - }); - }); - - it('returns null URLs, displayName and accentColor when the bot has none', async () => { - fetchMock.mockResolvedValueOnce( - jsonResponse(200, { - id: BOT_ID, - username: 'HallMaster', - global_name: null, - discriminator: '0001', - avatar: null, - banner: null, - accent_color: null, - }), - ); - - await expect(service.getCurrentUser(TOKEN)).resolves.toEqual({ - name: 'HallMaster', - displayName: null, - discriminator: '0001', - avatarUrl: null, - bannerUrl: null, - accentColor: null, - }); - }); - }); -}); diff --git a/packages/backend/src/discord/discord.service.ts b/packages/backend/src/discord/discord.service.ts deleted file mode 100644 index e4d9e09..0000000 --- a/packages/backend/src/discord/discord.service.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { z } from 'zod'; - -const DISCORD_API_VERSION = 'v10'; -const DISCORD_API_BASE_URL = `https://discord.com/api/${DISCORD_API_VERSION}`; -const DISCORD_CDN_BASE_URL = 'https://cdn.discordapp.com'; -const DISCORD_REQUEST_TIMEOUT_MS = 10_000; - -const DiscordGatewayBotSchema = z.object({ - shards: z.int().positive(), -}); - -const DiscordUserSchema = z.object({ - id: z.string(), - username: z.string(), - global_name: z.string().nullish(), - discriminator: z.string(), - avatar: z.string().nullish(), - banner: z.string().nullish(), - accent_color: z.number().int().nullish(), -}); - -export interface DiscordGatewayBot { - shards: number; -} - -export interface DiscordProfile { - name: string; - displayName: string | null; - discriminator: string; - avatarUrl: string | null; - bannerUrl: string | null; - accentColor: number | null; -} - -@Injectable() -export class DiscordService { - private async request(path: string, token: string, schema: S): Promise> { - let response: Response; - try { - response = await fetch(`${DISCORD_API_BASE_URL}${path}`, { - headers: { Authorization: `Bot ${token}` }, - signal: AbortSignal.timeout(DISCORD_REQUEST_TIMEOUT_MS), - }); - } catch { - throw new HttpException('The Discord API is unreachable or timed out.', HttpStatus.FAILED_DEPENDENCY); - } - - if (response.status === 401) { - throw new HttpException('Invalid Discord bot token.', HttpStatus.FAILED_DEPENDENCY); - } - - if (!response.ok) { - throw new HttpException( - `Discord API returned ${response.status}: ${response.statusText}`, - HttpStatus.FAILED_DEPENDENCY, - ); - } - - const payload: unknown = await response.json().catch(() => undefined); - const parsed = schema.safeParse(payload); - if (!parsed.success) { - throw new HttpException('The Discord API returned an unexpected response.', HttpStatus.FAILED_DEPENDENCY); - } - - return parsed.data; - } - - async getGatewayBot(token: string): Promise { - return await this.request('/gateway/bot', token, DiscordGatewayBotSchema); - } - - async getCurrentUser(token: string): Promise { - const user = await this.request('/users/@me', token, DiscordUserSchema); - - return { - name: user.username, - displayName: user.global_name ?? null, - discriminator: user.discriminator, - avatarUrl: DiscordService.buildCdnUrl('avatars', user.id, user.avatar ?? null), - bannerUrl: DiscordService.buildCdnUrl('banners', user.id, user.banner ?? null), - accentColor: user.accent_color ?? null, - }; - } - - private static buildCdnUrl(kind: 'avatars' | 'banners', id: string, hash: string | null): string | null { - if (!hash) { - return null; - } - - const extension = hash.startsWith('a_') ? 'gif' : 'png'; - return `${DISCORD_CDN_BASE_URL}/${kind}/${id}/${hash}.${extension}`; - } -} diff --git a/packages/backend/src/docker/docker.module.ts b/packages/backend/src/docker/docker.module.ts deleted file mode 100644 index 3d10b24..0000000 --- a/packages/backend/src/docker/docker.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { DockerSocket } from '@hallmaster/docker.js'; -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; - -import { PrismaModule } from '../prisma/prisma.module.js'; -import { PrismaService } from '../prisma/prisma.service.js'; - -import { DockerService } from './docker.service.js'; - -@Module({ - imports: [ConfigModule, PrismaModule], - providers: [ - { - provide: DockerService, - inject: [ConfigService, PrismaService], - async useFactory(configService: ConfigService, prismaService: PrismaService) { - const dockerSocket = new DockerSocket(); - await dockerSocket.init(); - - return new DockerService(configService, prismaService, dockerSocket); - }, - }, - ], - exports: [DockerService], -}) -export class DockerModule {} diff --git a/packages/backend/src/docker/docker.service.spec.ts b/packages/backend/src/docker/docker.service.spec.ts deleted file mode 100644 index c2fedd2..0000000 --- a/packages/backend/src/docker/docker.service.spec.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { DockerAPIHttpError, DockerSocket, type DockerContainerStats } from '@hallmaster/docker.js'; -import type { Bot, Cluster } from '@hallmaster/prisma-client'; -import { ConfigService } from '@nestjs/config'; -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; -import type { DeepMockProxy } from 'jest-mock-extended'; -import { mockDeep } from 'jest-mock-extended'; - -import { PrismaService } from '../prisma/prisma.service.js'; - -import { DockerService } from './docker.service.js'; - -describe('DockerService', () => { - let service: DockerService; - let prismaService: DeepMockProxy; - let configService: DeepMockProxy; - let dockerSocket: DeepMockProxy; - - beforeEach(async () => { - prismaService = mockDeep(); - configService = mockDeep(); - dockerSocket = mockDeep(); - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - DockerService, - { provide: ConfigService, useValue: configService }, - { provide: PrismaService, useValue: prismaService }, - { provide: DockerSocket, useValue: dockerSocket }, - ], - }).compile(); - - service = module.get(DockerService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('idempotent lifecycle ops when container was removed out-of-band', () => { - const cluster: Cluster = { - botId: 'bot-1', - id: 0, - containerId: 'vanished-container-id', - shardIds: [0], - status: 'RUNNING', - }; - - it('stop() marks the cluster STOPPED and clears containerId instead of throwing', async () => { - dockerSocket.apiCall.mockRejectedValueOnce(new DockerAPIHttpError(404, 'no such container')); - - await expect(service.stop(cluster)).resolves.not.toThrow(); - - expect(prismaService.cluster.update).toHaveBeenCalledWith({ - where: { botId_id: { botId: 'bot-1', id: 0 } }, - data: { status: 'STOPPED', containerId: null }, - }); - }); - - it('remove() deletes the DB row instead of throwing', async () => { - // First call inside stop(); second call inside remove(). Both 404. - dockerSocket.apiCall.mockRejectedValue(new DockerAPIHttpError(404, 'no such container')); - - await expect(service.remove(cluster)).resolves.not.toThrow(); - - expect(prismaService.cluster.delete).toHaveBeenCalledWith({ - where: { botId_id: { botId: 'bot-1', id: 0 } }, - }); - }); - }); - - describe('start() with name collision', () => { - const bot: Bot = { - id: 'bot-1', - token: 'discord-token', - totalShards: 1, - } as unknown as Bot; - - const cluster: Cluster = { - botId: 'bot-1', - id: 0, - containerId: null, - shardIds: [0], - status: 'STOPPED', - }; - - it('cleans up the stale container and retries when create returns 409', async () => { - configService.getOrThrow.mockReturnValue('ENV_NAME'); - prismaService.dockerImage.findUnique.mockResolvedValue({ - botId: 'bot-1', - serverName: 'docker.io', - image: 'foo/bar', - tag: 'latest', - username: null, - password: null, - }); - - // Image pull succeeds - // First create attempt fails with 409. Cleanup succeeds. - // Second create attempt succeeds. Then start succeeds. - dockerSocket.apiCall - .mockResolvedValueOnce(undefined) // pull - .mockRejectedValueOnce(new DockerAPIHttpError(409, 'name already in use')) // create #1 - .mockResolvedValueOnce(undefined) // stop the stale one - .mockResolvedValueOnce(undefined) // remove the stale one - .mockResolvedValueOnce({ Id: 'new-container-id' } as never) // create #2 - .mockResolvedValueOnce(undefined); // start - - await expect(service.start(bot, cluster)).resolves.toBe('new-container-id'); - }); - }); - - describe('reconcileClusterStatus', () => { - const baseCluster = { id: 0, botId: 'bot-1', containerId: 'container-id' }; - - it('flips DB RUNNING→STOPPED when the container has been stopped out-of-band', async () => { - dockerSocket.apiCall.mockResolvedValueOnce({ State: { Running: false } } as never); - - const status = await service.reconcileClusterStatus({ ...baseCluster, status: 'RUNNING' }); - - expect(status).toBe('STOPPED'); - expect(prismaService.cluster.update).toHaveBeenCalledWith({ - where: { botId_id: { botId: 'bot-1', id: 0 } }, - data: { status: 'STOPPED' }, - }); - }); - - it('clears containerId and marks STOPPED when the container has vanished (404)', async () => { - dockerSocket.apiCall.mockRejectedValueOnce(new DockerAPIHttpError(404, 'no such container')); - - const status = await service.reconcileClusterStatus({ ...baseCluster, status: 'RUNNING' }); - - expect(status).toBe('STOPPED'); - expect(prismaService.cluster.update).toHaveBeenCalledWith({ - where: { botId_id: { botId: 'bot-1', id: 0 } }, - data: { status: 'STOPPED', containerId: null }, - }); - }); - - it('does not touch DB when the actual state matches the stored one', async () => { - dockerSocket.apiCall.mockResolvedValueOnce({ State: { Running: true } } as never); - - const status = await service.reconcileClusterStatus({ ...baseCluster, status: 'RUNNING' }); - - expect(status).toBe('RUNNING'); - expect(prismaService.cluster.update).not.toHaveBeenCalled(); - }); - - it('skips transient states (STARTING/UPDATING/ERROR) without hitting Docker', async () => { - const status = await service.reconcileClusterStatus({ ...baseCluster, status: 'STARTING' }); - - expect(status).toBe('STARTING'); - expect(dockerSocket.apiCall).not.toHaveBeenCalled(); - expect(prismaService.cluster.update).not.toHaveBeenCalled(); - }); - - it('returns current status without throwing when Docker is unreachable', async () => { - dockerSocket.apiCall.mockRejectedValueOnce(new DockerAPIHttpError(500, 'socket error')); - - const status = await service.reconcileClusterStatus({ ...baseCluster, status: 'RUNNING' }); - - expect(status).toBe('RUNNING'); - expect(prismaService.cluster.update).not.toHaveBeenCalled(); - }); - }); - - describe('getContainerStats CPU percentage', () => { - const buildStats = (cpuTotal: number, precpuTotal: number) => - ({ - cpu_stats: { - cpu_usage: { total_usage: cpuTotal, percpu_usage: [0], usage_in_kernelmode: 0, usage_in_usermode: 0 }, - system_cpu_usage: 2_000_000, - online_cpus: 1, - throttling_data: { periods: 0, throttled_periods: 0, throttled_time: 0 }, - }, - precpu_stats: { - cpu_usage: { total_usage: precpuTotal, percpu_usage: [0], usage_in_kernelmode: 0, usage_in_usermode: 0 }, - system_cpu_usage: 1_000_000, - online_cpus: 1, - throttling_data: { periods: 0, throttled_periods: 0, throttled_time: 0 }, - }, - pids_stats: { current: 1, limit: 10 }, - memory_stats: { usage: 100, limit: 1000, stats: {} }, - blkio_stats: { io_service_bytes_recursive: [] }, - networks: {}, - }) as unknown as DockerContainerStats; - - it('clamps CPU to 0 when the delta is negative (counter reset on restart)', async () => { - dockerSocket.apiCall.mockResolvedValueOnce(buildStats(100, 1000)); - - const stats = await service.getContainerStats('container-1'); - - expect(stats.cpuPercentage).toBe(0); - }); - - it('computes the CPU percentage from a positive delta', async () => { - dockerSocket.apiCall.mockResolvedValueOnce(buildStats(1500, 1000)); - - const stats = await service.getContainerStats('container-1'); - - // cpuDelta 500 / systemDelta 1_000_000 * 1 online cpu * 100 - expect(stats.cpuPercentage).toBeCloseTo(0.05); - }); - }); -}); diff --git a/packages/backend/src/docker/docker.service.ts b/packages/backend/src/docker/docker.service.ts deleted file mode 100644 index fce7c45..0000000 --- a/packages/backend/src/docker/docker.service.ts +++ /dev/null @@ -1,496 +0,0 @@ -import { Readable, Transform } from 'node:stream'; - -import { - DockerContainersAPI, - DockerImagesAPI, - DockerSocket, - DockerAPIHttpError, - type DockerContainerCreated, - type DockerContainerCreationBody, - type DockerContainerStats, -} from '@hallmaster/docker.js'; -import { Bot, Cluster, ClusterStatus, DockerImage } from '@hallmaster/prisma-client'; -import { BadRequestException, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; - -import { GetClusterStatsZodDto } from '../clusters/dto/get-cluster-stats.dto.js'; -import { PrismaService } from '../prisma/prisma.service.js'; - -@Injectable() -export class DockerService { - constructor( - private readonly configService: ConfigService, - private readonly prismaService: PrismaService, - private readonly dockerSocket: DockerSocket, - ) {} - - private isDockerApiError(error: unknown, status: number): boolean { - return error instanceof DockerAPIHttpError && error.status === status; - } - - private isContainerNotFound(error: unknown): boolean { - return this.isDockerApiError(error, 404); - } - - private async clearContainerId(cluster: Cluster): Promise { - await this.prismaService.cluster.update({ - where: { botId_id: { botId: cluster.botId, id: cluster.id } }, - data: { containerId: null }, - }); - } - - private async cleanupStaleContainerByName(name: string): Promise { - const api = new DockerContainersAPI(this.dockerSocket); - - try { - await api.stop(name); - } catch { - // proceed to remove even if stop fails - } - - try { - await api.remove(name); - } catch (e) { - // Any 404 is treated as success: the container is gone, which is the goal. - if (!this.isContainerNotFound(e)) throw e; - } - } - - private async createContainerOrCleanup( - api: DockerContainersAPI, - body: Partial, - name: string, - ): Promise { - try { - return await api.create(body, name); - } catch (e) { - if (!this.isDockerApiError(e, 409)) throw e; - await this.cleanupStaleContainerByName(name); - return await api.create(body, name); - } - } - - async reconcileClusterStatus(cluster: { - id: number; - botId: string; - containerId: string | null; - status: ClusterStatus; - }): Promise { - if (cluster.status !== 'RUNNING' && cluster.status !== 'STOPPED') { - return cluster.status; - } - if (cluster.containerId === null) { - return cluster.status; - } - - const api = new DockerContainersAPI(this.dockerSocket); - - try { - const container = await api.get(cluster.containerId); - const actual: ClusterStatus = container.State.Running ? 'RUNNING' : 'STOPPED'; - - if (actual !== cluster.status) { - await this.prismaService.cluster.update({ - where: { botId_id: { botId: cluster.botId, id: cluster.id } }, - data: { status: actual }, - }); - } - return actual; - } catch (e) { - if (this.isContainerNotFound(e)) { - await this.prismaService.cluster.update({ - where: { botId_id: { botId: cluster.botId, id: cluster.id } }, - data: { status: 'STOPPED', containerId: null }, - }); - return 'STOPPED'; - } - return cluster.status; - } - } - - async verifyImage(dockerImage: { - serverName: string; - image: string; - tag: string; - username: string | null; - password: string | null; - }): Promise { - const dockerImagesAPI = new DockerImagesAPI(this.dockerSocket); - - try { - await dockerImagesAPI.verify({ - fromImage: `${dockerImage.serverName}/${dockerImage.image}`, - tag: dockerImage.tag, - auth: { - serveraddress: dockerImage.serverName, - username: dockerImage.username ?? '', - password: dockerImage.password ?? '', - }, - }); - } catch (e) { - throw new BadRequestException('Unable to pull the Docker image.', { - description: `Failed to verify ${dockerImage.serverName}/${dockerImage.image}:${dockerImage.tag}: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - - private async pullDockerImage(cluster: Cluster, dockerImage: DockerImage) { - const dockerImagesAPI = new DockerImagesAPI(this.dockerSocket); - - try { - await dockerImagesAPI.create({ - fromImage: `${dockerImage.serverName}/${dockerImage.image}`, - tag: dockerImage.tag, - auth: { - serveraddress: dockerImage.serverName, - username: dockerImage.username ?? '', - password: dockerImage.password ?? '', - }, - }); - } catch (e) { - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: cluster.botId, id: cluster.id }, - }, - data: { - status: 'ERROR', - }, - }); - - throw new BadRequestException('Unable to pull the Docker image.', { - description: `An error occurred while pulling the bot's Docker image from ${dockerImage.image}: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - - async start(bot: Bot, cluster: Cluster) { - if (cluster.status === 'RUNNING') { - return; - } - - const dockerImage = await this.prismaService.dockerImage.findUnique({ - where: { botId: bot.id }, - }); - if (null === dockerImage) { - throw new NotFoundException(); - } - - const discordBotTokenEnvName = this.configService.getOrThrow('DISCORD_BOT_TOKEN_ENV_NAME'); - - const totalShardsEnvName = this.configService.getOrThrow('TOTAL_SHARDS_ENV_NAME'); - - const shardIdListEnvName = this.configService.getOrThrow('SHARD_ID_LIST_ENV_NAME'); - - await this.prismaService.cluster.update({ - data: { - status: 'STARTING', - }, - where: { - botId_id: { botId: cluster.botId, id: cluster.id }, - }, - }); - - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - let containerId: null | string = cluster.containerId; - - // If we have a containerId in DB but Docker no longer knows about it - // (e.g. user removed it manually), clear the stale reference and fall - // through to the create path. - if (containerId !== null) { - try { - await dockerContainersAPI.get(containerId); - } catch (e) { - if (this.isContainerNotFound(e)) { - await this.clearContainerId(cluster); - containerId = null; - } else { - await this.prismaService.cluster.update({ - where: { botId_id: { botId: bot.id, id: cluster.id } }, - data: { status: 'ERROR' }, - }); - throw new BadRequestException('Unable to inspect the cluster container', { - description: `An error occurred while inspecting the Docker container: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - } - - if (null === containerId) { - await this.pullDockerImage(cluster, dockerImage); - try { - const container = await this.createContainerOrCleanup( - dockerContainersAPI, - { - Env: [ - `${discordBotTokenEnvName}=${bot.token}`, - `${totalShardsEnvName}=${bot.totalShards}`, - `${shardIdListEnvName}=${cluster.shardIds.join(',')}`, - ], - Image: `${dockerImage.serverName}/${dockerImage.image}:${dockerImage.tag}`, - Labels: { - kind: 'hallmaster', - 'cluster-id': String(cluster.id), - 'bot-id': bot.id, - }, - }, - `${bot.id}-${cluster.id}`, - ); - - containerId = container.Id; - - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: bot.id, id: cluster.id }, - }, - data: { - status: 'RUNNING', - containerId: containerId, - }, - }); - } catch (e) { - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: bot.id, id: cluster.id }, - }, - data: { - status: 'ERROR', - }, - }); - - throw new BadRequestException('Unable to create the cluster', { - description: `An error occurred while starting the Docker container project: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - - if (null === containerId) { - throw new InternalServerErrorException(); - } - - try { - await dockerContainersAPI.start(containerId); - - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: bot.id, id: cluster.id }, - }, - data: { - status: 'RUNNING', - containerId: containerId, - }, - }); - - return containerId; - } catch (e) { - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: bot.id, id: cluster.id }, - }, - data: { - status: 'ERROR', - }, - }); - - throw new BadRequestException('Unable to create the cluster', { - description: `An error occurred while starting the Docker container project: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - - async stop(cluster: Cluster) { - if (null === cluster.containerId) { - return; - } - - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - let containerVanished = false; - - try { - await dockerContainersAPI.stop(cluster.containerId); - } catch (e) { - if (this.isContainerNotFound(e)) { - // Container was removed out-of-band — treat stop as already done. - containerVanished = true; - } else { - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: cluster.botId, id: cluster.id }, - }, - data: { - status: 'ERROR', - }, - }); - - throw new BadRequestException('Unable to stop the cluster', { - description: `An error occurred while stopping the Docker container: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - - await this.prismaService.cluster.update({ - where: { - botId_id: { botId: cluster.botId, id: cluster.id }, - }, - data: { - status: 'STOPPED', - ...(containerVanished && { containerId: null }), - }, - }); - } - - async remove(cluster: Cluster) { - if (cluster.status !== 'STOPPED' && cluster.status !== 'ERROR') { - await this.stop(cluster); - } - - if (null !== cluster.containerId) { - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - - try { - await dockerContainersAPI.remove(cluster.containerId); - } catch (e) { - if (!this.isContainerNotFound(e)) { - throw new BadRequestException('Unable to remove the cluster', { - description: `An error occurred while removing the Docker container: ${e instanceof Error ? e.message : String(e)}`, - cause: e, - }); - } - } - } - - await this.prismaService.cluster.delete({ - where: { - botId_id: { botId: cluster.botId, id: cluster.id }, - }, - }); - } - - async restart(bot: Bot, cluster: Cluster): Promise { - if (cluster.status === 'RUNNING') { - await this.stop(cluster); - cluster.status = 'STOPPED'; - } - - await this.start(bot, cluster); - } - - async streamContainerLogs(containerId: string, tail?: number | 'all'): Promise { - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - return await dockerContainersAPI.logs(containerId, { - follow: true, - stdout: true, - stderr: true, - timestamps: true, - tail: tail, - }); - } - - async getContainerLogs(containerId: string, since?: Date, until?: Date, tail?: number | 'all') { - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - - const sinceTimestamp = since ? since.getTime() / 1000 : undefined; - const untilTimestamp = until ? until.getTime() / 1000 : undefined; - - return await dockerContainersAPI.logs(containerId, { - stdout: true, - stderr: true, - timestamps: true, - follow: false, - since: sinceTimestamp, - until: untilTimestamp, - tail: tail, - }); - } - - async getContainerStats(id: string): Promise { - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - - const stats = await dockerContainersAPI.stats(id, { - oneShot: false, - stream: false, - }); - - return DockerService.toStatsDto(stats); - } - - async streamContainerStats(id: string): Promise { - const dockerContainersAPI = new DockerContainersAPI(this.dockerSocket); - - const raw = await dockerContainersAPI.stats(id, { stream: true }); - - const transform = new Transform({ - objectMode: true, - transform(stats: DockerContainerStats, _encoding, callback) { - if (stats.precpu_stats.system_cpu_usage === 0) { - callback(); - return; - } - - callback(null, DockerService.toStatsDto(stats)); - }, - }); - - raw.pipe(transform); - raw.on('error', (err) => transform.destroy(err)); - - return transform; - } - - private static toStatsDto(stats: DockerContainerStats): GetClusterStatsZodDto { - const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage; - - const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage; - - const onlineCPUs = stats.cpu_stats.online_cpus ?? stats.cpu_stats.cpu_usage.percpu_usage?.length ?? 1; - - const cpuPercentage = systemDelta > 0 && cpuDelta > 0 ? (cpuDelta / systemDelta) * onlineCPUs * 100 : 0; - - const processesUsage = stats.pids_stats?.current ?? 0; - const processesLimit = stats.pids_stats?.limit ?? 0; - const processesPercentage = processesLimit > 0 ? (processesUsage / processesLimit) * 100 : 0; - - const memoryUsage = - (stats.memory_stats?.usage ?? 0) - - (stats.memory_stats?.stats?.inactive_file ?? 0) - - (stats.memory_stats?.stats?.slab_reclaimable ?? 0); - - const memoryLimit = stats.memory_stats?.limit ?? 0; - const memoryPercentage = memoryLimit > 0 ? (memoryUsage / memoryLimit) * 100 : 0; - - const diskRead = stats.blkio_stats?.io_service_bytes_recursive - ?.filter((service) => service.op === 'read') - ?.reduce((acc, cur) => acc + cur.value, 0); - const diskWrite = stats.blkio_stats?.io_service_bytes_recursive - ?.filter((service) => service.op === 'write') - ?.reduce((acc, cur) => acc + cur.value, 0); - - const networks = Object.entries(stats.networks ?? {}).map(([interfaceName, data]) => ({ - interface: interfaceName, - transmitted: data.tx_bytes, - received: data.rx_bytes, - })); - - return { - cpuPercentage: cpuPercentage, - processes: { - usage: processesUsage, - percentage: processesPercentage, - }, - memory: { - usage: memoryUsage, - percentage: memoryPercentage, - }, - disk: { - read: diskRead ?? null, - write: diskWrite ?? null, - }, - networks: networks, - }; - } -} diff --git a/packages/backend/src/exceptions/http-exceptions.ts b/packages/backend/src/exceptions/http-exceptions.ts new file mode 100644 index 0000000..87ad3fd --- /dev/null +++ b/packages/backend/src/exceptions/http-exceptions.ts @@ -0,0 +1,39 @@ +import { HTTPException } from "hono/http-exception"; + +type HTTPExceptionOptions = Omit< + NonNullable[1]>, + "res" +>; + +export class BadRequestException extends HTTPException { + constructor(options: HTTPExceptionOptions) { + super(400, options); + } +} + +export class UnauthorizedException extends HTTPException { + constructor(options: HTTPExceptionOptions) { + super(401, options); + } +} + +type NotFoundExceptionOptions = + | Omit + | { message?: `${string} not found` }; +export class NotFoundException extends HTTPException { + constructor(options: NotFoundExceptionOptions) { + super(404, options); + } +} + +export class ConflictException extends HTTPException { + constructor(options: HTTPExceptionOptions) { + super(409, options); + } +} + +export class FailedDependancyException extends HTTPException { + constructor(options: HTTPExceptionOptions) { + super(424, options); + } +} diff --git a/packages/backend/src/index.dto.ts b/packages/backend/src/index.dto.ts deleted file mode 100644 index d18bedf..0000000 --- a/packages/backend/src/index.dto.ts +++ /dev/null @@ -1,22 +0,0 @@ -export { ApiErrorSchema, type ApiErrorDto } from './common/dto/api-error.dto.js'; - -export { registerSchema, type Register } from './auth/schemas/register.schema.js'; -export { loginSchema } from './auth/schemas/login.schema.js'; -export { type UserToken } from './auth/schemas/user-token.schema.js'; - -export { CreateBotSchema, type CreateBotDto } from './bot/dto/create-bot.dto.js'; -export { UpdateBotSchema, type UpdateBotDto } from './bot/dto/update-bot.dto.js'; -export { GetBotSchema, type GetBotDto } from './bot/dto/get-bot.dto.js'; -export { GetRecommendedShardsSchema, type GetRecommendedShardsDto } from './bot/dto/get-recommended-shards.dto.js'; - -export { SseIntervalQuerySchema } from './clusters/dto/sse-interval-query.dto.js'; -export { ClusterIdParamSchema, type ClusterIdParamDto } from './clusters/dto/cluster-id-param.dto.js'; -export { GetClusterSchema, type GetClusterDto } from './clusters/dto/get-cluster.dto.js'; -export { - GetClusterLogsQuerySchema, - GetClusterLogsSchema, - type GetClusterLogsQueryDto, - type GetClusterLogsDto, -} from './clusters/dto/get-cluster-logs.dto.js'; -export { GetClusterStatsSchema, type GetClusterStatsDto } from './clusters/dto/get-cluster-stats.dto.js'; -export { type GetAggregateStatsDto } from './clusters/dto/get-aggregate-stats.dto.js'; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts new file mode 100644 index 0000000..1ec23fb --- /dev/null +++ b/packages/backend/src/index.ts @@ -0,0 +1,12 @@ +import { serve } from "@hono/node-server"; +import app from "./app"; + +serve( + { + fetch: app.fetch, + port: 3000, + }, + (info) => { + console.log(`Server is running on http://localhost:${info.port}`); + }, +); diff --git a/packages/backend/src/lib/database.ts b/packages/backend/src/lib/database.ts new file mode 100644 index 0000000..a642319 --- /dev/null +++ b/packages/backend/src/lib/database.ts @@ -0,0 +1,8 @@ +import "dotenv/config"; +import { PrismaPg } from "@prisma/adapter-pg"; +import { PrismaClient } from "#prisma/client"; + +const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }); +const prisma = new PrismaClient({ adapter }); + +export default prisma; diff --git a/packages/backend/src/lib/env.ts b/packages/backend/src/lib/env.ts new file mode 100644 index 0000000..167454e --- /dev/null +++ b/packages/backend/src/lib/env.ts @@ -0,0 +1,8 @@ +import z from "zod"; + +const EnvironmentSchema = z.object({ + SECRET: z.string(), +}); + +const env = EnvironmentSchema.parse(process.env); +export default env; diff --git a/packages/backend/src/logger/logger.module.ts b/packages/backend/src/logger/logger.module.ts deleted file mode 100644 index d2adb67..0000000 --- a/packages/backend/src/logger/logger.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { LoggerService } from './logger.service.js'; - -@Module({ - providers: [LoggerService], - exports: [LoggerService], -}) -export class LoggerModule {} diff --git a/packages/backend/src/logger/logger.service.spec.ts b/packages/backend/src/logger/logger.service.spec.ts deleted file mode 100644 index c42ab50..0000000 --- a/packages/backend/src/logger/logger.service.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; - -import { LoggerService } from './logger.service.js'; - -describe('LoggerService', () => { - let service: LoggerService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [LoggerService], - }).compile(); - - service = module.get(LoggerService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/packages/backend/src/logger/logger.service.ts b/packages/backend/src/logger/logger.service.ts deleted file mode 100644 index 0f64689..0000000 --- a/packages/backend/src/logger/logger.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable, LoggerService as NestLoggerService } from '@nestjs/common'; -import { utilities as nestWinstonModuleUtilities } from 'nest-winston'; -import * as winston from 'winston'; - -@Injectable() -export class LoggerService implements NestLoggerService { - private readonly logger: winston.Logger; - constructor() { - this.logger = winston.createLogger({ - level: 'debug', - transports: [ - new winston.transports.Console({ - format: winston.format.combine( - winston.format.timestamp(), - winston.format.ms(), - nestWinstonModuleUtilities.format.nestLike('Nest', { - colors: true, - prettyPrint: true, - processId: true, - appName: true, - }), - ), - }), - ], - }); - } - - log(message: string) { - this.logger.info(message); - } - - error(message: string, trace: string) { - this.logger.error(message, trace); - } - - warn(message: string) { - this.logger.warn(message); - } - - debug(message: string) { - this.logger.debug(message); - } - - verbose(message: string) { - this.logger.debug(message); - } -} diff --git a/packages/backend/src/logger/logging.interceptor.ts b/packages/backend/src/logger/logging.interceptor.ts deleted file mode 100644 index 95a7128..0000000 --- a/packages/backend/src/logger/logging.interceptor.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { CallHandler, ExecutionContext, HttpException, Injectable, NestInterceptor } from '@nestjs/common'; -import type { FastifyReply, FastifyRequest } from 'fastify'; -import { catchError, Observable, tap, throwError } from 'rxjs'; - -import { LoggerService } from './logger.service.js'; - -@Injectable() -export class LoggingInterceptor implements NestInterceptor { - constructor(private readonly logger: LoggerService) {} - - intercept(context: ExecutionContext, next: CallHandler): Observable { - const req = context.switchToHttp().getRequest(); - const method = req.method; - const url = req.url; - const start = Date.now(); - - return next.handle().pipe( - tap(() => { - const res = context.switchToHttp().getResponse(); - const statusCode = res.statusCode; - const duration = Date.now() - start; - - this.logger.log(`${method} ${url} ${statusCode} - ${duration}ms`); - }), - catchError((error: unknown) => { - const duration = Date.now() - start; - - if (error instanceof HttpException) { - const statusCode = error.getStatus(); - this.logger.error(`${method} ${url} ${statusCode} - ${duration}ms - ${error.message}`, error.stack ?? ''); - } else { - this.logger.error(`${method} ${url} 500 - ${duration}ms`, error instanceof Error ? (error.stack ?? '') : ''); - } - - return throwError(() => error); - }), - ); - } -} diff --git a/packages/backend/src/main.ts b/packages/backend/src/main.ts deleted file mode 100644 index 13b1546..0000000 --- a/packages/backend/src/main.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { constants } from 'node:zlib'; - -import fastifyCompress from '@fastify/compress'; -import { Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { NestFactory } from '@nestjs/core'; -import type { NestFastifyApplication } from '@nestjs/platform-fastify'; -import { FastifyAdapter } from '@nestjs/platform-fastify'; -import type { OpenAPIObject, SwaggerCustomOptions } from '@nestjs/swagger'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { cleanupOpenApiDoc, ZodValidationPipe } from 'nestjs-zod'; -import { SwaggerTheme, SwaggerThemeNameEnum } from 'swagger-themes'; - -import { AppModule } from './app.module.js'; -import { AllExceptionsFilter } from './common/filters/all-exceptions.filter.js'; -import { LoggerService } from './logger/logger.service.js'; -import { LoggingInterceptor } from './logger/logging.interceptor.js'; - -const getSwaggerDocumentConfig = (): Omit => - new DocumentBuilder() - .setTitle('OpenAPI Documentation') - .setDescription( - 'This document indexes all the available routes, along with their params, queries, payloads and responses.', - ) - .addTag('Authentication', 'All the endpoints used to authenticate a user.') - .addTag('Bot', "All the endpoints used to manage the bot's configuration (shards, clusters, ...)") - .addTag('Clusters', 'All the endpoints to manage the clusters themselves (start, stop, restart, ...)') - .addBearerAuth( - { - type: 'http', - description: "A JWT returned by the 'login' auth endpoint.", - name: 'bearer', - }, - 'jwt', - ) - .setVersion('1.0') - .build(); - -async function bootstrap() { - const app = await NestFactory.create(AppModule, new FastifyAdapter()); - - const configService = app.get(ConfigService); - const port = configService.get('PORT', 3000); - const logRequests = configService.get('LOG_REQUESTS', false); - - const loggerService = app.get(LoggerService); - app.useLogger(loggerService); - Logger.overrideLogger(loggerService); - - app.useGlobalPipes(new ZodValidationPipe()); - app.useGlobalFilters(new AllExceptionsFilter()); - if (logRequests) { - app.useGlobalInterceptors(new LoggingInterceptor(loggerService)); - } - - app.enableCors({ - origin: '*', - }); - - await app.register(fastifyCompress, { - brotliOptions: { params: { [constants.BROTLI_PARAM_QUALITY]: 1 } }, - }); - - const swaggerDocumentationConfig = getSwaggerDocumentConfig(); - const document = SwaggerModule.createDocument(app, swaggerDocumentationConfig); - cleanupOpenApiDoc(document); - const theme = new SwaggerTheme(); - const swaggerConfig: SwaggerCustomOptions = { - explorer: true, - customSiteTitle: swaggerDocumentationConfig.info.title, - customCss: theme.getBuffer(SwaggerThemeNameEnum.DARK), - }; - SwaggerModule.setup('/openapi', app, document, swaggerConfig); - - await app.listen(port, '0.0.0.0'); -} - -bootstrap().catch(console.error); diff --git a/packages/backend/src/middlewares/auth.middlware.ts b/packages/backend/src/middlewares/auth.middlware.ts new file mode 100644 index 0000000..c91838b --- /dev/null +++ b/packages/backend/src/middlewares/auth.middlware.ts @@ -0,0 +1,28 @@ +import { describeRoute } from "hono-openapi"; +import { jwt } from "hono/jwt"; +import env from "../lib/env"; + +const authMiddlewares = [ + describeRoute({ + responses: { + 401: { + description: + "Route is protected by an Authorization header that is either not provided or invalid", + content: { + "text/plain": { example: "Unauthorized" }, + }, + }, + }, + security: [ + { + bearerAuth: [], + }, + ], + }), + jwt({ + secret: env.SECRET, + alg: "HS256", + }), +]; + +export default authMiddlewares; diff --git a/packages/backend/src/prisma/prisma.module.ts b/packages/backend/src/prisma/prisma.module.ts deleted file mode 100644 index 2d18a8e..0000000 --- a/packages/backend/src/prisma/prisma.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; - -import { PrismaService } from './prisma.service.js'; - -@Module({ - imports: [ConfigModule], - providers: [ - { - provide: PrismaService, - inject: [ConfigService], - useFactory(configService: ConfigService) { - const datasourceUrl = configService.getOrThrow('DATABASE_URL'); - - return new PrismaService(datasourceUrl); - }, - }, - ], - exports: [PrismaService], -}) -export class PrismaModule {} diff --git a/packages/backend/src/prisma/prisma.service.spec.ts b/packages/backend/src/prisma/prisma.service.spec.ts deleted file mode 100644 index a763246..0000000 --- a/packages/backend/src/prisma/prisma.service.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { TestingModule } from '@nestjs/testing'; -import { Test } from '@nestjs/testing'; - -import { PrismaService } from './prisma.service.js'; - -describe('PrismaService', () => { - let service: PrismaService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - PrismaService, - { - provide: String, - useValue: 'postgres://username:password@localhost:5432/dbname', - }, - ], - }).compile(); - - service = module.get(PrismaService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/packages/backend/src/prisma/prisma.service.ts b/packages/backend/src/prisma/prisma.service.ts deleted file mode 100644 index 2e88644..0000000 --- a/packages/backend/src/prisma/prisma.service.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { PrismaClient } from '@hallmaster/prisma-client'; -import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; -import { PrismaPg } from '@prisma/adapter-pg'; -import { Pool } from 'pg'; - -@Injectable() -export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { - constructor(databaseUrl: string) { - const pool = new Pool({ connectionString: databaseUrl }); - const adapter = new PrismaPg(pool); - super({ - adapter, - }); - } - - async onModuleInit() { - await this.$connect(); - } - - async onModuleDestroy() { - await this.$disconnect(); - } -} diff --git a/packages/backend/src/repositories/bot.repository.ts b/packages/backend/src/repositories/bot.repository.ts new file mode 100644 index 0000000..75b78d3 --- /dev/null +++ b/packages/backend/src/repositories/bot.repository.ts @@ -0,0 +1,108 @@ +import prisma from "#lib/database"; +import type { Bot } from "#prisma/client"; + +export default class BotRepository { + private static readonly DEFAULT_SELECT = { + id: true, + totalShards: true, + } as const; + + static async count() { + return prisma.bot.count(); + } + + public static async create(data: Omit) { + return prisma.bot.create({ + data: { + ...data, + totalShards: 0, + }, + select: this.DEFAULT_SELECT, + }); + } + + public static async delete() { + const bot = await this.findFirstId(); + if (bot === null) return null; + + return prisma.bot.delete({ where: { id: bot.id }, select: this.DEFAULT_SELECT }); + } + + public static async findFirst() { + return prisma.bot.findFirst({ select: this.DEFAULT_SELECT }); + } + + public static async findFirstId() { + return prisma.bot.findFirst({ select: { id: true } }); + } + + public static async findFirstIdContainerImageTotalShardsAndToken() { + return prisma.bot.findFirst({ + select: { + id: true, + token: true, + totalShards: true, + containerImage: { + select: { image: true, tag: true, serverName: true, username: true, password: true }, + }, + }, + }); + } + + public static async findFirstToken() { + return prisma.bot.findFirst({ select: { token: true } }); + } + + public static async findFirstIdAndToken() { + return prisma.bot.findFirst({ select: { id: true, token: true } }); + } + + public static async findFirstClusters() { + return prisma.bot.findFirst({ + select: { clusters: { select: { id: true, status: true, shardIds: true } } }, + }); + } + + public static async updateToken(data: Pick) { + const bot = await this.findFirstId(); + if (bot === null) return null; + + return prisma.bot.update({ + data, + where: { + id: bot.id, + }, + select: this.DEFAULT_SELECT, + }); + } + + // public static async createClusters(data: Pick[]) { + // const bot = await this.findFirstId(); + // if (bot === null) return null; + + // const { clusters } = await prisma.bot.update({ + // data: { + // totalShards: data.flatMap(({ shardIds }) => shardIds).length, + // clusters: { + // createMany: { + // data, + // }, + // }, + // }, + // where: { + // id: bot.id, + // }, + // select: { + // clusters: { + // select: { + // id: true, + // status: true, + // shardIds: true, + // }, + // }, + // }, + // }); + + // return clusters; + // } +} diff --git a/packages/backend/src/repositories/cluster.repository.ts b/packages/backend/src/repositories/cluster.repository.ts new file mode 100644 index 0000000..53f70f3 --- /dev/null +++ b/packages/backend/src/repositories/cluster.repository.ts @@ -0,0 +1,92 @@ +import prisma from "#lib/database"; +import type { Cluster } from "#prisma/client"; + +export default class ClusterRepository { + private static readonly DEFAULT_SELECT = { + id: true, + shardIds: true, + status: true, + } as const; + + public static async findFirstContainerIdById(id: Cluster["id"]) { + return prisma.cluster.findFirst({ + where: { id }, + select: { + containerId: true, + }, + }); + } + + public static async create( + data: Pick, + ) { + return prisma.cluster.create({ + data, + select: this.DEFAULT_SELECT, + }); + } + + public static async findFirstById(id: Cluster["id"]) { + return prisma.cluster.findFirst({ where: { id }, select: this.DEFAULT_SELECT }); + } + + public static async findFirstBotIdById(id: Cluster["id"]) { + return prisma.cluster.findFirst({ + where: { + id, + }, + select: { botId: true }, + }); + } + + public static async findFirstShardIdsById(id: Cluster["id"]) { + return prisma.cluster.findFirst({ + where: { id }, + select: { shardIds: true }, + }); + } + + public static async updateStatusById(status: Cluster["status"], id: Cluster["id"]) { + const cluster = await this.findFirstBotIdById(id); + if (cluster === null) return null; + + return prisma.cluster.update({ + data: { + status, + }, + where: { botId_id: { botId: cluster.botId, id } }, + select: this.DEFAULT_SELECT, + }); + } + + public static async deleteById(id: Cluster["id"]) { + const cluster = await this.findFirstBotIdById(id); + if (cluster === null) return null; + + return prisma.cluster.delete({ + where: { botId_id: { botId: cluster.botId, id } }, + select: this.DEFAULT_SELECT, + }); + } + + public static async findMany() { + return prisma.cluster.findMany({ + select: this.DEFAULT_SELECT, + }); + } + + public static async findManyIdAndContainerId() { + return prisma.cluster.findMany({ select: { id: true, containerId: true } }); + } + + public static async updateManyStatusById(status: Cluster["status"], ids: Cluster["id"][]) { + return prisma.cluster.updateMany({ + data: { status }, + where: { id: { in: ids } }, + }); + } + + public static async deleteManyById(ids: Cluster["id"][]) { + return prisma.cluster.deleteMany({ where: { id: { in: ids } } }); + } +} diff --git a/packages/backend/src/repositories/container-image.repository.ts b/packages/backend/src/repositories/container-image.repository.ts new file mode 100644 index 0000000..10d2b4c --- /dev/null +++ b/packages/backend/src/repositories/container-image.repository.ts @@ -0,0 +1,66 @@ +import prisma from "#lib/database"; +import type { ContainerImage } from "#prisma/client"; +import { HTTPException } from "hono/http-exception"; +import BotRepository from "./bot.repository"; + +export default class ContainerImageRepository { + private static readonly DEFAULT_SELECT = { + serverName: true, + image: true, + tag: true, + username: true, + password: true, + } as const; + + public static async count() { + return prisma.containerImage.count(); + } + + public static async create( + data: Pick, + ) { + const bot = await BotRepository.findFirstId(); + if (bot === null) return null; + + if (await this.count()) + throw new HTTPException(409, { message: "A container image is already registered" }); + + return prisma.containerImage.create({ + data: { + ...data, + botId: bot.id, + }, + select: { + image: true, + serverName: true, + tag: true, + username: true, + }, + }); + } + + public static async findFirst() { + return prisma.containerImage.findFirst({ + select: this.DEFAULT_SELECT, + }); + } + + public static async findFirstId() { + return prisma.containerImage.findFirst({ select: { id: true } }); + } + + public static async update( + data: Pick, + ) { + const containerImage = await this.findFirstId(); + if (containerImage === null) return null; + + return prisma.containerImage.update({ + data, + where: { + id: containerImage.id, + }, + select: this.DEFAULT_SELECT, + }); + } +} diff --git a/packages/backend/src/repositories/user.repository.ts b/packages/backend/src/repositories/user.repository.ts new file mode 100644 index 0000000..360e3ae --- /dev/null +++ b/packages/backend/src/repositories/user.repository.ts @@ -0,0 +1,17 @@ +import prisma from "#lib/database"; +import type { User } from "#prisma/client"; + +export default class UserRepository { + static async create(data: User) { + return prisma.user.create({ select: { username: true }, data }); + } + + static async findFirstByUsername(username: User["username"]) { + return prisma.user.findFirst({ + select: { username: true, passwordHash: true }, + where: { + username, + }, + }); + } +} diff --git a/packages/backend/src/schemas/auth.schema.ts b/packages/backend/src/schemas/auth.schema.ts new file mode 100644 index 0000000..2123801 --- /dev/null +++ b/packages/backend/src/schemas/auth.schema.ts @@ -0,0 +1,12 @@ +import z from "zod"; + +export const UserSchema = z.object({ + username: z.string().min(1).meta({ description: "User username" }), + password: z.string().min(8).meta({ description: "User password" }), +}); +export type User = z.infer; + +export const UserTokenResponseSchema = z.object({ + token: z.jwt().meta({ description: "User session token" }), +}); +export type UserTokenResponse = z.infer; diff --git a/packages/backend/src/schemas/bot.schema.ts b/packages/backend/src/schemas/bot.schema.ts new file mode 100644 index 0000000..e5e264d --- /dev/null +++ b/packages/backend/src/schemas/bot.schema.ts @@ -0,0 +1,73 @@ +import z from "zod"; +import { ClusterSchema } from "./cluster.schema"; + +export const ContainerImageSchema = z.object({ + url: z.string().meta({ + description: "URL of the container image (example: ghcr.io/repo/image:tag)", + }), + username: z.string().optional().meta({ + description: "Username used to authenticate to the container registry", + }), + password: z.string().optional().meta({ + description: "Password used to authenticate to the container registry", + }), +}); +export type ContainerImage = z.infer; + +export const CreateBotSchema = z.object({ + token: z.string().meta({ + description: "Discord token of the bot", + }), +}); +export type CreateBot = z.infer; + +export const BotClusterLayoutSchema = z.array(ClusterSchema).meta({ + description: "Clusters layout", +}); +export type BotClusterLayout = z.infer; + +export const BotDiscordProfileSchema = z.object({ + name: z.string().meta({ + description: "Discord username of the bot", + }), + displayName: z.string().nullable().meta({ + description: "Discord display name (global name) of the bot, or null if none is set", + }), + discriminator: z.string().meta({ + description: "Discord discriminator", + }), + avatarUrl: z.string().nullable().meta({ + description: "URL of the bot avatar on the Discord CDN, or null if none is set", + }), + bannerUrl: z.string().nullable().meta({ + description: "URL of the bot banner on the Discord CDN, or null if none is set", + }), + accentColor: z.number().int().nullable().meta({ + description: "The bot profile accent color as an integer (0xRRGGBB)", + }), +}); +export type BotDiscordProfile = z.infer; + +export const BotSchema = z.object({ + id: z.string().meta({ + description: "Bot's ID", + }), + shards: z.number().nonnegative().meta({ + description: "Number of shards", + }), + // layout: z.array(BotClusterLayoutSchema).meta({ + // description: "Clusters layout", + // }), + // containerImage: ContainerImageSchema.omit({ password: true }).meta({ + // description: "Container image configuration associated to the bot", + // }), + // discordProfile: BotDiscordProfileSchema.nullable().optional().meta({ + // description: "Bot Discord profile", + // }), +}); +export type Bot = z.infer; + +// export const UpdateBotSchema = CreateBotSchema.extend({ +// layout: BotClusterLayoutSchema.meta({ description: "Cluster layout" }), +// }); +// export type UpdateBot = z.infer; diff --git a/packages/backend/src/schemas/cluster.schema.ts b/packages/backend/src/schemas/cluster.schema.ts new file mode 100644 index 0000000..a035bf4 --- /dev/null +++ b/packages/backend/src/schemas/cluster.schema.ts @@ -0,0 +1,111 @@ +import z from "zod"; + +export const ClusterIdSchema = z.coerce.number().int().nonnegative().meta({ + description: "Cluster ID", +}); +export type ClusterId = z.infer; + +export const ClusterSchema = z.object({ + id: ClusterIdSchema, + shardIds: z.array(z.number().nonnegative()).meta({ + description: "Shard IDs associated to that cluster", + }), + status: z.enum(["STARTING", "RUNNING", "STOPPED", "ERROR", "UPDATING"]).meta({ + description: "Status of the cluster", + }), +}); +export type Cluster = z.infer; + +export const ClustersSchema = z.array(ClusterSchema); + +export const UpdateClustersSchema = z + .array(ClusterSchema.pick({ id: true, shardIds: true }).partial({ id: true })) + .min(1) + .meta({ description: "List of clusters to update or create (if id is not provided)" }); +export type UpdateClusters = z.infer; + +export const SseIntervalQuerySchema = z.object({ + interval: z.coerce.number().positive().min(1).max(120).optional().default(5).meta({ + description: "Refresh interval in seconds", + }), +}); + +export const ClusterStatsSchema = z.object({ + cpuPercentage: z.number().min(0).nonnegative().meta({ + description: "CPU usage percentage of the cluster", + }), + memory: z.object({ + usage: z.number().nonnegative().meta({ + description: "Number of bytes used in memory", + }), + percentage: z.number().min(0).nonnegative().meta({ + description: "Percentage of available memory used", + }), + }), + network: z.array( + z.object({ + interface: z.string().meta({ + description: "Networking interface name", + }), + transmitted: z.number().nonnegative().meta({ + description: "Number of bytes transmitted by the interface", + }), + received: z.number().nonnegative().meta({ + description: "Number of bytes received by the interface", + }), + }), + ), +}); + +export const ClustersStatsSchema = z.record(ClusterIdSchema, ClusterStatsSchema).meta({ + description: + "Stats of every running clusters keyed by ID (clusters whose stats failed to fetch are omitted)", +}); + +export const ClustersBulkActionResultSchema = z.object({ + succeeded: z.array(ClusterIdSchema).meta({ + description: "Successfull clusters IDs", + }), + failed: z + .array( + z.object({ + id: ClusterIdSchema, + reason: z.string(), + }), + ) + .meta({ + description: "Failed clusters IDs with the reason of failure", + }), +}); + +export const ClusterIdParamSchema = z.object({ + id: ClusterIdSchema, +}); + +export const ClusterLogsQuerySchema = z.object({ + since: z.iso.datetime().optional().meta({ + description: "At which point in time does the logs collection start", + }), + until: z.iso.datetime().optional().meta({ + description: "At which point in time does the logs collection end", + }), + tail: z.coerce.number().positive().min(1).or(z.literal("all")).optional().default("all").meta({ + description: "How many logs to fetch from latest to oldest ('all' gets all the logs)", + }), +}); + +export const ClusterLogSchema = z.object({ + content: z.string().meta({ + description: "Content of a log line", + }), + stream: z.enum(["STDOUT", "STDERR"]).meta({ + description: "Stream that the log was published into", + }), + timestamp: z.string().optional().meta({ + description: "RFC3339Nano timestamp emitted by the Docker daemon for that log line", + }), +}); + +export const ClusterLogsSchema = z.array(ClusterLogSchema).meta({ + description: "Logs (line by line) of a container", +}); diff --git a/packages/backend/src/schemas/discord.schema.ts b/packages/backend/src/schemas/discord.schema.ts new file mode 100644 index 0000000..5cdd40c --- /dev/null +++ b/packages/backend/src/schemas/discord.schema.ts @@ -0,0 +1,15 @@ +import z from "zod"; +import { BotSchema } from "./bot.schema"; + +export const DiscordGatewayBotSchema = BotSchema.pick({ shards: true }); +export type DiscordGatewayBot = z.infer; + +export const DiscordUserSchema = z.object({ + id: z.string(), + username: z.string(), + global_name: z.string().nullable(), + discriminator: z.string(), + avatar: z.string().nullable(), + banner: z.string().nullable(), + accent_color: z.number().int().nullable(), +}); diff --git a/packages/backend/src/services/auth.service.ts b/packages/backend/src/services/auth.service.ts new file mode 100644 index 0000000..fa5788f --- /dev/null +++ b/packages/backend/src/services/auth.service.ts @@ -0,0 +1,29 @@ +import prisma from "#lib/database.js"; +import type { User, UserTokenResponse } from "#schemas/auth.schema.js"; +import { hash, verify } from "argon2"; +import { sign } from "hono/jwt"; +import { ConflictException, UnauthorizedException } from "../exceptions/http-exceptions.js"; +import env from "../lib/env.js"; +import UserRepository from "../repositories/user.repository.js"; + +export class AuthService { + public static async register({ username, password }: User): Promise { + const userCount = await prisma.user.count(); + if (userCount !== 0) throw new ConflictException({ message: "A user is already registered" }); + + const user = await UserRepository.create({ username, passwordHash: await hash(password) }); + + return { token: await sign({ sub: user.username }, env.SECRET) }; + } + + public static async login({ username, password }: User): Promise { + const user = await UserRepository.findFirstByUsername(username); + + if (user === null || !(await verify(user.passwordHash, password))) + throw new UnauthorizedException({ message: "Invalid username or password" }); + + return { + token: await sign({ sub: user.username }, env.SECRET), + }; + } +} diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts new file mode 100644 index 0000000..bb1d9bb --- /dev/null +++ b/packages/backend/src/services/bot.service.ts @@ -0,0 +1,171 @@ +import type { Bot, ContainerImage, CreateBot } from "#schemas/bot.schema"; +import { HTTPException } from "hono/http-exception"; +import { NotFoundException } from "../exceptions/http-exceptions"; +import BotRepository from "../repositories/bot.repository"; +import ContainerImageRepository from "../repositories/container-image.repository"; +import DiscordService from "./discord.service"; +import DockerService from "./docker.service"; +import ClusterService from "./cluster.service"; + +export default class BotService { + // FIX this doesn't work for local images without a host (example: "hallmaster-bot:latest") + private static parseContainerImage(url: ContainerImage["url"]) { + const [host, ...path] = url.trim().split("/"); + const [image, tag = "latest"] = path.join("/").split(":"); + + return { host, image, tag }; + } + + public static async create({ token }: CreateBot): Promise { + await DiscordService.getGatewayBot(token); + + const botCount = await BotRepository.count(); + if (botCount !== 0) throw new HTTPException(409, { message: "A bot is already registered" }); + + const { id, totalShards } = await BotRepository.create({ + id: Buffer.from(token.split(".")[0], "base64").toString("ascii"), + token, + }); + + return { + id, + shards: totalShards, + }; + } + + public static async update({ token }: CreateBot): Promise { + const bot = BotRepository.findFirst(); + if (bot === null) throw new NotFoundException({ message: "Bot not found" }); + + await DiscordService.getGatewayBot(token); + + const updated = await BotRepository.updateToken({ token }); + if (updated === null) throw new NotFoundException({ message: "Bot not found" }); + + return { + id: updated.id, + shards: updated.totalShards, + }; + } + + public static async get(): Promise { + const bot = await BotRepository.findFirst(); + if (bot === null) throw new NotFoundException({ message: "Bot not found" }); + + return { + id: bot.id, + shards: bot.totalShards, + }; + } + + public static async delete(): Promise { + const bot = await BotRepository.findFirstId(); + if (bot === null) throw new NotFoundException({ message: "Bot not found" }); + + await ClusterService.stopAll(); + await ClusterService.removeAll(); + + const deleted = await BotRepository.delete(); + if (deleted === null) throw new NotFoundException({ message: "Bot not found" }); + + return { + id: deleted.id, + shards: deleted.totalShards, + }; + } + + public static async discordProfile() { + const bot = await BotRepository.findFirstToken(); + if (bot === null) throw new NotFoundException({ message: "Bot not found" }); + + return DiscordService.getBotProfile(bot.token); + } + + public static async createContainerImage({ + url, + username, + password, + }: ContainerImage): Promise> { + const { host, image, tag } = this.parseContainerImage(url); + + const auth = + username && password + ? { + serveraddress: host, + username: username, + password: password, + } + : undefined; + + await DockerService.pullImage({ + fromImage: [host, image].join("/"), + tag, + auth, + }); + + const containerImage = await ContainerImageRepository.create({ + image, + tag, + serverName: host, + username: username ?? null, + password: password ?? null, + }); + + if (containerImage === null) throw new NotFoundException({ message: "Bot not found" }); + + return { + url: `${containerImage.serverName}/${containerImage.image}:${containerImage.tag}`, + username: containerImage.username ?? undefined, + }; + } + + public static async getContainerImage() { + const containerImage = await ContainerImageRepository.findFirst(); + if (containerImage === null) + throw new NotFoundException({ message: "Container image not found" }); + + return { + url: `${containerImage.serverName}/${containerImage.image}:${containerImage.tag}`, + username: containerImage.username ?? undefined, + }; + } + + public static async updateContainerImage({ + url, + username, + password, + }: ContainerImage): Promise> { + const { host, image, tag } = this.parseContainerImage(url); + + const auth = + username && password + ? { + serveraddress: host, + username: username, + password: password, + } + : undefined; + + await DockerService.pullImage({ + fromImage: [host, image].join("/"), + tag, + auth, + }); + + const containerImage = await ContainerImageRepository.update({ + image, + tag, + serverName: host, + username: username ?? null, + password: password ?? null, + }); + + if (containerImage === null) + throw new NotFoundException({ message: "Container image not found" }); + + return { + url: `${containerImage.serverName}/${containerImage.image}:${containerImage.tag}`, + username: containerImage.username ?? undefined, + }; + } +} diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts new file mode 100644 index 0000000..a9b2271 --- /dev/null +++ b/packages/backend/src/services/cluster.service.ts @@ -0,0 +1,214 @@ +import type { Bot, ContainerImage } from "#prisma/client"; +import type { Cluster, ClusterId, UpdateClusters } from "#schemas/cluster.schema"; +import { BadRequestException, NotFoundException } from "../exceptions/http-exceptions"; +import BotRepository from "../repositories/bot.repository"; +import ClusterRepository from "../repositories/cluster.repository"; +import ContainerImageRepository from "../repositories/container-image.repository"; +import DockerService from "./docker.service"; + +export default class ClusterService { + private static async create( + cluster: Pick, + bot: Pick, + containerImage: Pick, + ) { + const shards = cluster.shardIds.join(","); + + const container = await DockerService.create( + { + Image: `${containerImage.serverName}/${containerImage.image}:${containerImage.tag}`, + Env: [ + `DISCORD_BOT_TOKEN=${bot.token}`, + `TOTAL_SHARDS=${bot.totalShards}`, + `SHARD_ID_LIST=${shards}`, + ], + Labels: { + "dev.hallmaster.cluster": cluster.id.toString(), + "dev.hallmaster.shards": shards, + }, + }, + `${bot.id}-${cluster.id}`, + ); + + return ClusterRepository.create({ + id: cluster.id, + botId: bot.id, + containerId: container.Id, + shardIds: cluster.shardIds, + status: cluster.status, + }); + } + + public static async get(id: ClusterId) { + const cluster = await ClusterRepository.findFirstById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return cluster; + } + + public static async start(id: ClusterId) { + const cluster = await ClusterRepository.findFirstContainerIdById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + if ((await ClusterRepository.updateStatusById("STARTING", id)) === null) + throw new NotFoundException({ message: `Cluster ${id} not found` }); + + await DockerService.start(cluster.containerId).catch(async (error) => { + if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) + throw new NotFoundException({ message: `Cluster ${id} not found` }); + + throw error; + }); + + const updated = await ClusterRepository.updateStatusById("RUNNING", id); + if (updated === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return updated; + } + + public static async stop(id: ClusterId) { + const cluster = await ClusterRepository.findFirstContainerIdById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + await DockerService.stop(cluster.containerId).catch(async (error) => { + if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) + throw new NotFoundException({ message: `Cluster ${id} not found` }); + + throw error; + }); + + const updated = await ClusterRepository.updateStatusById("STOPPED", id); + if (updated === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return updated; + } + + public static async restart(id: ClusterId) { + const cluster = await ClusterRepository.findFirstContainerIdById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + if ((await ClusterRepository.updateStatusById("STARTING", id)) === null) + throw new NotFoundException({ message: `Cluster ${id} not found` }); + + await DockerService.restart(cluster.containerId).catch(async (error) => { + if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) + throw new NotFoundException({ message: `Cluster ${id} not found` }); + + throw error; + }); + + const updated = await ClusterRepository.updateStatusById("RUNNING", id); + if (updated === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return updated; + } + + public static async remove(id: ClusterId) { + const cluster = await ClusterRepository.findFirstContainerIdById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + await DockerService.remove(cluster.containerId); + + return ClusterRepository.deleteById(id); + } + + public static async getAll() { + const clusters = await ClusterRepository.findMany(); + if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); + + return clusters; + } + + public static async stopAll() { + const clusters = await ClusterRepository.findManyIdAndContainerId(); + if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); + + await Promise.all(clusters.map(({ containerId }) => DockerService.stop(containerId))); + + return ClusterRepository.updateManyStatusById("STOPPED", clusters.map(({ id }) => id)); + } + + public static async removeAll() { + const clusters = await ClusterRepository.findManyIdAndContainerId(); + if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); + + await Promise.all(clusters.map(({ containerId }) => DockerService.remove(containerId))); + + return ClusterRepository.deleteManyById(clusters.map(({ id }) => id)); + } + + public static async updateAll(clusters: UpdateClusters) { + const bot = await BotRepository.findFirstIdAndToken(); + if (bot === null) throw new NotFoundException({ message: "Bot not found" }); + + const ids = new Set(); + const previous = await ClusterRepository.findMany(); + const previousIds = new Set(previous.map(({ id }) => id)); + + for (const [index, cluster] of clusters.entries()) { + if (cluster.shardIds.length === 0) + throw new BadRequestException({ message: `Cluster at index ${index} is empty` }); + + if (cluster.id === undefined) continue; + + if (ids.has(cluster.id)) + throw new BadRequestException({ message: `Duplicate cluster ${cluster.id}` }); + + if (!previousIds.has(cluster.id)) + throw new BadRequestException({ message: `Cluster ${cluster.id} does not exist` }); + + ids.add(cluster.id); + } + + const shards = clusters.flatMap(({ shardIds }) => shardIds).sort((a, b) => a - b); + for (let index = 0; index < shards.length; index++) { + const shard = shards[index]; + + if (shard !== index) { + throw new BadRequestException({ + message: `Non-contiguous shard IDs, expected ${index} but got ${shard}`, + }); + } + } + + let id = 0; + for (const cluster of clusters) { + if (cluster.id !== undefined) continue; + + while (ids.has(id)) id++; + cluster.id = id++; + } + + const containerImage = await ContainerImageRepository.findFirst(); + if (containerImage === null) + throw new NotFoundException({ message: "Container image not found" }); + + await this.removeAll(); + + const previousStatuses = new Map(previous.map(({ id, status }) => [id, status])); + const updated = await Promise.all( + clusters.map((cluster) => + this.create( + { + id: cluster.id!, + shardIds: cluster.shardIds, + status: previousStatuses.get(id!) === "STOPPED" ? "STOPPED" : "UPDATING", + }, + { + ...bot, + totalShards: shards.length, + }, + containerImage, + ), + ), + ); + + return Promise.all( + updated.map((cluster) => { + if (cluster.status === "STOPPED") return Promise.resolve(cluster); + + return this.start(cluster.id); + }), + ); + } +} diff --git a/packages/backend/src/services/discord.service.ts b/packages/backend/src/services/discord.service.ts new file mode 100644 index 0000000..0f8657d --- /dev/null +++ b/packages/backend/src/services/discord.service.ts @@ -0,0 +1,67 @@ +import type { BotDiscordProfile } from "#schemas/bot.schema"; +import { + DiscordGatewayBotSchema, + DiscordUserSchema, + type DiscordGatewayBot, +} from "#schemas/discord.schema"; +import { HTTPException } from "hono/http-exception"; +import type z from "zod"; + +export default class DiscordService { + private static readonly DISCORD_API_BASE_URL = "https://discord.com/api/v10"; + private static readonly DISCORD_CDN_BASE_URL = "https://cdn.discordapp.com"; + + private static async request( + path: `/${string}`, + token: string, + schema: S, + ): Promise> { + const response = await fetch(`${DiscordService.DISCORD_API_BASE_URL}${path}`, { + headers: { Authorization: `Bot ${token}` }, + signal: AbortSignal.timeout(10_000), + }).catch(() => { + throw new HTTPException(424, { message: "Discord API is unreachable or timed out" }); + }); + + if (response.status === 401) + throw new HTTPException(424, { message: "Invalid Discord bot token" }); + + if (!response.ok) { + throw new HTTPException(424, { + message: `Discord API returned a ${response.status} ${response.statusText}`, + }); + } + + try { + return schema.parse(await response.json()); + } catch (error) { + throw new HTTPException(424, { + message: "Discord API returned an unexpected response body", + cause: error, + }); + } + } + + private static cdnUrl(kind: "avatar" | "banner", id: string, hash: string) { + const extension = hash.startsWith("a_") ? "gif" : "png"; + + return `${this.DISCORD_CDN_BASE_URL}/${kind}/${id}/${hash}.${extension}`; + } + + public static async getGatewayBot(token: string): Promise { + return await this.request("/gateway/bot", token, DiscordGatewayBotSchema); + } + + public static async getBotProfile(token: string): Promise { + const user = await this.request("/users/@me", token, DiscordUserSchema); + + return { + name: user.username, + displayName: user.global_name, + discriminator: user.discriminator, + avatarUrl: user.avatar ? this.cdnUrl("avatar", user.id, user.avatar) : null, + bannerUrl: user.banner ? this.cdnUrl("banner", user.id, user.banner) : null, + accentColor: user.accent_color, + }; + } +} diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts new file mode 100644 index 0000000..a1954b7 --- /dev/null +++ b/packages/backend/src/services/docker.service.ts @@ -0,0 +1,116 @@ +import { + DockerAPIHttpError, + DockerContainersAPI, + DockerImagesAPI, + DockerSocket, +} from "@hallmaster/docker.js"; +import { FailedDependancyException } from "../exceptions/http-exceptions"; + +type MethodParameters< + C extends abstract new (...args: any[]) => any, + P extends keyof InstanceType, +> = Parameters[P], (...args: any[]) => any>>; + +const socket = new DockerSocket(); +await socket.init(); + +export default class DockerService { + private static readonly images = new DockerImagesAPI(socket); + private static readonly containers = new DockerContainersAPI(socket); + + public static async pullImage(...options: MethodParameters) { + return this.images.create(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } + + public static async create(...options: MethodParameters) { + return this.containers.create(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } + + public static async start(...options: MethodParameters) { + return this.containers.start(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + if (error.status === 304) return; + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } + + public static async stop(...options: MethodParameters) { + return this.containers.stop(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + if (error.status === 304) return; + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } + + public static async restart(...options: MethodParameters) { + return this.containers.restart(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } + + public static async remove(...options: MethodParameters) { + return this.containers.remove(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + if (error.status === 404) return; + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } +} diff --git a/packages/backend/tests/.gitkeep b/packages/backend/tests/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/packages/backend/tsconfig.build.json b/packages/backend/tsconfig.build.json deleted file mode 100644 index 64f86c6..0000000 --- a/packages/backend/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index dc6ff8d..456471e 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -1,38 +1,27 @@ { - "$schema": "https://json.schemastore.org/tsconfig.json", "compilerOptions": { - "strict": true, - "allowUnreachableCode": false, - "noFallthroughCasesInSwitch": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - - "module": "NodeNext", - "moduleResolution": "NodeNext", + "allowImportingTsExtensions": true, + "noEmit": true, + "target": "es2023", + "module": "esnext", + "moduleResolution": "bundler", "resolveJsonModule": true, - - "declaration": true, - "declarationMap": true, - "removeComments": true, - "sourceMap": true, - "newLine": "lf", - "outDir": "./dist", - "rootDir": "./", - - "allowSyntheticDefaultImports": true, + "verbatimModuleSyntax": true, + "strict": true, "esModuleInterop": true, + "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - - "target": "ES2023", - "lib": ["ES2023"], - "types": ["jest", "mocha"], - "experimentalDecorators": true, - "emitDecoratorMetadata": true, - - "incremental": true, - "skipLibCheck": true + "outDir": "dist", + "rootDir": ".", + "types": ["node"], + "paths": { + "#prisma/client": ["./prisma/generated/prisma/client"], + "#controllers/*": ["./src/controllers/*"], + "#middlewares/*": ["./src/middlewares/*"], + "#database/*": ["./src/database/*"], + "#lib/*": ["./src/lib/*"], + "#services/*": ["./src/services/*"], + "#schemas/*": ["./src/schemas/*"] + } } } diff --git a/packages/frontend/.oxlintrc.json b/packages/frontend/.oxlintrc.json index dfe0c31..d4bceb0 100644 --- a/packages/frontend/.oxlintrc.json +++ b/packages/frontend/.oxlintrc.json @@ -2,6 +2,7 @@ "$schema": "./node_modules/oxlint/configuration_schema.json", "plugins": ["eslint", "typescript", "unicorn", "oxc", "import", "node", "promise", "vitest"], "options": { - "typeAware": true + "typeAware": true, + "typeCheck": true } } diff --git a/packages/frontend/README.md b/packages/frontend/README.md deleted file mode 100644 index 75842c4..0000000 --- a/packages/frontend/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# sv - -Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). - -## Creating a project - -If you're seeing this, you've probably already done this step. Congrats! - -```sh -# create a new project in the current directory -npx sv create - -# create a new project in my-app -npx sv create my-app -``` - -## Developing - -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: - -```sh -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open -``` - -## Building - -To create a production version of your app: - -```sh -npm run build -``` - -You can preview the production build with `npm run preview`. - -> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/packages/prisma-client/.gitignore b/packages/prisma-client/.gitignore deleted file mode 100644 index be6cbc1..0000000 --- a/packages/prisma-client/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/dist -/node_modules -/generated diff --git a/packages/prisma-client/package.json b/packages/prisma-client/package.json deleted file mode 100644 index 7beb092..0000000 --- a/packages/prisma-client/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@hallmaster/prisma-client", - "version": "0.0.1", - "type": "module", - "main": "./generated/client.js", - "types": "./generated/client.d.ts", - "private": false, - "exports": { - ".": { - "types": "./generated/client.d.ts", - "import": "./generated/client.js" - } - }, - "files": [ - "generated", - "package.json" - ], - "dependencies": { - "@prisma/client-runtime-utils": "^7.4.0" - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d654b1..e263bf3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -171,10 +171,10 @@ importers: devDependencies: '@changesets/cli': specifier: ^2.29.8 - version: 2.29.8(@types/node@22.19.11) + version: 2.29.8(@types/node@26.6.3) '@commitlint/cli': specifier: ^20.4.2 - version: 20.4.2(@types/node@22.19.11)(typescript@5.9.3) + version: 20.4.2(@types/node@26.6.3)(typescript@7.0.2) '@commitlint/config-conventional': specifier: ^20.4.2 version: 20.4.2 @@ -184,175 +184,67 @@ importers: packages/backend: dependencies: - '@fastify/compress': - specifier: 8.3.1 - version: 8.3.1 '@hallmaster/docker.js': - specifier: 0.0.28 + specifier: ^0.0.28 version: 0.0.28 - '@hallmaster/prisma-client': - specifier: workspace:* - version: link:../prisma-client - '@nestjs/common': - specifier: 11.1.14 - version: 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/config': - specifier: 4.0.3 - version: 4.0.3(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) - '@nestjs/core': - specifier: 11.1.14 - version: 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/jwt': - specifier: 11.0.2 - version: 11.0.2(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)) - '@nestjs/platform-fastify': - specifier: 11.1.14 - version: 11.1.14(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) - '@nestjs/swagger': - specifier: 11.2.6 - version: 11.2.6(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2) + '@hono/node-server': + specifier: ^2.1.1 + version: 2.1.1(hono@4.13.9) + '@hono/standard-validator': + specifier: ^0.4.0 + version: 0.4.0(@standard-schema/spec@1.1.0)(hono@4.13.9) '@prisma/adapter-pg': - specifier: 7.4.1 - version: 7.4.1 + specifier: ^7.10.0 + version: 7.10.0 '@prisma/client': - specifier: 7.4.1 - version: 7.4.1(prisma@7.4.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) + specifier: ^7.4.1 + version: 7.4.1(prisma@7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2))(typescript@7.0.2) + '@scalar/hono-api-reference': + specifier: ^0.12.6 + version: 0.12.6(hono@4.13.9) argon2: - specifier: 0.44.0 - version: 0.44.0 - fastify: - specifier: 5.7.4 - version: 5.7.4 - nest-winston: - specifier: 1.10.2 - version: 1.10.2(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(winston@3.19.0) - nestjs-zod: - specifier: 5.1.1 - version: 5.1.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.6(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.3.6) + specifier: ^0.45.1 + version: 0.45.1 + dotenv: + specifier: ^18.0.4 + version: 18.0.4 + hono: + specifier: ^4.13.9 + version: 4.13.9 + hono-openapi: + specifier: ^1.3.3 + version: 1.3.3(effect@3.20.0)(hono@4.13.9)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5) pg: - specifier: 8.18.0 - version: 8.18.0 - prisma: - specifier: 7.4.1 - version: 7.4.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - reflect-metadata: - specifier: 0.2.2 - version: 0.2.2 - rxjs: - specifier: 7.8.2 - version: 7.8.2 - swagger-themes: - specifier: 1.4.3 - version: 1.4.3 - winston: - specifier: 3.19.0 - version: 3.19.0 + specifier: ^8.23.0 + version: 8.23.0 zod: - specifier: 4.3.6 - version: 4.3.6 + specifier: ^4.6.5 + version: 4.6.5 devDependencies: - '@eslint/eslintrc': - specifier: 3.3.3 - version: 3.3.3 - '@eslint/js': - specifier: 9.39.3 - version: 9.39.3 - '@jest/globals': - specifier: 30.2.0 - version: 30.2.0 - '@nestjs/cli': - specifier: 11.0.16 - version: 11.0.16(@swc/cli@0.6.0(@swc/core@1.15.11(@swc/helpers@0.5.18))(chokidar@4.0.3))(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11) - '@nestjs/schematics': - specifier: 11.0.9 - version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) - '@nestjs/testing': - specifier: 11.1.14 - version: 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-express@11.1.13) - '@swc/cli': - specifier: 0.6.0 - version: 0.6.0(@swc/core@1.15.11(@swc/helpers@0.5.18))(chokidar@4.0.3) - '@swc/core': - specifier: 1.15.11 - version: 1.15.11(@swc/helpers@0.5.18) - '@types/jest': - specifier: 30.0.0 - version: 30.0.0 - '@types/mocha': - specifier: ^10.0.10 - version: 10.0.10 '@types/node': - specifier: 22.19.11 - version: 22.19.11 + specifier: ^26.1.1 + version: 26.6.3 '@types/pg': - specifier: 8.16.0 - version: 8.16.0 - '@types/supertest': - specifier: 6.0.3 - version: 6.0.3 - dotenv: - specifier: 17.3.1 - version: 17.3.1 - dotenv-cli: - specifier: 10.0.0 - version: 10.0.0 - eslint: - specifier: 9.39.3 - version: 9.39.3(jiti@2.7.0) - eslint-config-prettier: - specifier: 10.1.8 - version: 10.1.8(eslint@9.39.3(jiti@2.7.0)) - eslint-import-resolver-typescript: - specifier: ^4.4.4 - version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.7.0)) - eslint-plugin-import: - specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.3(jiti@2.7.0)) - eslint-plugin-jest: - specifier: 29.15.0 - version: 29.15.0(@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3) - eslint-plugin-prettier: - specifier: 5.5.5 - version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.3(jiti@2.7.0)))(eslint@9.39.3(jiti@2.7.0))(prettier@3.8.1) - globals: - specifier: 16.5.0 - version: 16.5.0 - jest: - specifier: 29.7.0 - version: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - jest-esm-transformer: - specifier: 1.0.0 - version: 1.0.0 - jest-mock-extended: - specifier: 4.0.0 - version: 4.0.0(@jest/globals@30.2.0)(jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3) - prettier: - specifier: 3.8.1 - version: 3.8.1 - source-map-support: - specifier: 0.5.21 - version: 0.5.21 - supertest: - specifier: 7.2.2 - version: 7.2.2 - ts-jest: - specifier: 29.4.6 - version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3) - ts-loader: - specifier: 9.5.4 - version: 9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18))) - ts-node: - specifier: 10.9.2 - version: 10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3) - tsconfig-paths: - specifier: 4.2.0 - version: 4.2.0 + specifier: ^8.23.1 + version: 8.23.1 + oxfmt: + specifier: ^0.70.0 + version: 0.70.0(svelte@5.56.4(@typescript-eslint/types@8.56.0)) + oxlint: + specifier: ^1.85.0 + version: 1.85.0(oxlint-tsgolint@7.0.2003) + oxlint-tsgolint: + specifier: ^7.0.2003 + version: 7.0.2003 + prisma: + specifier: ^7.10.0 + version: 7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) + tsx: + specifier: ^4.23.0 + version: 4.23.15 typescript: - specifier: 5.9.3 - version: 5.9.3 - typescript-eslint: - specifier: 8.56.0 - version: 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) + specifier: ^7.0.2 + version: 7.0.2 packages/frontend: devDependencies: @@ -370,16 +262,16 @@ importers: version: 4.15.2(svelte@5.56.4(@typescript-eslint/types@8.56.0)) '@sveltejs/adapter-node': specifier: ^5.5.4 - version: 5.5.4(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0))) + version: 5.5.4(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.69.2 - version: 2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + version: 2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^6.2.4 - version: 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + version: 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) '@tailwindcss/vite': specifier: ^4.3.1 - version: 4.3.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + version: 4.3.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) '@types/d3-shape': specifier: ^3.1.8 version: 3.1.8 @@ -400,7 +292,7 @@ importers: version: 16.5.0 layerchart: specifier: 2.0.0-next.65 - version: 2.0.0-next.65(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6) + version: 2.0.0-next.65(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6) oxfmt: specifier: ^0.57.0 version: 0.57.0(svelte@5.56.4(@typescript-eslint/types@8.56.0)) @@ -427,225 +319,22 @@ importers: version: 0.49.2(patch_hash=2c0d482dc06ad381cab34a6d5581eefb4327fc24a8dd80dad7562bd8922f879c)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(svelte@5.56.4(@typescript-eslint/types@8.56.0)) vite: specifier: ^7.3.5 - version: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0) - - packages/prisma-client: - dependencies: - '@prisma/client-runtime-utils': - specifier: ^7.4.0 - version: 7.4.1 + version: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) packages: - '@angular-devkit/core@19.2.17': - resolution: {integrity: sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - chokidar: ^4.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/core@19.2.19': - resolution: {integrity: sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - chokidar: ^4.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/schematics-cli@19.2.19': - resolution: {integrity: sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - hasBin: true - - '@angular-devkit/schematics@19.2.17': - resolution: {integrity: sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - - '@angular-devkit/schematics@19.2.19': - resolution: {integrity: sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@babel/code-frame@7.29.0': resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.28.6': - resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - '@borewit/text-codec@0.2.1': - resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} - '@changesets/apply-release-plan@7.0.14': resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} @@ -701,26 +390,6 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@chevrotain/cst-dts-gen@10.5.0': - resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} - - '@chevrotain/gast@10.5.0': - resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} - - '@chevrotain/types@10.5.0': - resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} - - '@chevrotain/utils@10.5.0': - resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} - - '@colors/colors@1.5.0': - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - - '@colors/colors@1.6.0': - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - engines: {node: '>=0.1.90'} - '@commitlint/cli@20.4.2': resolution: {integrity: sha512-YjYSX2yj/WsVoxh9mNiymfFS2ADbg2EK4+1WAsMuckwKMCqJ5PDG0CJU/8GvmHWcv4VRB2V02KqSiecRksWqZQ==} engines: {node: '>=v18'} @@ -790,41 +459,25 @@ packages: resolution: {integrity: sha512-aO5l99BQJ0X34ft8b0h7QFkQlqxC6e7ZPVmBKz13xM9O8obDaM1Cld4sQlJDXXU/VFuUzQ30mVtHjVz74TuStw==} engines: {node: '>=v18'} - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@dabh/diagnostics@2.0.8': - resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==} - '@dagrejs/dagre@2.0.4': resolution: {integrity: sha512-J6vCWTNpicHF4zFlZG1cS5DkGzMr9941gddYkakjrg3ZNev4bbqEgLHFTWiFrcJm7UCRu7olO3K6IRDd9gSGhA==} '@dagrejs/graphlib@3.0.4': resolution: {integrity: sha512-HxZ7fCvAwTLCWCO0WjDkzAFQze8LdC6iOpKbetDKHIuDfIgMlIzYzqZ4nxwLlclQX+3ZVeZ1K2OuaOE2WWcyOg==} - '@electric-sql/pglite-socket@0.0.20': - resolution: {integrity: sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==} + '@electric-sql/pglite-socket@0.1.3': + resolution: {integrity: sha512-LAciWM0M1dCL8hlsxu2venbVZcdxema0BtDfpWYVqr+Y468UADw0pFWidhKw1M8sfJ8rdLT71tjMmnirf/IZRQ==} hasBin: true peerDependencies: - '@electric-sql/pglite': 0.3.15 + '@electric-sql/pglite': 0.4.3 - '@electric-sql/pglite-tools@0.2.20': - resolution: {integrity: sha512-BK50ZnYa3IG7ztXhtgYf0Q7zijV32Iw1cYS8C+ThdQlwx12V5VZ9KRJ42y82Hyb4PkTxZQklVQA9JHyUlex33A==} + '@electric-sql/pglite-tools@0.3.3': + resolution: {integrity: sha512-AlzLJTRJ8+UFgK8CmxIpyIpJ0+YaFw02IiOSdYrqxwPXdSyeIShz8aa9Tq+tYFXdPwcaMp/Fc80mQZ1dkOQ/wg==} peerDependencies: - '@electric-sql/pglite': 0.3.15 - - '@electric-sql/pglite@0.3.15': - resolution: {integrity: sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==} - - '@emnapi/core@1.10.0': - resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@electric-sql/pglite': 0.4.3 - '@emnapi/wasi-threads@1.2.1': - resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@electric-sql/pglite@0.4.3': + resolution: {integrity: sha512-ichuWTgtd4mOM1G4SpyGJa5trT03lWbMypDV0fUXUCXg5hiHqVAz/bZyV68NqmkLB7WcYmj1RMJVSp8HV/v/ZQ==} '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -835,229 +488,311 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.2': + resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.27.3': resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.2': + resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.27.3': resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.2': + resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.27.3': resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.2': + resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.27.3': resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.2': + resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.27.3': resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.2': + resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.27.3': resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.2': + resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.3': resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.2': + resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.27.3': resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.2': + resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.27.3': resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.2': + resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.27.3': resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.2': + resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.27.3': resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.2': + resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.27.3': resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.2': + resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.27.3': resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.2': + resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.27.3': resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.2': + resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.27.3': resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.2': + resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.27.3': resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.2': + resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.27.3': resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.2': + resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.3': resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.2': + resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.27.3': resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.2': + resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.3': resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.2': + resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.27.3': resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.2': + resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.27.3': resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.2': + resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.27.3': resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.2': + resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.27.3': resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.2': + resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.27.3': resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.3.3': - resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.39.3': - resolution: {integrity: sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@fastify/accept-negotiator@2.0.1': - resolution: {integrity: sha512-/c/TW2bO/v9JeEgoD/g1G5GxGeCF1Hafdf79WPmUlgYiBXummY0oX3VVq4yFkKKVBKDNlaDUYoab7g38RpPqCQ==} - - '@fastify/ajv-compiler@4.0.5': - resolution: {integrity: sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==} - - '@fastify/compress@8.3.1': - resolution: {integrity: sha512-BUpItLr6MUX9e9ukg5Y6xekyA/7pBFG8QWtFCrUDm9ctoBc3R2/nA16yOaOWtVoccpXGjdDEYA/MxAb5+8cxag==} - - '@fastify/cors@11.2.0': - resolution: {integrity: sha512-LbLHBuSAdGdSFZYTLVA3+Ch2t+sA6nq3Ejc6XLAKiQ6ViS2qFnvicpj0htsx03FyYeLs04HfRNBsz/a8SvbcUw==} - - '@fastify/error@4.2.0': - resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} - - '@fastify/fast-json-stringify-compiler@5.0.3': - resolution: {integrity: sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==} - - '@fastify/formbody@8.0.2': - resolution: {integrity: sha512-84v5J2KrkXzjgBpYnaNRPqwgMsmY7ZDjuj0YVuMR3NXCJRCgKEZy/taSP1wUYGn0onfxJpLyRGDLa+NMaDJtnA==} - - '@fastify/forwarded@3.0.1': - resolution: {integrity: sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==} - - '@fastify/merge-json-schemas@0.2.1': - resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} - - '@fastify/proxy-addr@5.1.0': - resolution: {integrity: sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==} - - '@fastify/send@4.1.0': - resolution: {integrity: sha512-TMYeQLCBSy2TOFmV95hQWkiTYgC/SEx7vMdV+wnZVX4tt8VBLKzmH8vV9OzJehV0+XBfg+WxPMt5wp+JBUKsVw==} - - '@fastify/static@8.3.0': - resolution: {integrity: sha512-yKxviR5PH1OKNnisIzZKmgZSus0r2OZb8qCSbqmw34aolT4g3UlzYfeBRym+HJ1J471CR8e2ldNub4PubD1coA==} + '@esbuild/win32-x64@0.28.2': + resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] '@floating-ui/core@1.7.5': resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} @@ -1071,164 +806,20 @@ packages: '@hallmaster/docker.js@0.0.28': resolution: {integrity: sha512-Nu3dALGsn3o6k4vcX7IRgFU/BXtZ+JZlBxEYuu2lbdaEoKL6Pu/Hq8BT3urSIVbl3usXNleXbiaHyCLCGxtJUA==} - '@hono/node-server@1.19.9': - resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} - engines: {node: '>=18.14.1'} + '@hono/node-server@2.1.1': + resolution: {integrity: sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==} + engines: {node: '>=20'} peerDependencies: hono: ^4 - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} + '@hono/standard-validator@0.4.0': + resolution: {integrity: sha512-MxOm1asDh9j7c1D0KiWwppSbFgAQxTRO4YEhjMrJn3lF1BIcXenPdgjSGKDRfQmZdaTaMA8slQLETgVOKyJxFw==} + peerDependencies: + '@standard-schema/spec': ^1.0.0 + hono: '>=4.11.2' - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/figures@1.0.15': - resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} - engines: {node: '>=18'} - - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/prompts@7.10.1': - resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/prompts@7.3.2': - resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/type@3.0.10': - resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -1239,132 +830,6 @@ packages: '@internationalized/date@3.12.0': resolution: {integrity: sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==} - '@isaacs/cliui@9.0.0': - resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} - engines: {node: '>=18'} - - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/core@29.7.0': - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/diff-sequences@30.0.1': - resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/environment@30.2.0': - resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@30.2.0': - resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@30.2.0': - resolution: {integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@30.2.0': - resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/get-type@30.1.0': - resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@30.2.0': - resolution: {integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/pattern@30.0.1': - resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/schemas@30.0.5': - resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/snapshot-utils@30.2.0': - resolution: {integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@30.2.0': - resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@30.2.0': - resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -1384,9 +849,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@layerstack/svelte-actions@1.0.1-next.18': resolution: {integrity: sha512-gxPzCnJ1c9LTfWtRqLUzefCx+k59ZpxDUQ2XB+LokveZQPe7IDSOwHaBOEMlaGoGrtwc3Ft8dSZq+2WT2o9u/g==} @@ -1404,372 +866,201 @@ packages: peerDependencies: svelte: ^5 - '@lukeed/csprng@1.1.0': - resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} - engines: {node: '>=8'} - - '@lukeed/ms@2.0.2': - resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} - engines: {node: '>=8'} - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@microsoft/tsdoc@0.16.0': - resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} - '@mrleebo/prisma-ast@0.13.1': - resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==} - engines: {node: '>=16'} + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} - '@napi-rs/nice-android-arm-eabi@1.1.1': - resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} - engines: {node: '>= 10'} + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@oxfmt/binding-android-arm-eabi@0.57.0': + resolution: {integrity: sha512-qVBsEO+KugOsCmUHcO8iqNnqc65p7PCKpCs8M66mPZ+Ri+CWbcpoQOEJBg2OTu03+0qu++NK1jj6IzvQVs0Sig==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@napi-rs/nice-android-arm64@1.1.1': - resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==} - engines: {node: '>= 10'} + '@oxfmt/binding-android-arm-eabi@0.70.0': + resolution: {integrity: sha512-Xd7YO4/T2axEj6FTLcj4Why3mTBqFMg+x24xtorT4Lb2+1g82090GH0a/4U1m0pABGYiix2bq1pqkYrmV3f0Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.57.0': + resolution: {integrity: sha512-mp6PibWbao3aizijcheOeHQaYEhcUAt8pwLniYbtLfHxL/psFF0BykAwCj+s3c6qIpa8yN8keZICWrqtZ70w8g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@napi-rs/nice-darwin-arm64@1.1.1': - resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==} - engines: {node: '>= 10'} + '@oxfmt/binding-android-arm64@0.70.0': + resolution: {integrity: sha512-x9rlMYyKXdgKdYyUJzGsK1ZV8P4di/J32ipzcS6Jet6p9r9UAh28neXIMtdlSaJJycdi61Z4YkcLKLpk8ueFjg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.57.0': + resolution: {integrity: sha512-T+0stuCBqmUVY+aMIvrgXhzGhHO3sD5tNiiEcYqgSdPsnukskQqn2u5qOVD0sv1l7RLdFS5Z/f5Wi9Ktyjr3Eg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@napi-rs/nice-darwin-x64@1.1.1': - resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==} - engines: {node: '>= 10'} + '@oxfmt/binding-darwin-arm64@0.70.0': + resolution: {integrity: sha512-IUTUPvrBVYy7POh4stXzRdz4IVC/1QSaviCWoyenSlOhGu0X9j5K07vCTM9biLjAA2Zs31l0Rj5vvRpj9n95wA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.57.0': + resolution: {integrity: sha512-O+3JbqWs/mCI2oi4xfhRO2IVPFJNDDEBV8Odo+ZpmsUOeKJfjXoNH7nDmBEQcDgK7NfjDIyE7kRgYSZcTLDO0A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@napi-rs/nice-freebsd-x64@1.1.1': - resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==} - engines: {node: '>= 10'} + '@oxfmt/binding-darwin-x64@0.70.0': + resolution: {integrity: sha512-vw745q870oTd6J517O24asoX4/E+eK0nxYIFoedSLqgJ+nI5En7+ZS82iZSHZ69zevQrnOXiyHP01dA+t8xD8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.57.0': + resolution: {integrity: sha512-pxwhxVC+JkLX9twOQ/8C/vbuOQcMZyKIDmiRDZfO7yITuVcIdZCiLRqqf4QOxb2+8FWrRXzQpm+1DBKcMpHSSQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': - resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==} - engines: {node: '>= 10'} + '@oxfmt/binding-freebsd-x64@0.70.0': + resolution: {integrity: sha512-NO14EgSM9dFkcg+MfGPxvsKqXYs9LKaxPrOKXpv1R0rLokGGFDcCq6dBMq18dE4wlpFOovX0UZY2uh1P30O7QA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.57.0': + resolution: {integrity: sha512-pxBU4zH2imB/MDBfth2rOMeVxXUMjRQLCazagwLARIFH3hVlxZJBlM4nSnHXaIHJK4/qezoFCIORN6AY8Mra4A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@napi-rs/nice-linux-arm64-gnu@1.1.1': - resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==} - engines: {node: '>= 10'} + '@oxfmt/binding-linux-arm-gnueabihf@0.70.0': + resolution: {integrity: sha512-139OEhHarj9CYoJ/i9gXlPv4KLBGtLj2toseOWYFf09QwlhklaZk+wW3aOvlqoeZtuayvkoSNVWra7WJ21s3VQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.57.0': + resolution: {integrity: sha512-JAprOzt8tycYou36ZgEw14DlRHTiN8qdtKANdV3VZIRIvTI/lh/cX13c9pJ/EnDk2GT3FASH7KvCgQ2AufAifQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.70.0': + resolution: {integrity: sha512-GEh2PY3IWTE0M24eNhTduountANSbWyDmMnzFSQE/nGg/bjPugbUgiGuFu+xdqcQSd/HKSwH80/F2yVVD48yhA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.57.0': + resolution: {integrity: sha512-ajtjaxSaj9xl4BW7REt+Cef/ttzbAq00Bq4z7JUDZEfgFXdwSjH8K9bF+IcIJzZB9lKqMfQ4eHuSFOvvlvtqOg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@napi-rs/nice-linux-arm64-musl@1.1.1': - resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} - engines: {node: '>= 10'} + '@oxfmt/binding-linux-arm64-gnu@0.70.0': + resolution: {integrity: sha512-En5i+UJmZSPxuSf47F2Hl5YOzKB0bicQLnGQkeTCMQ35cWLtbrSwACJKfiLqRZrk05DwSnsJkhBRaM3OURtIaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.57.0': + resolution: {integrity: sha512-p4Y/+RYk9Bk5WO+zHSUXAClRmZ2fbJCejMuCAsU2HhyME4jqf6Ftt/mJYEwIah1wGCBDYOB7wEGV1x5bCEZ6hA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@napi-rs/nice-linux-ppc64-gnu@1.1.1': - resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} - engines: {node: '>= 10'} + '@oxfmt/binding-linux-arm64-musl@0.70.0': + resolution: {integrity: sha512-WWOoV5W9Im3flVwOVrWn/2DUlOF8v5vcCip+kcNuaMpulRCh6nzzt1Su2vcL2F908YJIXNV3HvegbBHuyLwKHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.57.0': + resolution: {integrity: sha512-By6tRALAZsno0F4zedmtG+wdMvJiJmJoXM4d3+A9zHE4HRXLqXITwRH8mgrlcXc5yJM2g2W3riRPwTYdgemZLQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@napi-rs/nice-linux-riscv64-gnu@1.1.1': - resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} - engines: {node: '>= 10'} - cpu: [riscv64] + '@oxfmt/binding-linux-ppc64-gnu@0.70.0': + resolution: {integrity: sha512-YUouneIqW+5n7aE8xx/zeZ6/utr/KH7oykcGoFyd8Uz8uh591T1oKlnoWA3BsRq/ZR42oY1w4MUYvS/0e/MQOA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] os: [linux] libc: [glibc] - '@napi-rs/nice-linux-s390x-gnu@1.1.1': - resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} - engines: {node: '>= 10'} - cpu: [s390x] + '@oxfmt/binding-linux-riscv64-gnu@0.57.0': + resolution: {integrity: sha512-skYeG+RgvyzspqVEBsEprL90OYYZfoVNqB3HcCNR6QDJyXKOzfDRT3zncnHmUaFluIlBHuY23mU1b5WGgR98hA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] os: [linux] libc: [glibc] - '@napi-rs/nice-linux-x64-gnu@1.1.1': - resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} - engines: {node: '>= 10'} - cpu: [x64] + '@oxfmt/binding-linux-riscv64-gnu@0.70.0': + resolution: {integrity: sha512-iEnMf21S5aGVa4hViDGY8sAQ/AHyCu2JPyrQF8P06wtHhSkD1YJBeT4m/KiGewgf7+a5XCYSCRIPcRQa1xwEoQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] os: [linux] libc: [glibc] - '@napi-rs/nice-linux-x64-musl@1.1.1': - resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} - engines: {node: '>= 10'} - cpu: [x64] + '@oxfmt/binding-linux-riscv64-musl@0.57.0': + resolution: {integrity: sha512-FFgACrZOXAXUh5KQh2mt1CDOVOZmn+QzHP71wM9QobNwyQvoFfyAeefVUltW83g3sm7LTiH3yfFqLLVUpA5ZFQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] os: [linux] libc: [musl] - '@napi-rs/nice-openharmony-arm64@1.1.1': - resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [openharmony] - - '@napi-rs/nice-win32-arm64-msvc@1.1.1': - resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@napi-rs/nice-win32-ia32-msvc@1.1.1': - resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@napi-rs/nice-win32-x64-msvc@1.1.1': - resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@napi-rs/nice@1.1.1': - resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} - engines: {node: '>= 10'} - - '@napi-rs/wasm-runtime@0.2.12': - resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - - '@nestjs/cli@11.0.16': - resolution: {integrity: sha512-P0H+Vcjki6P5160E5QnMt3Q0X5FTg4PZkP99Ig4lm/4JWqfw32j3EXv3YBTJ2DmxLwOQ/IS9F7dzKpMAgzKTGg==} - engines: {node: '>= 20.11'} - hasBin: true - peerDependencies: - '@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 - '@swc/core': ^1.3.62 - peerDependenciesMeta: - '@swc/cli': - optional: true - '@swc/core': - optional: true - - '@nestjs/common@11.1.14': - resolution: {integrity: sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==} - peerDependencies: - class-transformer: '>=0.4.1' - class-validator: '>=0.13.2' - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/config@4.0.3': - resolution: {integrity: sha512-FQ3M3Ohqfl+nHAn5tp7++wUQw0f2nAk+SFKe8EpNRnIifPqvfJP6JQxPKtFLMOHbyer4X646prFG4zSRYEssQQ==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - rxjs: ^7.1.0 - - '@nestjs/core@11.1.14': - resolution: {integrity: sha512-7OXPPMoDr6z+5NkoQKu4hOhfjz/YYqM3bNilPqv1WVFWrzSmuNXxvhbX69YMmNmRYascPXiwESqf5jJdjKXEww==} - engines: {node: '>= 20'} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - '@nestjs/websockets': ^11.0.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - '@nestjs/microservices': - optional: true - '@nestjs/platform-express': - optional: true - '@nestjs/websockets': - optional: true - - '@nestjs/jwt@11.0.2': - resolution: {integrity: sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==} - peerDependencies: - '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - - '@nestjs/mapped-types@2.1.0': - resolution: {integrity: sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - class-transformer: ^0.4.0 || ^0.5.0 - class-validator: ^0.13.0 || ^0.14.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/platform-express@11.1.13': - resolution: {integrity: sha512-LYmi43BrAs1n74kLCUfXcHag7s1CmGETcFbf9IVyA/KWXAuAH95G3wEaZZiyabOLFNwq4ifnRGnIwUwW7cz3+w==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - - '@nestjs/platform-fastify@11.1.14': - resolution: {integrity: sha512-/hnKvPpeH+CTWus7L+F+qXkdbTRXudSQU3Z8tcs5kfvluoBLsxSDTTvjjySr7zrPs5bebaFbGIyG2fKMAWl++g==} - peerDependencies: - '@fastify/static': ^8.0.0 || ^9.0.0 - '@fastify/view': ^10.0.0 || ^11.0.0 - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - peerDependenciesMeta: - '@fastify/static': - optional: true - '@fastify/view': - optional: true - - '@nestjs/schematics@11.0.9': - resolution: {integrity: sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==} - peerDependencies: - typescript: '>=4.8.2' - - '@nestjs/swagger@11.2.6': - resolution: {integrity: sha512-oiXOxMQqDFyv1AKAqFzSo6JPvMEs4uA36Eyz/s2aloZLxUjcLfUMELSLSNQunr61xCPTpwEOShfmO7NIufKXdA==} - peerDependencies: - '@fastify/static': ^8.0.0 || ^9.0.0 - '@nestjs/common': ^11.0.1 - '@nestjs/core': ^11.0.1 - class-transformer: '*' - class-validator: '*' - reflect-metadata: ^0.1.12 || ^0.2.0 - peerDependenciesMeta: - '@fastify/static': - optional: true - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/testing@11.1.14': - resolution: {integrity: sha512-cQxX0ronsTbpfHz8/LYOVWXxoTxv6VoxrnuZoQaVX7QV2PSMqxWE7/9jSQR0GcqAFUEmFP34c6EJqfkjfX/k4Q==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - peerDependenciesMeta: - '@nestjs/microservices': - optional: true - '@nestjs/platform-express': - optional: true - - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@nuxt/opencollective@0.4.1': - resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==} - engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} - hasBin: true - - '@oxfmt/binding-android-arm-eabi@0.57.0': - resolution: {integrity: sha512-qVBsEO+KugOsCmUHcO8iqNnqc65p7PCKpCs8M66mPZ+Ri+CWbcpoQOEJBg2OTu03+0qu++NK1jj6IzvQVs0Sig==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@oxfmt/binding-android-arm64@0.57.0': - resolution: {integrity: sha512-mp6PibWbao3aizijcheOeHQaYEhcUAt8pwLniYbtLfHxL/psFF0BykAwCj+s3c6qIpa8yN8keZICWrqtZ70w8g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@oxfmt/binding-darwin-arm64@0.57.0': - resolution: {integrity: sha512-T+0stuCBqmUVY+aMIvrgXhzGhHO3sD5tNiiEcYqgSdPsnukskQqn2u5qOVD0sv1l7RLdFS5Z/f5Wi9Ktyjr3Eg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@oxfmt/binding-darwin-x64@0.57.0': - resolution: {integrity: sha512-O+3JbqWs/mCI2oi4xfhRO2IVPFJNDDEBV8Odo+ZpmsUOeKJfjXoNH7nDmBEQcDgK7NfjDIyE7kRgYSZcTLDO0A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@oxfmt/binding-freebsd-x64@0.57.0': - resolution: {integrity: sha512-pxwhxVC+JkLX9twOQ/8C/vbuOQcMZyKIDmiRDZfO7yITuVcIdZCiLRqqf4QOxb2+8FWrRXzQpm+1DBKcMpHSSQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@oxfmt/binding-linux-arm-gnueabihf@0.57.0': - resolution: {integrity: sha512-pxBU4zH2imB/MDBfth2rOMeVxXUMjRQLCazagwLARIFH3hVlxZJBlM4nSnHXaIHJK4/qezoFCIORN6AY8Mra4A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxfmt/binding-linux-arm-musleabihf@0.57.0': - resolution: {integrity: sha512-JAprOzt8tycYou36ZgEw14DlRHTiN8qdtKANdV3VZIRIvTI/lh/cX13c9pJ/EnDk2GT3FASH7KvCgQ2AufAifQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxfmt/binding-linux-arm64-gnu@0.57.0': - resolution: {integrity: sha512-ajtjaxSaj9xl4BW7REt+Cef/ttzbAq00Bq4z7JUDZEfgFXdwSjH8K9bF+IcIJzZB9lKqMfQ4eHuSFOvvlvtqOg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@oxfmt/binding-linux-arm64-musl@0.57.0': - resolution: {integrity: sha512-p4Y/+RYk9Bk5WO+zHSUXAClRmZ2fbJCejMuCAsU2HhyME4jqf6Ftt/mJYEwIah1wGCBDYOB7wEGV1x5bCEZ6hA==} + '@oxfmt/binding-linux-riscv64-musl@0.70.0': + resolution: {integrity: sha512-91Sdniaj20fQzyMeCxMDzTP4c9s4RB8dGQ308xHhDR0n6U7+1Xq7N9klE7mfXq8iV3lRmIGSXi5X23Hn/0XX/g==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] + cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.57.0': - resolution: {integrity: sha512-By6tRALAZsno0F4zedmtG+wdMvJiJmJoXM4d3+A9zHE4HRXLqXITwRH8mgrlcXc5yJM2g2W3riRPwTYdgemZLQ==} + '@oxfmt/binding-linux-s390x-gnu@0.57.0': + resolution: {integrity: sha512-Nm/BAOfQeFiiKd502mZn/GAVKJwtd0RdCg17G3Wz/WSOIQmDi3+7/SZH4BHn1Ye5KvTVH3ua8WvfwLLycNIuvA==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] + cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.57.0': - resolution: {integrity: sha512-skYeG+RgvyzspqVEBsEprL90OYYZfoVNqB3HcCNR6QDJyXKOzfDRT3zncnHmUaFluIlBHuY23mU1b5WGgR98hA==} + '@oxfmt/binding-linux-s390x-gnu@0.70.0': + resolution: {integrity: sha512-uUV30M6E+2TKKGMaKiwfeL4RZrviHXlUxsrYJ/jFBb+1EZy+pnFT+hF73eeWdzh5NqPOAnw0iiMAIqjqiLZPFg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] + cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.57.0': - resolution: {integrity: sha512-FFgACrZOXAXUh5KQh2mt1CDOVOZmn+QzHP71wM9QobNwyQvoFfyAeefVUltW83g3sm7LTiH3yfFqLLVUpA5ZFQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@oxfmt/binding-linux-s390x-gnu@0.57.0': - resolution: {integrity: sha512-Nm/BAOfQeFiiKd502mZn/GAVKJwtd0RdCg17G3Wz/WSOIQmDi3+7/SZH4BHn1Ye5KvTVH3ua8WvfwLLycNIuvA==} + '@oxfmt/binding-linux-x64-gnu@0.57.0': + resolution: {integrity: sha512-BiSy5Ku3mQqyxS6YIqAJgd403wEUWvI7kerfzPxc2l/txZVmZM0pSj7oDM+4bGBExowxOi7o73jEam1W0EDTZg==} engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] + cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.57.0': - resolution: {integrity: sha512-BiSy5Ku3mQqyxS6YIqAJgd403wEUWvI7kerfzPxc2l/txZVmZM0pSj7oDM+4bGBExowxOi7o73jEam1W0EDTZg==} + '@oxfmt/binding-linux-x64-gnu@0.70.0': + resolution: {integrity: sha512-ivMcX6kNDPhqtbOaBt/ItFlLlTlXNHLgRuNmxP6Na6UuYXRT10llpJcAPbGeRgjjb3Qzv4jwPp3fB0hui50WNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -1782,102 +1073,205 @@ packages: os: [linux] libc: [musl] + '@oxfmt/binding-linux-x64-musl@0.70.0': + resolution: {integrity: sha512-w+S+fERxYmlZSyZlJK/U292FjyBoH8cCEj21/tYJX6atX5kNSn+HDkhlFQKT2zcMwUW0uAtUL/bOrlwJZwqRdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@oxfmt/binding-openharmony-arm64@0.57.0': resolution: {integrity: sha512-4Oaxe1qrGgXfpCJ1C/ERJ2iCtV2rN1R79ga9fsfyVHfSQRu/hVW780u2KDqZWFZ/iGTHODJji0JemxqFZ63eIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@oxfmt/binding-openharmony-arm64@0.70.0': + resolution: {integrity: sha512-Zlom1Xkx257R8bk4ZI4zJsrGno2opknz1+5v5baka3nn4FPyvNSdh8JUL4CdN1S1OWRMvJ9UJQ+RfIqGGCUEfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@oxfmt/binding-win32-arm64-msvc@0.57.0': resolution: {integrity: sha512-MYLAsDnhdNsSGheLYhWgbk0vfIrlS84iQYun/y21fX6u0jj8iBtYtbpZMdiqYeuf8U12eVPUjVY2xE2NrCfJ0g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@oxfmt/binding-win32-arm64-msvc@0.70.0': + resolution: {integrity: sha512-FQgPW5R17vzt7cgrJ8eG/dqX00o2xHsqFeLfw4xzA9FRHpN/DjFo9YDonvIIXGxiEuS9F/jZPnGO+KHNKuCo4Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@oxfmt/binding-win32-ia32-msvc@0.57.0': resolution: {integrity: sha512-PBwdzZALJY/jcCx2E6is0yu+cuVXeySTDmwuseD+9j0mHqlRNxwlKgsyRTBed/woPeqfVfuXfWjoq4Cx2Zt3Eg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] + '@oxfmt/binding-win32-ia32-msvc@0.70.0': + resolution: {integrity: sha512-ZfZublNhZ+XBndMiXhkiLlPE+XyGRDa0CweeTL6t1fZypfCh1LTg7e5CvnOeTBunq15MskOcRempumSPGAaCQA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + '@oxfmt/binding-win32-x64-msvc@0.57.0': resolution: {integrity: sha512-bQJdH9i4RRfw55jm7+8/xS7GzHLLTbHx4huhrrDxQJaJtbSDbsyOnODvP1ftT7EG0KFKAYO2S+q6AcioXODx8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@oxfmt/binding-win32-x64-msvc@0.70.0': + resolution: {integrity: sha512-HlIZEn+WzLQL0DszNzldiRl/DPRCX5R0Vkt6qeUPR1YHwy52hZZo4x6HoTOVmKRP2wUiwPGtKsihNY/f8KRaBg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxlint-tsgolint/darwin-arm64@0.17.4': resolution: {integrity: sha512-XEA7vl/T1+wiVnMq2MR6u5OYr2pwKHiAPgklxpK8tPrjQ1ci/amNmwI8ECn6TPXSCsC8SJsSN5xvzXm5H3dTfw==} cpu: [arm64] os: [darwin] + '@oxlint-tsgolint/darwin-arm64@7.0.2003': + resolution: {integrity: sha512-TgV33rXr6ueXBwvc+0nssUkTBSXHJxv77I8p4RCjDjCnvexHtmoPiudVRDfj3S3puMDdeQdHW6jdUgUPy3nr/w==} + cpu: [arm64] + os: [darwin] + '@oxlint-tsgolint/darwin-x64@0.17.4': resolution: {integrity: sha512-EY2wmHWqkz72B0/ddMiAM564ZXpEuN1i7JqJJhLmDUQfiHX0/X0EqK3xlSScMCFcVicitOxbKO9oqbde3658yg==} cpu: [x64] os: [darwin] + '@oxlint-tsgolint/darwin-x64@7.0.2003': + resolution: {integrity: sha512-hY3FMAjIaPDdK3FNxyRXRfHPOFeoBn6dmnLyKZgQ2IbTTD1adXhl6PfCIqHhCPvurigv7nf7mYuWZZ19MmJzmg==} + cpu: [x64] + os: [darwin] + '@oxlint-tsgolint/linux-arm64@0.17.4': resolution: {integrity: sha512-XL2X8hgp3/TZWeHFLUnWrveTCBPxy1kNtpzfvVkLtBgyoaRyopPYL0Mnm+ypXKgGvUdcjDaiJhnRjFHWmqZkew==} cpu: [arm64] os: [linux] + '@oxlint-tsgolint/linux-arm64@7.0.2003': + resolution: {integrity: sha512-eAET4JpyfBbg8SO0K74o4R55tEjVC292pIyxGOw2XGf8x/HrPNyxwQ478jMYOM54FIVOHc8sJ6VRHxluy6lucw==} + cpu: [arm64] + os: [linux] + '@oxlint-tsgolint/linux-x64@0.17.4': resolution: {integrity: sha512-jT+aWtQuU8jefwfBLAZu16p4t8xUDjxL6KKlOeuwX3cS6NO60ITJ4Glm8eQYq5cGsOmYIKXNIe4ckPpL5LC+5g==} cpu: [x64] os: [linux] + '@oxlint-tsgolint/linux-x64@7.0.2003': + resolution: {integrity: sha512-GXdyO/XyqDJ3s/llR/oOktLsYNjZtWSQBy0JMc+/0gsNPvTcseKPhn9c6KcFyWmnr6H4vljYALbujonqSzzEGw==} + cpu: [x64] + os: [linux] + '@oxlint-tsgolint/win32-arm64@0.17.4': resolution: {integrity: sha512-pnnkBaI5tHBFhx+EhmpUHccBT3VOAXTgWK2eQBVLE4a/ywhpHN+8D6/QQN+ZTaA4LTkKowvlGD6vDOVP5KRPvw==} cpu: [arm64] os: [win32] + '@oxlint-tsgolint/win32-arm64@7.0.2003': + resolution: {integrity: sha512-TWauXnPfet0VgpmrstoAK58eJ4gbuwPUuUTQmYQAzE/RG1CpnxXtrKUJULf6lyerPE4KN3gM+DflIA1hOH+38Q==} + cpu: [arm64] + os: [win32] + '@oxlint-tsgolint/win32-x64@0.17.4': resolution: {integrity: sha512-JxT81aEUBNA/s01Ql2OQ2DLAsuM0M+mK9iLHunukOdPMhjA6NvFE/GtTablBYJKScK21d/xTvnoSLgQU3l22Cw==} cpu: [x64] os: [win32] + '@oxlint-tsgolint/win32-x64@7.0.2003': + resolution: {integrity: sha512-DoRmfe7j8VqNlukp8liRVW45GQDhzRccNenjD/pdzelgtffW47pCMd1xbJLkPaPbKnTwID3onn3VZlL7JbebEA==} + cpu: [x64] + os: [win32] + '@oxlint/binding-android-arm-eabi@1.72.0': resolution: {integrity: sha512-zhCmvn+1Mj3UchAc/90i99S0t7jJUsHmFVSPg4UWrjO8b8eaSGwscgO6QAUtvHBstkjQwBttQNswEnAF1mIQdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] + '@oxlint/binding-android-arm-eabi@1.85.0': + resolution: {integrity: sha512-q2KO/Zso9UT+OMn0NF9ywn4E4t0MI3yxiDhNyhsQ7DyQJrC4FhFE4TXOi4bktFnOWXTMds8qZSbpv2XwRaNOBg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + '@oxlint/binding-android-arm64@1.72.0': resolution: {integrity: sha512-mtH+aY/ozv1eZoCUC2owjFAtyNBKHpJHygKeEu9zXXnQGW1Q2/qOpvx+I+Lf23+TvTz66F4iiXUbl2cGvoLPCQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + '@oxlint/binding-android-arm64@1.85.0': + resolution: {integrity: sha512-SxLN3ALjoT9NNdvpjEevGeHvfzTAFrF0NBYB5tzK7/GtCKMze3j1e/m/X2ozqGj2U9hfGG/dg/OG8vpVK4PiDA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@oxlint/binding-darwin-arm64@1.72.0': resolution: {integrity: sha512-EvnajNPDtfknB3ZieeOOyDTwJn9QXDiwfnF4ZDQqART6RG6hjY4WigQcZdGoK2dkB3e1vrmEzN9aYbQCUkh/gQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@oxlint/binding-darwin-arm64@1.85.0': + resolution: {integrity: sha512-Y/Sup/J4f0f9UGsSd/xyCNTeWL+gepO63GBdEDAfue9nBsnk9zMmnIXx1O6b1V8C90vB5nucYNZ0pbMXAp8zJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@oxlint/binding-darwin-x64@1.72.0': resolution: {integrity: sha512-ZkCdEa/G80A7vEHfeCDz/+L3m33DE73v32mDKhgOIgz8Uwf0DFcK7+uu6qC+7LEhmz5fpOe1osWKyjSNMydFIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@oxlint/binding-darwin-x64@1.85.0': + resolution: {integrity: sha512-ApOSNC04ynpDTwvBD+//0wyfODRSbEzvRoKpX8teffmc27z8AockwSNeMXGJXn5KP85eahDgR/2llICWLkzcnw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@oxlint/binding-freebsd-x64@1.72.0': resolution: {integrity: sha512-NroXv2vh+sxVY1uya/rM5pjhx1hm8BzlYpx9q67QP0Xhw5MH2bf5GJylpvLEC+781p1Xli/317EoV9AlGwViag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@oxlint/binding-freebsd-x64@1.85.0': + resolution: {integrity: sha512-bNrVrCOA/kHky3Tu79IXWXe5bhIgLXfUuUEDHlAGOHUk96MkvDZ1ecaQF19rwstrnaqfP1o9nBTqzIr9+ZHkUg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@oxlint/binding-linux-arm-gnueabihf@1.72.0': resolution: {integrity: sha512-0NDywYgfj279Ou/BcQuCYSj7NJwBfmWn5qc5uGO/Ny7fUWmXyIpvawqX/8acQlWG6IXelJsJhj+JAy6sjsKj0A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@oxlint/binding-linux-arm-gnueabihf@1.85.0': + resolution: {integrity: sha512-NUrzOJ1s/EqsVvfn2L/1D8Wro2LPIZUbihL8kOJLh5fEdGEN3rdOGUYq3HwnUIL8sjpoP+4N6RaGrgmMJnaMPw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxlint/binding-linux-arm-musleabihf@1.72.0': resolution: {integrity: sha512-4vpXB06h65Ezsy4hRyrGjGrfa1SkVPii09yaajiYhmVpgsFiLD+KNxIx/BNAY+XiO+i1yqp9HHdwqM8VTqa5XQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@oxlint/binding-linux-arm-musleabihf@1.85.0': + resolution: {integrity: sha512-UJXrAT3E/RWkEqXLIs2ehETja1qfgkPb+5gwLIIS+o/6cf+grHvoOXTa5997a/YNQfcJS0DRBTOfZt95cvOI1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxlint/binding-linux-arm64-gnu@1.72.0': resolution: {integrity: sha512-immaN4g2ZGFiOkKrvRX9LvzZdd2GkQM5wR+UyzYyUuyhUTXGQ4HKUJH18xp4G8OfhCVaVAJfKZxwE1r8+4hhaQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1885,6 +1279,13 @@ packages: os: [linux] libc: [glibc] + '@oxlint/binding-linux-arm64-gnu@1.85.0': + resolution: {integrity: sha512-lK40QLjI0HxigO7CjDDshEtfYIeiYS0020v5BHFPqN4uuQBQxd2K9LNom2dW15o9F1937quSCRVp4ZsVhdbYdg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@oxlint/binding-linux-arm64-musl@1.72.0': resolution: {integrity: sha512-JGHS9Mnr7iWyyLDxgCv1MhzVpAckgptg00F2gnxt/GD7lQ2SW1BRcxHqhSTaSdDpjWRrBkBxMMh4+Hn3aVtExg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1892,6 +1293,13 @@ packages: os: [linux] libc: [musl] + '@oxlint/binding-linux-arm64-musl@1.85.0': + resolution: {integrity: sha512-c2zbdBwGKreHXwRx3gWBuFGJxLhxgsg6YlZ+3H+RgRusU/UEV9jNwJ3HGYK+nRo0LvBa7mt6Kj86xoVotUo8cw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@oxlint/binding-linux-ppc64-gnu@1.72.0': resolution: {integrity: sha512-AOYgBZqxNshrg83P9v0RYv+m8s10Cqkj4/PxXFDhcS3k7FqsIG5+CxErshZCIN7G8iy4Y+VGfAsuEdar8AcbBg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1899,6 +1307,13 @@ packages: os: [linux] libc: [glibc] + '@oxlint/binding-linux-ppc64-gnu@1.85.0': + resolution: {integrity: sha512-tlt/Hy8lZ97/lCPmCgw/B3k/mwh+BzaIPbPkldZEly7TwLmx0xe2CQcaW2g/rR0dOgS9JNGCZsMEqLhUNMGvaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@oxlint/binding-linux-riscv64-gnu@1.72.0': resolution: {integrity: sha512-QMybPS5ij3/vrKG67mqzHwW++91sYxK/PPUVi6SBtNCEzW4niS52fVBdXbQ6nou0wWbUPEpx8Sl/ZjtgE3clXA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1906,6 +1321,13 @@ packages: os: [linux] libc: [glibc] + '@oxlint/binding-linux-riscv64-gnu@1.85.0': + resolution: {integrity: sha512-3tNR9Xey82X0zKuY1d8hJ6Rc9gwRDurmqGLnQZa5xqOXy8/YyiqFXjAtugkKLY82obOlpK1eSiDRlgcNPuxtIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@oxlint/binding-linux-riscv64-musl@1.72.0': resolution: {integrity: sha512-gOc3W7JV0PXRpIL7stUlLe3Wa9Gp0Kdlup87IT3gHDvPKck2xNgMIl/Gs2lldYY2lyXZDC4rWi3hmoLUobkgbQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1913,6 +1335,13 @@ packages: os: [linux] libc: [musl] + '@oxlint/binding-linux-riscv64-musl@1.85.0': + resolution: {integrity: sha512-wbGRd5PqCcjkJFHhZuZ2OBSUQY9czlQsoA/cQQB9JK/L9mC5MQgGoKAh+xd8QjA5V+0D3j+Qd1lAWn1I8zlelA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + '@oxlint/binding-linux-s390x-gnu@1.72.0': resolution: {integrity: sha512-rpGxph+FjjHcYI5q6uxB3Az+tnfmEnDbSA8+PK9ZE/VzyUAkvBOMeuY7ZQMhu5mpZH7YQDsTdW6Cx4kV/msc6w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1920,6 +1349,13 @@ packages: os: [linux] libc: [glibc] + '@oxlint/binding-linux-s390x-gnu@1.85.0': + resolution: {integrity: sha512-3Sn0kSrE4DPZCWV/8o+n4x3aFZxI9ulMnkYlwCbJ8eUVkwRK2IerohE/A/z3SNbCwoPFOCJmGE5Avrq0rrvdvQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@oxlint/binding-linux-x64-gnu@1.72.0': resolution: {integrity: sha512-WND+uhf/Ko13SLqQMWQUgsZuLvYYEvL0ZKgg0tgGYfLqxG7l8Ju123fHDMJyYSDl5E3bUbpFUuii/OvMreFQzw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1927,6 +1363,13 @@ packages: os: [linux] libc: [glibc] + '@oxlint/binding-linux-x64-gnu@1.85.0': + resolution: {integrity: sha512-JY2pxxYfB62bAGfejljVCqc44etItehPuAyaeSAdMuEMtwNA00ggMnS66lC1oIhos6oOXUkuU6mZ9bpFh3BqWg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@oxlint/binding-linux-x64-musl@1.72.0': resolution: {integrity: sha512-SrpbrUL70nG9vh6zP4/oKHWgLuHquwsr7MW9XOn0olBVgh10Uqr8qscKhQoBGEn6olK/IUpn5GSKcdQ5AjUhGA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1934,49 +1377,70 @@ packages: os: [linux] libc: [musl] + '@oxlint/binding-linux-x64-musl@1.85.0': + resolution: {integrity: sha512-5k74vZ6qJBjBHEOlBk9B/iv68Yu0F1Afw/vvT2ar6OGCqEeXLaSjXz2n/IPCbhLG22UoKoYEJTzpYraRdcp6PA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@oxlint/binding-openharmony-arm64@1.72.0': resolution: {integrity: sha512-qkrsEn6NmgFKr7U/QnezQMb+q/vzAy0Dd9Y95gQGQTyjzDLN+HRZMuM5u70iyH4nBLCfKBzhjMsYCehKay2jyg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@oxlint/binding-openharmony-arm64@1.85.0': + resolution: {integrity: sha512-GbAl5qt5TCkPLXTaIISZJnugrcBhra6rodcXc9jYt620UtdsTt71NlNmJmm0frxzFpd54x/G+MkitEJA8I/BoA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@oxlint/binding-win32-arm64-msvc@1.72.0': resolution: {integrity: sha512-LWR6ZlFZph+KPjXv8opgZsXRDCdrdQe8VL8Cg9zxCoBS73h6znzZpydVgmdnwj8mB9AuSM5jxEgDJDpQkjboeg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@oxlint/binding-win32-arm64-msvc@1.85.0': + resolution: {integrity: sha512-kjmws5MK0et2swk4ND85D7NVQyDHw162i6whtZDLUA/lo6FQyBZDcmMRCMcVZcNrAhIaftVb00x9ChGDOjjNJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@oxlint/binding-win32-ia32-msvc@1.72.0': resolution: {integrity: sha512-yt6HEh7IsHvtjRWtmeZRX134eaXKHq5Gnqlf1xBJdJl1JtdoRUEJw3nAxpZoUDS860cX/foKbztO441anVBtVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] + '@oxlint/binding-win32-ia32-msvc@1.85.0': + resolution: {integrity: sha512-eSsIJx9n4yxvOqYTZyPEMyEXRmE60XH7xGAU7i0Qbsn1lf6Za3CWJ9aRd82oSFKXaxhp+sA6/yMJVRIpLpna6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + '@oxlint/binding-win32-x64-msvc@1.72.0': resolution: {integrity: sha512-b2eKFD2hX7tIwmo/cyH6TDq8vzWRZ2qNHrzoGntUTmq0h3zQh/uX3eTSHCwI8OB/ADQfJCRelLItK8BsxuucDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@paralleldrive/cuid2@2.3.1': - resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} + '@oxlint/binding-win32-x64-msvc@1.85.0': + resolution: {integrity: sha512-pBebIPUpKKhWrhSMWhy8TdAZBewiXnfxmaAGxhzxM1068GagqFaTwgKlU6e+UyJ2sPR+VoHouhXuGJkQjsrDvA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] '@phc/format@1.0.0': resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} engines: {node: '>=10'} - '@pinojs/redact@0.4.0': - resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} - - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@prisma/adapter-pg@7.4.1': - resolution: {integrity: sha512-AH9XrqvSoBAaStn0Gm/sAnF97pDKz8uLpNmn51j1S9O9dhUva6LIxGdoDiiU9VXRIR89wAJXsvJSy+mK40m2xw==} + '@prisma/adapter-pg@7.10.0': + resolution: {integrity: sha512-N7nwSor0HO1Kz6xBv0TPAjAPysKK0fac6p4fVN3ensLOuzc/83Fgmln5k92eK/cvzqdkSR/2kkAqlbcdwVrwpw==} '@prisma/client-runtime-utils@7.4.1': resolution: {integrity: sha512-8fy74OMYC7mt9cJ2MncIDk1awPRgmtXVvwTN2FlW4JVhbck8Dgt0wTkhPG85myfj4ZeP2stjF9Sdg12n5HrpQg==} @@ -1993,46 +1457,125 @@ packages: typescript: optional: true - '@prisma/config@7.4.1': - resolution: {integrity: sha512-vteSXm8N46bo3FW9MhPGVHAj+KRgrR6TWtlSk6GqToCKjTnOexXdPZyiDyEsfVW38YhqEmVl6w/6iHN8uYVJcw==} + '@prisma/config@7.10.0': + resolution: {integrity: sha512-Rcg828gIRE3HOQ3pOATFjV5d/P0U9OIobxhd/IMxlfWjA4vru0eGwb0AIwFw0rmcLMVShohZYWPixVxkBHsxUA==} + + '@prisma/debug@7.10.0': + resolution: {integrity: sha512-caygJKtltmRIgdJ3jRpkOr7yM4DW6zxo5uOmojKWFb3asnxWoRkQOwZmXBgD8FZp4htrX+nMpcWqDwzlQ1+Y4g==} '@prisma/debug@7.2.0': resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} - '@prisma/debug@7.4.1': - resolution: {integrity: sha512-qEtzO8oLouRv18JDQUC3G3Gnv+fGVscHZm/x1DBB/WT+kOvPDQLM2woX6IGgWnSMYYlrxjuALshT7G/blvY0bQ==} + '@prisma/dev@0.24.17': + resolution: {integrity: sha512-UvdZzmpFwknnfreh6Jije84ekkYGPYEJhXG1tFzCsCfQyzJifrOo38eZc0qajzvaC6OLUOrN9ML5XfCnEZL9DA==} - '@prisma/dev@0.20.0': - resolution: {integrity: sha512-ovlBYwWor0OzG+yH4J3Ot+AneD818BttLA+Ii7wjbcLHUrnC4tbUPVGyNd3c/+71KETPKZfjhkTSpdS15dmXNQ==} + '@prisma/driver-adapter-utils@7.10.0': + resolution: {integrity: sha512-u8zkcRLlaryO652T4qavBg0HmzNW5tSKdsCn6hc1PhWAp/J6k0vrxLuUs+b9o+HcjsK7Dfa01o4OFSn0frauJA==} - '@prisma/driver-adapter-utils@7.4.1': - resolution: {integrity: sha512-gEZOC2tnlHaZNbHUdbK8YvQphq2tKq/Ovu1YixJ/hPSutDAvNzC3R+xUeBuJ4AJp236eELMzwxb7rgo3UbRkTg==} + '@prisma/engines-version@7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3': + resolution: {integrity: sha512-8OJ6RuZTZ06eFUOtBwxVmv8XMmOW6HWN5F+uxUbZkGxR0Bfab1dfAdXaHPmR5mb59E+fmUo8IOzXlbLY1SClbw==} - '@prisma/engines-version@7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3': - resolution: {integrity: sha512-fUxVd1TjOW8K4XsZ8dAm88sDW5Ry7AxWDfsYEWwScS6Fjo3caKC6hgNumUfsmsy0Il9LjDn5X0PpVXNt3iwayw==} + '@prisma/engines@7.10.0': + resolution: {integrity: sha512-KNumN6NHFwybvfdYzTee9pqwx5PvknpWAaHn6L5NsbrKdl+SQrsVZs9opKs6U6SAsvB26HDt3WybRjOhgoWOYQ==} - '@prisma/engines@7.4.1': - resolution: {integrity: sha512-BZEBdHvNJx5PzIG37EI/Zi5UUI5hGWjkYsQmKa7OIK6evAvebOTwutjS/VRI6cA6grmA52eLZR+oekGRMqkKxQ==} + '@prisma/fetch-engine@7.10.0': + resolution: {integrity: sha512-Zqyu8DY14t6W/xwmAxUYWCXtHrvQnSvT644EAZSsdM8NSmCS74vJJbBKdVsK3ucFpnUWkEpbO1a0CxJXrg130g==} - '@prisma/fetch-engine@7.4.1': - resolution: {integrity: sha512-Z9kbuxX2bvEsyeS3LZEiEnxG0lVtZbpYgaAnPj69N+A9f2De8Lta0EoFtld9zhfERVPIQWhSWUc8himky3qYdA==} + '@prisma/get-platform@7.10.0': + resolution: {integrity: sha512-0bra1LFYi8xNw0yqV62bHJNQk4BKleOngZiqPWIQc7a3+9q6rqsnqJ15BuepP8943PFMvtmgnP52juZsyYkA6w==} '@prisma/get-platform@7.2.0': resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} - '@prisma/get-platform@7.4.1': - resolution: {integrity: sha512-kN4tmkQzlgm/KtE+jTNSYjsDxxe/5i6GApPI32BN9T0tlgsgSBtDJbjGBICttkAIjsh73dXf8raPKxO/2n2UUg==} - '@prisma/query-plan-executor@7.2.0': resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} - '@prisma/studio-core@0.13.1': - resolution: {integrity: sha512-agdqaPEePRHcQ7CexEfkX1RvSH9uWDb6pXrZnhCRykhDFAV0/0P3d07WtfiY8hZWb7oRU4v+NkT4cGFHkQJIPg==} + '@prisma/streams-local@0.1.11': + resolution: {integrity: sha512-0TcebL559MByKqTJ+SsrFIEg228iw8UCVRFckzgfRSiJqczhs+MuAgWOF9lnOIV/IVqvu+KMnFTH0eDeTQMpUg==} + engines: {bun: '>=1.2.0', node: '>=22.0.0'} + + '@prisma/studio-core@0.33.0': + resolution: {integrity: sha512-V2fX/nKEymNTrHXwfP26PGjoLStO35Ogu+ex7CFJbLrMYEcZxxZpiSNOs7px23Hk5mzLWvM5RsqG6Ka+rha+wg==} + engines: {node: ^20.19 || ^22.12 || >=24.0, pnpm: '8'} peerDependencies: '@types/react': ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@rollup/plugin-commonjs@29.0.2': resolution: {integrity: sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -2207,30 +1750,31 @@ packages: cpu: [x64] os: [win32] - '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - - '@scarf/scarf@1.4.0': - resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + '@scalar/client-side-rendering@0.4.5': + resolution: {integrity: sha512-6dhBKaRecrrk2WyVLxrA8oyTiY+4zzhwf5OUjUyuyQXZHt/751QDf/TvW14p2osEaNELfY/gjtN8meX+UslFQw==} + engines: {node: '>=22'} - '@sinclair/typebox@0.27.10': - resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} + '@scalar/helpers@0.15.0': + resolution: {integrity: sha512-eUnaVQ1EE2YFIVrPOQxVaUiVYZjM6Yg2Oh0yUI87pyI0ilH6jOLA6wIJVWP/AXLv5WnhsfJW33xlZcztozEZ2A==} + engines: {node: '>=22'} - '@sinclair/typebox@0.34.48': - resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} - - '@sindresorhus/is@5.6.0': - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} + '@scalar/hono-api-reference@0.12.6': + resolution: {integrity: sha512-u5CXaHTz8K9OZP5Yk/HungHJc4sqtQ1Y4Hf3NdY1EYsrUrgtCKdDn8G2xLKP2u+8+FICeUHx6IjPjEn3BkCCKg==} + engines: {node: '>=22'} + peerDependencies: + hono: ^4.12.5 - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@scalar/schemas@0.12.1': + resolution: {integrity: sha512-D/1h/ek2mCwZFOBHQQ8gqD/pQ6Dz5xwur3s6aCwA71AnDF8/Wh+nwUNyIz+H4iquYIq5AUQD02mRyhUE/nIqtg==} + engines: {node: '>=22'} - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@scalar/types@0.22.1': + resolution: {integrity: sha512-h8/oe1tG1UIPoAKETiOBtYpyoH7GW3mp4Yu3Znl1RWa2iYLWl7ZtnqJ2A+n5s7MvZEDAb33pt4W/eswB3VVr6Q==} + engines: {node: '>=22'} - '@sinonjs/fake-timers@13.0.5': - resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} + '@scalar/validation@0.6.5': + resolution: {integrity: sha512-t/dtbqesbOLsTusFI2kXfiFEQOiZC7+oAY+jZnE82Y64xLamo/1KrZpta8GkuAfIHyeOHOx5bDFid04eO13MIg==} + engines: {node: '>=20'} '@skeletonlabs/skeleton-common@4.15.2': resolution: {integrity: sha512-y7KZn++Av8UHdoeaaguQ7zIS1HlK7Y5jyPDnHy65wJ60iS97fMtQV0kGhqVoYCWhor+xrjbAFjWtaPOPPDEMYA==} @@ -2245,8 +1789,65 @@ packages: peerDependencies: tailwindcss: ^4.0.0 - '@so-ric/colorspace@1.1.6': - resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@standard-community/standard-json@0.3.6': + resolution: {integrity: sha512-tuXKz1Ps4al0W4xhfwii7v4lskKtOv3QE7mcouRVThrBdOR6JzY4Vea3nsxcEnte84bOJvMvXGmOMWE3IBbHyA==} + peerDependencies: + '@standard-schema/spec': ^1.0.0 + '@types/json-schema': ^7.0.15 + '@valibot/to-json-schema': ^1.3.0 + arktype: ^2.1.20 + effect: ^3.20.0 + sury: ^10.0.0 + typebox: ^1.0.17 + valibot: ^1.4.2 + zod: ^3.25.0 || ^4.0.0 + zod-to-json-schema: ^3.24.5 + peerDependenciesMeta: + '@valibot/to-json-schema': + optional: true + arktype: + optional: true + effect: + optional: true + sury: + optional: true + typebox: + optional: true + valibot: + optional: true + zod: + optional: true + zod-to-json-schema: + optional: true + + '@standard-community/standard-openapi@0.2.10': + resolution: {integrity: sha512-whaCN9eu5zv5gu6Kyeh5am/S3AyS1nqo57/wAxs/KPS7+94UhBUZmkIetmXzm22qIV0zMrHt3y+2VfjkbkYYTA==} + peerDependencies: + '@standard-community/standard-json': ^0.3.5 + '@standard-schema/spec': ^1.0.0 + arktype: ^2.1.20 + effect: ^3.20.0 + openapi-types: ^12.1.3 + sury: ^10.0.0 + typebox: ^1.0.0 + valibot: ^1.4.2 + zod: ^3.25.0 || ^4.0.0 + zod-openapi: ^4 + peerDependenciesMeta: + arktype: + optional: true + effect: + optional: true + sury: + optional: true + typebox: + optional: true + valibot: + optional: true + zod: + optional: true + zod-openapi: + optional: true '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2301,103 +1902,9 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 - '@swc/cli@0.6.0': - resolution: {integrity: sha512-Q5FsI3Cw0fGMXhmsg7c08i4EmXCrcl+WnAxb6LYOLHw4JFFC3yzmx9LaXZ7QMbA+JZXbigU2TirI7RAfO0Qlnw==} - engines: {node: '>= 16.14.0'} - hasBin: true - peerDependencies: - '@swc/core': ^1.2.66 - chokidar: ^4.0.1 - peerDependenciesMeta: - chokidar: - optional: true - - '@swc/core-darwin-arm64@1.15.11': - resolution: {integrity: sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==} - engines: {node: '>=10'} - cpu: [arm64] - os: [darwin] - - '@swc/core-darwin-x64@1.15.11': - resolution: {integrity: sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==} - engines: {node: '>=10'} - cpu: [x64] - os: [darwin] - - '@swc/core-linux-arm-gnueabihf@1.15.11': - resolution: {integrity: sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux] - - '@swc/core-linux-arm64-gnu@1.15.11': - resolution: {integrity: sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@swc/core-linux-arm64-musl@1.15.11': - resolution: {integrity: sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@swc/core-linux-x64-gnu@1.15.11': - resolution: {integrity: sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@swc/core-linux-x64-musl@1.15.11': - resolution: {integrity: sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@swc/core-win32-arm64-msvc@1.15.11': - resolution: {integrity: sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - - '@swc/core-win32-ia32-msvc@1.15.11': - resolution: {integrity: sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==} - engines: {node: '>=10'} - cpu: [ia32] - os: [win32] - - '@swc/core-win32-x64-msvc@1.15.11': - resolution: {integrity: sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - - '@swc/core@1.15.11': - resolution: {integrity: sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==} - engines: {node: '>=10'} - peerDependencies: - '@swc/helpers': '>=0.5.17' - peerDependenciesMeta: - '@swc/helpers': - optional: true - - '@swc/counter@0.1.3': - resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.18': resolution: {integrity: sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==} - '@swc/types@0.1.25': - resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} - '@tailwindcss/node@4.3.1': resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} @@ -2492,67 +1999,50 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tokenizer/inflate@0.2.7': - resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} - engines: {node: '>=18'} - - '@tokenizer/inflate@0.4.1': - resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} - engines: {node: '>=18'} - - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/cookiejar@2.1.5': - resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/d3-array@3.0.3': + resolution: {integrity: sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==} '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + '@types/d3-color@3.1.0': + resolution: {integrity: sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==} + '@types/d3-contour@3.0.6': resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + '@types/d3-delaunay@6.0.1': + resolution: {integrity: sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==} + + '@types/d3-format@3.0.1': + resolution: {integrity: sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-interpolate@3.0.1': + resolution: {integrity: sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==} + '@types/d3-path@3.1.1': resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + '@types/d3-scale@4.0.2': + resolution: {integrity: sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + '@types/d3-shape@3.1.8': resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} - '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + '@types/d3-time-format@2.1.0': + resolution: {integrity: sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==} - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + '@types/d3-time@3.0.0': + resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2560,41 +2050,11 @@ packages: '@types/geojson@7946.0.16': resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - - '@types/http-cache-semantics@4.2.0': - resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} - - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@30.0.0': - resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - - '@types/jsonwebtoken@9.0.10': - resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} - - '@types/methods@1.1.4': - resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} - - '@types/mocha@10.0.10': - resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/lodash@4.17.25': + resolution: {integrity: sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==} '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -2602,8 +2062,11 @@ packages: '@types/node@22.19.11': resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} - '@types/pg@8.16.0': - resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} + '@types/node@26.6.3': + resolution: {integrity: sha512-dsqMQQoeTLqu9wynDD00q573mNzso3IdQOAfHRJqLCcmCFPoGo9A1bDpUcv/9tnKpErQWv9uKeGfl37EIS02Yg==} + + '@types/pg@8.23.1': + resolution: {integrity: sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==} '@types/react@19.2.11': resolution: {integrity: sha512-tORuanb01iEzWvMGVGv2ZDhYZVeRMrw453DCSAIn/5yvcSVnMoUMTyf33nQJLahYEnv9xqrTNbgz4qY5EfSh0g==} @@ -2611,285 +2074,167 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - - '@types/superagent@8.1.9': - resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} - - '@types/supertest@6.0.3': - resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} - - '@types/triple-beam@1.3.5': - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - '@types/validator@13.15.10': - resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - - '@typescript-eslint/eslint-plugin@8.56.0': - resolution: {integrity: sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.56.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/parser@8.56.0': - resolution: {integrity: sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.56.0': - resolution: {integrity: sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/scope-manager@8.56.0': - resolution: {integrity: sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.56.0': - resolution: {integrity: sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.56.0': - resolution: {integrity: sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.56.0': resolution: {integrity: sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.56.0': - resolution: {integrity: sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.56.0': - resolution: {integrity: sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.56.0': - resolution: {integrity: sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} - cpu: [arm] - os: [android] - - '@unrs/resolver-binding-android-arm64@1.11.1': - resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} - cpu: [arm64] - os: [android] + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] - '@unrs/resolver-binding-darwin-arm64@1.11.1': - resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] - '@unrs/resolver-binding-darwin-x64@1.11.1': - resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] - '@unrs/resolver-binding-freebsd-x64@1.11.1': - resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} cpu: [x64] os: [freebsd] - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} - cpu: [arm] + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] os: [linux] - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} - cpu: [arm64] + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] os: [linux] - libc: [glibc] - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} - cpu: [arm64] + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] os: [linux] - libc: [musl] - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} cpu: [ppc64] os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} cpu: [riscv64] os: [linux] - libc: [musl] - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} cpu: [s390x] os: [linux] - libc: [glibc] - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] - libc: [glibc] - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} - cpu: [x64] - os: [linux] - libc: [musl] + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} cpu: [arm64] - os: [win32] + os: [openbsd] - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} - cpu: [ia32] + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] os: [win32] - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] - '@webassemblyjs/ast@1.14.1': - resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + '@visx/curve@4.0.1-alpha.0': + resolution: {integrity: sha512-jRu61Uz274pV1zyioXmboyrLutYbnKsgjj4njSGCnhdXj5GkZvZbg+ThDb6oOzoAnJOBRLz4rzPlWvNJOzuVMg==} - '@webassemblyjs/floating-point-hex-parser@1.13.2': - resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + '@visx/event@4.0.1-alpha.0': + resolution: {integrity: sha512-EQqCMSv/s8NbFjo+hz3FKsvvYfP+2QslsFJ/24/O5l/W+7UC6J6aAvO0ujVwrTwdYbuQ+vhxKi1xdPdKR/qj1g==} - '@webassemblyjs/helper-api-error@1.13.2': - resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + '@visx/grid@4.0.1-alpha.0': + resolution: {integrity: sha512-rycutGmTHO+znNdPumheWMglm7YfpffvRwUkVy5zy4WoORIuKTMkDxwnOzHG2xMxU3EE/YCd37xFV5AxA30yeg==} + peerDependencies: + react: ^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0 - '@webassemblyjs/helper-buffer@1.14.1': - resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + '@visx/group@4.0.1-alpha.0': + resolution: {integrity: sha512-V19l7iQ7jccBv8kao/EByuI6o4xtxzzLV9nqVI1hRvmdzTVsuLpqlwzYCZUXJaTVvUWf8s4D2SQFjGkj/Nw+0w==} + peerDependencies: + react: ^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0 - '@webassemblyjs/helper-numbers@1.13.2': - resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + '@visx/point@4.0.1-alpha.0': + resolution: {integrity: sha512-ijTfr/Nx09f03vIj9nyTr3z4Xth4Y75427UaogJh6dnIRLMEFHQOwNu791sbfiNj0a+ZXuaE32h0vKrFe4/8Qg==} - '@webassemblyjs/helper-wasm-bytecode@1.13.2': - resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - - '@webassemblyjs/helper-wasm-section@1.14.1': - resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - - '@webassemblyjs/ieee754@1.13.2': - resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - - '@webassemblyjs/leb128@1.13.2': - resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - - '@webassemblyjs/utf8@1.13.2': - resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - - '@webassemblyjs/wasm-edit@1.14.1': - resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - - '@webassemblyjs/wasm-gen@1.14.1': - resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - - '@webassemblyjs/wasm-opt@1.14.1': - resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - - '@webassemblyjs/wasm-parser@1.14.1': - resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - - '@webassemblyjs/wast-printer@1.14.1': - resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - - '@xhmikosr/archive-type@7.1.0': - resolution: {integrity: sha512-xZEpnGplg1sNPyEgFh0zbHxqlw5dtYg6viplmWSxUj12+QjU9SKu3U/2G73a15pEjLaOqTefNSZ1fOPUOT4Xgg==} - engines: {node: '>=18'} - - '@xhmikosr/bin-check@7.1.0': - resolution: {integrity: sha512-y1O95J4mnl+6MpVmKfMYXec17hMEwE/yeCglFNdx+QvLLtP0yN4rSYcbkXnth+lElBuKKek2NbvOfOGPpUXCvw==} - engines: {node: '>=18'} - - '@xhmikosr/bin-wrapper@13.2.0': - resolution: {integrity: sha512-t9U9X0sDPRGDk5TGx4dv5xiOvniVJpXnfTuynVKwHgtib95NYEw4MkZdJqhoSiz820D9m0o6PCqOPMXz0N9fIw==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-tar@8.1.0': - resolution: {integrity: sha512-m0q8x6lwxenh1CrsTby0Jrjq4vzW/QU1OLhTHMQLEdHpmjR1lgahGz++seZI0bXF3XcZw3U3xHfqZSz+JPP2Gg==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-tarbz2@8.1.0': - resolution: {integrity: sha512-aCLfr3A/FWZnOu5eqnJfme1Z1aumai/WRw55pCvBP+hCGnTFrcpsuiaVN5zmWTR53a8umxncY2JuYsD42QQEbw==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-targz@8.1.0': - resolution: {integrity: sha512-fhClQ2wTmzxzdz2OhSQNo9ExefrAagw93qaG1YggoIz/QpI7atSRa7eOHv4JZkpHWs91XNn8Hry3CwUlBQhfPA==} - engines: {node: '>=18'} - - '@xhmikosr/decompress-unzip@7.1.0': - resolution: {integrity: sha512-oqTYAcObqTlg8owulxFTqiaJkfv2SHsxxxz9Wg4krJAHVzGWlZsU8tAB30R6ow+aHrfv4Kub6WQ8u04NWVPUpA==} - engines: {node: '>=18'} - - '@xhmikosr/decompress@10.2.0': - resolution: {integrity: sha512-MmDBvu0+GmADyQWHolcZuIWffgfnuTo4xpr2I/Qw5Ox0gt+e1Be7oYqJM4te5ylL6mzlcoicnHVDvP27zft8tg==} - engines: {node: '>=18'} - - '@xhmikosr/downloader@15.2.0': - resolution: {integrity: sha512-lAqbig3uRGTt0sHNIM4vUG9HoM+mRl8K28WuYxyXLCUT6pyzl4Y4i0LZ3jMEsCYZ6zjPZbO9XkG91OSTd4si7g==} - engines: {node: '>=18'} + '@visx/responsive@4.0.1-alpha.0': + resolution: {integrity: sha512-o+1zGywQZY0+yOx3Iw87wc4bbPJRr/HnIukTwfOz4UVyj9pB1OQNVHB7OORO1+LBHJceWpB31co/ZV9KHncKrA==} + peerDependencies: + react: ^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0 - '@xhmikosr/os-filter-obj@3.0.0': - resolution: {integrity: sha512-siPY6BD5dQ2SZPl3I0OZBHL27ZqZvLEosObsZRQ1NUB8qcxegwt0T9eKtV96JMFQpIz1elhkzqOg4c/Ri6Dp9A==} - engines: {node: ^14.14.0 || >=16.0.0} + '@visx/scale@4.0.1-alpha.0': + resolution: {integrity: sha512-nzjeE87vFSAXGWFiiNfBpNLAf0Q8Qmf6syvKLjqNi4kGZkdhbUll3E/59YsgWXmjM8+llPLWzGsP+JPvo5eq1A==} - '@xtuc/ieee754@1.2.0': - resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + '@visx/shape@4.0.1-alpha.0': + resolution: {integrity: sha512-62QeiVNmPlterQGwhkEDcbq7M0MqY0lBsK5QKXtM9ZoPZWkuGV3aykA3+Xu20B2FAvyJq4LqJzBc7Sxr+EAdbA==} + peerDependencies: + react: ^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0 - '@xtuc/long@4.2.2': - resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + '@visx/vendor@4.0.0-alpha.0': + resolution: {integrity: sha512-6I+MuqXBcv9jnlcVowHoHKSdk9gXTWkHLKyqBwRWg7LY6A3Ei8SHfubpqGV5rBUSppxMq2RszPJUS6w+H0YgmQ==} '@zag-js/accordion@1.39.1': resolution: {integrity: sha512-GA3m7gRTm3weSe1eMlHIsTNztcjZ6joIaRgxxKil7q/UX0xIVVGDy0aCr6oo7FAuoMiOOBVurYXILpFZ30nOXA==} @@ -3038,69 +2383,11 @@ packages: '@zag-js/utils@1.39.1': resolution: {integrity: sha512-9k741cH7L655Ua3tedTkuMblcXVXVgCLTB9svp9oTjA7oatpOpYF4z43kgAQVjyThNXMJ7AvtO4C80ajQLTScg==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - - abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - acorn-import-phases@1.0.4: - resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} - engines: {node: '>=10.13.0'} - peerDependencies: - acorn: ^8.14.0 - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} - acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - - ajv-keywords@5.1.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} - peerDependencies: - ajv: ^8.8.2 - - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} @@ -3108,10 +2395,6 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3120,29 +2403,8 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} - engines: {node: '>=14'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - append-field@1.0.0: - resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - - arch@3.0.0: - resolution: {integrity: sha512-AmIAC+Wtm2AU8lGfTtHsw0Y9Qtftx2YXEEtiBP10xFUtMOA+sHHx6OAddyL52mUKh1vsXQ6/w1mVDptZCyUt4Q==} - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argon2@0.44.0: - resolution: {integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==} + argon2@0.45.1: + resolution: {integrity: sha512-skm+/WCjkGqCQxF7FG1LuZXM5yvbFjgbfiCGsud2oLgaDhh6b6dbH0b1EkghbM+xx4Bj8Ape+KKgixoIlWZicQ==} engines: {node: '>=16.17.0'} argparse@1.0.10: @@ -3155,64 +2417,13 @@ packages: resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} engines: {node: '>= 0.4'} - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} - array-includes@3.1.9: - resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} - engines: {node: '>= 0.4'} - - array-timsort@1.0.3: - resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} - array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - array.prototype.findlastindex@1.2.6: - resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - - asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - avvio@9.2.0: - resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==} - aws-ssl-profiles@1.1.2: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} @@ -3221,354 +2432,81 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} - b4a@1.8.0: - resolution: {integrity: sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==} - peerDependencies: - react-native-b4a: '*' - peerDependenciesMeta: - react-native-b4a: - optional: true - - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-istanbul@7.0.1: - resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} - engines: {node: '>=12'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - babel-preset-current-node-syntax@1.2.0: - resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} - peerDependencies: - '@babel/core': ^7.0.0 || ^8.0.0-0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - balanced-match@4.0.3: - resolution: {integrity: sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==} - engines: {node: 20 || >=22} - - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - bare-events@2.8.2: - resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} - peerDependencies: - bare-abort-controller: '*' - peerDependenciesMeta: - bare-abort-controller: - optional: true - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} - engines: {node: '>=6.0.0'} - hasBin: true - better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - bin-version-check@5.1.0: - resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==} - engines: {node: '>=12'} - - bin-version@6.0.0: - resolution: {integrity: sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==} - engines: {node: '>=12'} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - body-parser@2.3.0: - resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} - engines: {node: '>=18'} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - brace-expansion@5.0.2: - resolution: {integrity: sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==} - engines: {node: 20 || >=22} - - brace-expansion@5.0.7: - resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} - engines: {node: 18 || 20 || >=22} + better-result@2.10.0: + resolution: {integrity: sha512-oQhh0y1qo2/ZKdAAEvHZAqKKiHOFU5k/bW96fE2ScgQOVkJRiHwB+nOS1SgFsYqRlxMDWvefXi9Q3px7QvgNDw==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - 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'} - - c12@3.1.0: - resolution: {integrity: sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==} + c12@3.3.4: + resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} peerDependencies: - magicast: ^0.3.5 + magicast: '*' peerDependenciesMeta: magicast: optional: true - cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.9: - resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001770: - resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - chevrotain@10.5.0: - resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - chrome-trace-event@1.0.4: - resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} - engines: {node: '>=6.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} - engines: {node: '>=8'} - - citty@0.1.6: - resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - - citty@0.2.1: - resolution: {integrity: sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==} - - cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - - class-transformer@0.5.1: - resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} - - class-validator@0.14.3: - resolution: {integrity: sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} - - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.3: - resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-convert@3.1.3: - resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==} - engines: {node: '>=14.6'} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-name@2.1.0: - resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==} - engines: {node: '>=12.20'} - - color-string@2.1.4: - resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==} - engines: {node: '>=18'} - - color@5.0.3: - resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==} - engines: {node: '>=18'} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - comment-json@4.4.1: - resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} - engines: {node: '>= 6'} - commondir@1.0.1: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} - component-emitter@1.3.1: - resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} - confbox@0.2.4: resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} - consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} - - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - - content-disposition@1.1.0: - resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - content-type@2.0.0: - resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} - engines: {node: '>=18'} - conventional-changelog-angular@8.1.0: resolution: {integrity: sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==} engines: {node: '>=18'} @@ -3582,35 +2520,10 @@ packages: engines: {node: '>=18'} hasBin: true - 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.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cookie@1.1.1: - resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} - engines: {node: '>=18'} - - cookiejar@2.1.4: - resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - cosmiconfig-typescript-loader@6.2.0: resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} engines: {node: '>=v18'} @@ -3619,15 +2532,6 @@ packages: cosmiconfig: '>=9' typescript: '>=5' - cosmiconfig@8.3.6: - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -3637,14 +2541,6 @@ packages: typescript: optional: true - create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - cross-env@10.1.0: resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} engines: {node: '>=20'} @@ -3660,6 +2556,10 @@ packages: d3-array@2.12.1: resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + d3-array@3.2.1: + resolution: {integrity: sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==} + engines: {node: '>=12'} + d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} engines: {node: '>=12'} @@ -3676,6 +2576,10 @@ packages: resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} engines: {node: '>=12'} + d3-delaunay@6.0.2: + resolution: {integrity: sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==} + engines: {node: '>=12'} + d3-delaunay@6.0.4: resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} engines: {node: '>=12'} @@ -3693,6 +2597,10 @@ packages: resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} engines: {node: '>=12'} + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + d3-format@3.1.2: resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} engines: {node: '>=12'} @@ -3701,6 +2609,10 @@ packages: resolution: {integrity: sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==} engines: {node: '>=12'} + d3-geo@3.1.0: + resolution: {integrity: sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==} + engines: {node: '>=12'} + d3-geo@3.1.1: resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} engines: {node: '>=12'} @@ -3772,50 +2684,6 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - dedent@1.7.1: - resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge-ts@7.1.5: resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} engines: {node: '>=16.0.0'} @@ -3824,43 +2692,16 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - defaults@2.0.2: - resolution: {integrity: sha512-cuIw0PImdp76AOfgkjbW4VhQODRmNNcKR73vdCH5cLd/ifj7aamfoXvYgfGkEAjNJZ3ozMIy9Gu2LutUkGEPbA==} - engines: {node: '>=16'} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} delaunator@5.1.0: resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -3876,85 +2717,31 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - devalue@5.8.1: resolution: {integrity: sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==} - dezalgo@1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} - engines: {node: '>=0.3.1'} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} - dotenv-cli@10.0.0: - resolution: {integrity: sha512-lnOnttzfrzkRx2echxJHQRB6vOAMSCzzZg79IxpC00tU42wZPuZkQxNNrrwVAxaQZIIh001l4PxVlCrBxngBzA==} - hasBin: true - - dotenv-expand@11.0.7: - resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} - engines: {node: '>=12'} - - dotenv-expand@12.0.3: - resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} - engines: {node: '>=12'} - - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - - dotenv@17.2.3: - resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} - engines: {node: '>=12'} - dotenv@17.3.1: resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} engines: {node: '>=12'} - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - duplexify@3.7.1: - resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} - - duplexify@4.1.3: - resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - effect@3.18.4: - resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} + dotenv@18.0.4: + resolution: {integrity: sha512-jGt9uI5BIxpVO15kvy+iEFqNAwxYClKWnkptLjSNPnFLC003Imd0ofr3aEVsbpHdSBdSsbcApNIlz4nbyGMwdA==} + engines: {node: '>=12'} + hasBin: true - electron-to-chromium@1.5.302: - resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + effect@3.20.0: + resolution: {integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==} - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} + elkjs@0.11.1: + resolution: {integrity: sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3963,20 +2750,6 @@ packages: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} - enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - enhanced-resolve@5.19.0: - resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} - engines: {node: '>=10.13.0'} - enhanced-resolve@5.21.6: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} @@ -3989,40 +2762,13 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-abstract@1.24.2: - resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - es-toolkit@1.49.0: resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} @@ -4031,159 +2777,23 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.2: + resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-config-prettier@10.1.8: - resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-import-context@0.1.9: - resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - peerDependencies: - unrs-resolver: ^1.0.0 - peerDependenciesMeta: - unrs-resolver: - optional: true - - eslint-import-resolver-node@0.3.10: - resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} - - eslint-import-resolver-typescript@4.4.4: - resolution: {integrity: sha512-1iM2zeBvrYmUNTj2vSC/90JTHDth+dfOfiNKkxApWRsTJYNrc8rOdxxIf5vazX+BiAXTeOT0UvWpGI/7qIWQOw==} - engines: {node: ^16.17.0 || >=18.6.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - eslint-plugin-import-x: '*' - peerDependenciesMeta: - eslint-plugin-import: - optional: true - eslint-plugin-import-x: - optional: true - - eslint-module-utils@2.12.1: - resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.32.0: - resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-jest@29.15.0: - resolution: {integrity: sha512-ZCGr7vTH2WSo2hrK5oM2RULFmMruQ7W3cX7YfwoTiPfzTGTFBMmrVIz45jZHd++cGKj/kWf02li/RhTGcANJSA==} - engines: {node: ^20.12.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@typescript-eslint/eslint-plugin': ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - jest: '*' - typescript: '>=4.8.4 <6.0.0' - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - jest: - optional: true - typescript: - optional: true - - eslint-plugin-prettier@5.5.5: - resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@5.0.1: - resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - eslint@9.39.3: - resolution: {integrity: sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - esm-env@1.2.2: resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} - engines: {node: '>=0.10'} - esrap@2.2.13: resolution: {integrity: sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==} peerDependencies: @@ -4192,75 +2802,16 @@ packages: '@typescript-eslint/types': optional: true - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events-universal@1.0.1: - resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - eventsource-parser@3.0.8: resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} engines: {node: '>=18.0.0'} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - expect@30.2.0: - resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} - ext-list@2.2.2: - resolution: {integrity: sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==} - engines: {node: '>=0.10.0'} - - ext-name@5.0.0: - resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} - engines: {node: '>=4'} - extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -4274,46 +2825,19 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-json-stringify@6.3.0: - resolution: {integrity: sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-querystring@1.1.2: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fastify-plugin@5.1.0: - resolution: {integrity: sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==} - - fastify@5.7.4: - resolution: {integrity: sha512-e6l5NsRdaEP8rdD8VR0ErJASeyaRbzXYpmkrpr2SuvuMq6Si3lvsaVy5C+7gLanEkvjpMDzBXWE5HPeb/hgTxA==} - fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -4323,105 +2847,22 @@ packages: picomatch: optional: true - fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - - fflate@0.8.2: - resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - file-type@20.5.0: - resolution: {integrity: sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==} - engines: {node: '>=18'} - - file-type@21.3.0: - resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} - engines: {node: '>=20'} - - filename-reserved-regex@3.0.0: - resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - filenamify@6.0.0: - resolution: {integrity: sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==} - engines: {node: '>=16'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - - find-my-way@9.4.0: - resolution: {integrity: sha512-5Ye4vHsypZRYtS01ob/iwHzGRUDELlsoCftI/OZFhcLs1M0tkGPcXldE80TAZC5yYuJMBPJQQ43UHlqbJWiX2w==} + find-my-way@9.7.0: + resolution: {integrity: sha512-f2JHn75x2JlwUwLenZypgczR7YWMb/uO9BvUXtus+JMgkbIkLADd38cI4EiV+OQqrGo1Zlq6V8wnqMJ8e62wUQ==} engines: {node: '>=20'} find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - find-versions@5.1.0: - resolution: {integrity: sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==} - engines: {node: '>=12'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - fork-ts-checker-webpack-plugin@9.1.0: - resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} - engines: {node: '>=14.21.3'} - peerDependencies: - typescript: '>3.6.0' - webpack: ^5.11.0 - - form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - formidable@3.5.4: - resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} - engines: {node: '>=14.0.0'} - - 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-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -4430,12 +2871,6 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} - fs-monkey@1.1.0: - resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4444,56 +2879,18 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - generate-function@2.3.1: resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} - generator-function@2.0.1: - resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} - engines: {node: '>= 0.4'} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - get-port-please@3.2.0: resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.14.0: - resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} - - giget@2.0.0: - resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} + giget@3.3.1: + resolution: {integrity: sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==} hasBin: true git-raw-commits@4.0.0: @@ -4505,55 +2902,18 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - glob@11.1.0: - resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} - engines: {node: 20 || >=22} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true - - glob@13.0.0: - resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} - engines: {node: 20 || >=22} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - globals@16.5.0: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - got@13.0.0: - resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} - engines: {node: '>=16'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -4563,67 +2923,23 @@ packages: graphmatch@1.1.1: resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true - - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.11.4: - resolution: {integrity: sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==} - engines: {node: '>=16.9.0'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - - http-status-codes@2.3.0: - resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + hono-openapi@1.3.3: + resolution: {integrity: sha512-+LVFPQEc4eDrXr/Rd0QQkRh/DbEZRXnm1RZU4bVaoqmWiWgspT4nNHIQMgi44X+D9/8k1eGzHvR0KLNPJ/0xaQ==} + peerDependencies: + hono: ^4.11.2 - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} + hono@4.13.9: + resolution: {integrity: sha512-7dMkQmZoC4E6F7AtaQSPhlWAdnBti+j7rreMZl8QB4jFiEhP9TWbGWUMi8WYzBCgmgulxuvLQupKqo+Co6Omyg==} + engines: {node: '>=16.9.0'} human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -4641,51 +2957,21 @@ packages: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - 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==} - ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - inspect-with-kind@1.0.5: - resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} - - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - internmap@1.0.1: resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} @@ -4693,95 +2979,28 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - ipaddr.js@2.3.0: - resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} - engines: {node: '>= 10'} - - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-bun-module@2.0.0: - resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - - is-generator-function@1.1.2: - resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - is-module@1.0.0: resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -4790,17 +3009,10 @@ packages: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - is-property@1.0.2: resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} @@ -4810,278 +3022,17 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - - iterare@1.2.1: - resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} - engines: {node: '>=6'} - - jackspeak@4.2.3: - resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} - engines: {node: 20 || >=22} - - jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-diff@30.2.0: - resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-esm-transformer@1.0.0: - resolution: {integrity: sha512-FoPgeMMwy1/CEsc8tBI41i83CEO3x85RJuZi5iAMmWoARXhfgk6Jd7y+4d+z+HCkTKNVDvSWKGRhwjzU9PUbrw==} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@30.2.0: - resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@30.2.0: - resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@30.2.0: - resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-mock-extended@4.0.0: - resolution: {integrity: sha512-7BZpfuvLam+/HC+NxifIi9b+5VXj/utUDMPUqrDJehGWVuXPtLS9Jqlob2mJLrI/pg2k1S8DMfKDvEB88QNjaQ==} - peerDependencies: - '@jest/globals': ^28.0.0 || ^29.0.0 || ^30.0.0 - jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 || ^30.0.0 - typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@30.2.0: - resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-regex-util@30.0.1: - resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@30.2.0: - resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@30.2.0: - resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@27.5.1: - resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} - engines: {node: '>= 10.13.0'} - - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@30.2.0: - resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -5101,94 +3052,24 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-ref-resolver@3.0.0: - resolution: {integrity: sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - layerchart@2.0.0-next.65: resolution: {integrity: sha512-pyIpQwSrUDz5de/Es8JwrDuEg6VhpFVMlR1RGSGhOYVnDeOUO04v4ZKPNXEltYXldQt9VM7W7rmpwUuR4l7DOg==} peerDependencies: svelte: ^5.0.0 - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - libphonenumber-js@1.13.8: - resolution: {integrity: sha512-80xal1m93rADejw2pMp2MSzFhHCPLEspjHxnH2UtqI+DgAmElsbmLMiqk9niwH9NWAfjsRtaJI+qBrOEmRx9nQ==} - - light-my-request@6.6.0: - resolution: {integrity: sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==} - lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -5263,21 +3144,9 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} - lilconfig@2.1.0: - resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} - engines: {node: '>=10'} - lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - load-esm@1.0.3: - resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} - engines: {node: '>=13.2.0'} - - loader-runner@4.3.1: - resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} - engines: {node: '>=6.11.5'} - locate-character@3.0.0: resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} @@ -5285,46 +3154,15 @@ packages: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - lodash.kebabcase@4.1.1: resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.mergewith@4.6.2: resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash.snakecase@4.1.1: resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} @@ -5334,34 +3172,12 @@ packages: lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - lodash@4.17.23: resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - logform@2.7.0: - resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} - engines: {node: '>= 12.0.0'} - long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} - engines: {node: 20 || >=22} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru.min@1.1.4: resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} @@ -5370,38 +3186,9 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - memfs@3.5.3: - resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} - engines: {node: '>= 4.0.0'} - memoize@10.2.0: resolution: {integrity: sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==} engines: {node: '>=18'} @@ -5414,93 +3201,21 @@ packages: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} - 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==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - - mime@3.0.0: - resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} - engines: {node: '>=10.0.0'} - hasBin: true - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - minimatch@10.2.2: - resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} - engines: {node: 18 || 20 || >=22} - - minimatch@10.2.5: - resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} - engines: {node: 18 || 20 || >=22} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -5509,17 +3224,6 @@ packages: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - multer@2.0.2: - resolution: {integrity: sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==} - engines: {node: '>= 10.16.0'} - - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} - mysql2@3.15.3: resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} engines: {node: '>= 8.0'} @@ -5533,155 +3237,46 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - napi-postinstall@0.3.4: - resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + nanoid@5.1.16: + resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} + engines: {node: ^18 || >=20} hasBin: true - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - nest-winston@1.10.2: - resolution: {integrity: sha512-Z9IzL/nekBOF/TEwBHUJDiDPMaXUcFquUQOFavIRet6xF0EbuWnOzslyN/ksgzG+fITNgXhMdrL/POp9SdaFxA==} - peerDependencies: - '@nestjs/common': ^5.0.0 || ^6.6.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - winston: ^3.0.0 - - nestjs-zod@5.1.1: - resolution: {integrity: sha512-pXa9Jrdip7iedKvGxJTvvCFVRCoIcNENPCsHjpCefPH3PcFejRgkZkUcr3TYITRyxnUk7Zy5OsLpirZGLYBfBQ==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - '@nestjs/swagger': ^7.4.2 || ^8.0.0 || ^11.0.0 - rxjs: ^7.0.0 - zod: ^3.25.0 || ^4.0.0 - peerDependenciesMeta: - '@nestjs/swagger': - optional: true - - node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - - node-addon-api@8.5.0: - resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} + node-addon-api@8.9.2: + resolution: {integrity: sha512-VijLXbi3UACN69I0JVXJsX4tjACjNoQDgv2gTF6sx2wWEi8tkSg2eX8p5gSIFi8z2+DL3oHmY6OyKce38SDolg==} engines: {node: ^18 || ^20 || >= 21} - node-emoji@1.11.0: - resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} - - node-exports-info@1.6.0: - resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} - engines: {node: '>= 0.4'} - - node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - normalize-url@8.1.1: - resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} - engines: {node: '>=14.16'} + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - nypm@0.6.5: - resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==} - engines: {node: '>=18'} + oxfmt@0.57.0: + resolution: {integrity: sha512-ZB7Bi+rGDSqmVIo9jwcLyFgjxXvQhDdU+jx+ZrVy6VRiVXK2+CHc4hO3J4dUQjHe7V0ymHB+MDuv5z+NhK07HA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - - ohash@2.0.11: - resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} - - on-exit-leak-free@2.1.2: - 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==} - - one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - - oxfmt@0.57.0: - resolution: {integrity: sha512-ZB7Bi+rGDSqmVIo9jwcLyFgjxXvQhDdU+jx+ZrVy6VRiVXK2+CHc4hO3J4dUQjHe7V0ymHB+MDuv5z+NhK07HA==} + oxfmt@0.70.0: + resolution: {integrity: sha512-IsHxZ4y0wQLLMhnrJblBJgZsLDzfULrJnAw5j/QqsTlMa/m3AqsbToi+W71uhBGaqlqq/PbbjvHc09TJwdv3Tw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -5697,6 +3292,10 @@ packages: resolution: {integrity: sha512-4F/NXJiK2KnK4LQiULUPXRzVq0LOfextGvwCVRW1VKQbF5epI3MDMEGVAl5XjAGL6IFc7xBc/eVA95wczPeEQg==} hasBin: true + oxlint-tsgolint@7.0.2003: + resolution: {integrity: sha512-VnK4zlqgmgq/7ZcjzCk/WpN8kKFsYGcB85Io9qT3wL4K8Un3RKmJkp793crNETieMusPMKOA4a+Mw2wbteI6TQ==} + hasBin: true + oxlint@1.72.0: resolution: {integrity: sha512-1rhdZIP/EvoI91ABIwNU5Q8+bWf8mjrS5UzIOZld4d4bXxJvtlUhlQvaoTogIGin/qdErMOrwaIJvCSIAKTLhA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5710,9 +3309,18 @@ packages: vite-plus: optional: true - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} + oxlint@1.85.0: + resolution: {integrity: sha512-bc26s97nuvPj1ViyPsqmKecVkUWFMEdtayO8MaQ6oiLfs1pj94cQlZZhrh4BPNlr9HQosjhIlwgZKsfcwmcNgg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true p-filter@2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} @@ -5722,18 +3330,10 @@ packages: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - p-map@2.1.0: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} @@ -5742,9 +3342,6 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} @@ -5756,18 +3353,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - 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'} @@ -5775,13 +3364,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5789,39 +3371,36 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - peek-stream@1.1.3: - resolution: {integrity: sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==} + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pg-cloudflare@1.4.0: + resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - - pg-cloudflare@1.3.0: - resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} - - pg-connection-string@2.11.0: - resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==} + pg-connection-string@2.14.0: + resolution: {integrity: sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==} pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} - pg-pool@3.11.0: - resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==} + pg-pool@3.14.0: + resolution: {integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==} peerDependencies: pg: '>=8.0' pg-protocol@1.11.0: resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} + pg-protocol@1.16.0: + resolution: {integrity: sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==} + pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg@8.18.0: - resolution: {integrity: sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==} + pg@8.23.0: + resolution: {integrity: sha512-Ip2EQCngowJLGOfCwkFhPXU7/ljlhn6Rxlmy4XYfL2Y+vyRM59+8uR2xqRWKdYmbXmxCFOAmKxBuSUCdF34qLg==} engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -5839,10 +3418,6 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} @@ -5851,38 +3426,9 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pino-abstract-transport@3.0.0: - resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - - pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - - pino@10.3.1: - resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} - hasBin: true - - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - - piscina@4.9.2: - resolution: {integrity: sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - pluralize@8.0.0: - resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} - engines: {node: '>=4'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -5911,34 +3457,13 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} - engines: {node: '>=6.0.0'} - prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} hasBin: true - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} - hasBin: true - - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - pretty-format@30.2.0: - resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - prisma@7.4.1: - resolution: {integrity: sha512-gDKOXwnPiMdB+uYMhMeN8jj4K7Cu3Q2wB/wUsITOoOk446HtVb8T9BZxFJ1Zop6alc89k6PMNdR2FZCpbXp/jw==} + prisma@7.10.0: + resolution: {integrity: sha512-o0ornyJOWgygVAzGCpr8PdXV8EJLHyVGDDUr/voBQt8Azzw8cYTByzzPGcA/m4tCkPcnJA8raEOv2CslsKhPEw==} engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: @@ -5950,89 +3475,29 @@ packages: typescript: optional: true - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process-warning@4.0.1: - resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} - - process-warning@5.0.0: - resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - proxy-compare@3.0.1: resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - pumpify@2.0.1: - resolution: {integrity: sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} - engines: {node: '>=0.6'} - - qs@6.15.3: - resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} - engines: {node: '>=0.6'} - quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - range-parser@1.3.0: - resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - - rc9@2.1.2: - resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + rc9@3.1.0: + resolution: {integrity: sha512-ufjkNVzbRHKcCOmTahZkmVsyc3W+MSk3jY03m+a7tGHkIsdVMG9l10/3HvFbWkkKzY5VFp3pkRsIo/UYgmFL7Q==} react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: react: ^19.2.4 - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react@19.2.4: resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} @@ -6041,38 +3506,13 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - reflect-metadata@0.2.2: - resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} - - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - - regexp-to-ast@0.5.0: - resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} - - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} remeda@2.33.4: resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} @@ -6085,13 +3525,6 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6100,31 +3533,11 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.6: - resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} - engines: {node: '>= 0.4'} - hasBin: true - - responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - ret@0.5.0: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} @@ -6137,9 +3550,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} @@ -6148,10 +3558,6 @@ 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'} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -6170,114 +3576,30 @@ packages: rw@1.3.3: resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safe-regex2@5.0.0: resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - - schema-utils@4.3.3: - resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} - engines: {node: '>= 10.13.0'} - - secure-json-parse@4.1.0: - resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} - - seek-bzip@2.0.0: - resolution: {integrity: sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==} - hasBin: true - - semver-regex@4.0.5: - resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} - engines: {node: '>=12'} - - semver-truncate@3.0.0: - resolution: {integrity: sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg==} - engines: {node: '>=12'} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - seq-queue@0.0.5: resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - set-cookie-parser@2.7.2: - resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} - set-cookie-parser@3.0.1: resolution: {integrity: sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -6286,30 +3608,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - side-channel@1.1.1: - resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} - engines: {node: '>= 0.4'} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -6321,31 +3619,14 @@ packages: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - sonic-boom@4.2.1: - resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} - - sort-keys-length@1.0.1: - resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} - engines: {node: '>=0.10.0'} - - sort-keys@1.1.2: - resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} - engines: {node: '>=0.10.0'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -6353,14 +3634,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} @@ -6375,64 +3648,13 @@ packages: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} - stable-hash-x@0.2.0: - resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} - engines: {node: '>=12.0.0'} - - stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - - stream-shift@1.0.3: - resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - - streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - - streamx@2.23.0: - resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} - - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -6441,41 +3663,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-dirs@3.0.0: - resolution: {integrity: sha512-I0sdgcFTfKQlUPZyAqPJmSG3HLO9rWDFnxonnIbskYNM3DwFOeTNB5KzVq3dA1GdRAc/25b5Y7UO2TQfKWw4aQ==} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strtok3@10.3.4: - resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} - engines: {node: '>=18'} - - superagent@10.3.0: - resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} - engines: {node: '>=14.18.0'} - - supertest@7.2.2: - resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} - engines: {node: '>=14.18.0'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -6492,19 +3679,9 @@ packages: resolution: {integrity: sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==} engines: {node: '>=18'} - swagger-themes@1.4.3: - resolution: {integrity: sha512-1G0CqJC1IBbNxkAOyJoREd9hfwXH1R6+3GOFxLhQho2w2i+AbaJqkF4mTJhkce4yhaEMUXvv4KKu1YO/qpe6nQ==} - - swagger-ui-dist@5.31.0: - resolution: {integrity: sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==} - - symbol-observable@4.0.0: - resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} - engines: {node: '>=0.10'} - - synckit@0.11.12: - resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} - engines: {node: ^14.18.0 || >=16.0.0} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} tailwind-merge@3.6.0: resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} @@ -6512,62 +3689,19 @@ packages: tailwindcss@4.3.1: resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} - engines: {node: '>=6'} - tapable@2.3.3: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - terser-webpack-plugin@5.3.16: - resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - terser@5.46.0: resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} engines: {node: '>=10'} hasBin: true - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - - text-decoder@1.2.7: - resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} - - text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - - thread-stream@4.0.0: - resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} - engines: {node: '>=20'} - - through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -6580,237 +3714,58 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - token-types@6.1.2: - resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} - engines: {node: '>=14.16'} - totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - ts-api-utils@2.4.0: - resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - ts-essentials@10.1.1: - resolution: {integrity: sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw==} - peerDependencies: - typescript: '>=4.5.0' - peerDependenciesMeta: - typescript: - optional: true - - ts-jest@29.4.6: - resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} - engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 || ^30.0.0 - '@jest/types': ^29.0.0 || ^30.0.0 - babel-jest: ^29.0.0 || ^30.0.0 - esbuild: '*' - jest: ^29.0.0 || ^30.0.0 - jest-util: ^29.0.0 || ^30.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/transform': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - jest-util: - optional: true - - ts-loader@9.5.4: - resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - typescript: '*' - webpack: ^5.0.0 - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} + engines: {node: '>=18.0.0'} hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - tsconfig-paths-webpack-plugin@4.2.0: - resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} - engines: {node: '>=10.13.0'} - - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - - type-is@2.1.0: - resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} - engines: {node: '>= 18'} - - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} - - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - - typescript-eslint@8.56.0: - resolution: {integrity: sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' + type-fest@5.10.0: + resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==} + engines: {node: '>=20'} typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} hasBin: true - uid@2.0.2: - resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} - engines: {node: '>=8'} - - uint8array-extras@1.5.0: - resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} - engines: {node: '>=18'} - - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - unrs-resolver@1.11.1: - resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - - valibot@1.2.0: - resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + valibot@1.4.2: + resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} peerDependencies: typescript: '>=5' peerDependenciesMeta: typescript: optional: true - validator@13.15.35: - resolution: {integrity: sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==} - engines: {node: '>= 0.10'} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - virtua@0.49.2: resolution: {integrity: sha512-aEp3+6cmIRjHUlQnWdgGXYMYtrIG26QnN9jJDZEE5LRhvo1Z9HzoJwLDgyVULUPWcSdCnZAroQm7raXJyTG0AA==} peerDependencies: @@ -6879,89 +3834,15 @@ packages: vite: optional: true - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - - watchpack@2.5.1: - resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} - engines: {node: '>=10.13.0'} - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - webpack-node-externals@3.0.0: - resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} - engines: {node: '>=6'} - - webpack-sources@3.3.4: - resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} - engines: {node: '>=10.13.0'} - - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - winston-transport@4.9.0: - resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} - engines: {node: '>= 12.0.0'} - - winston@3.19.0: - resolution: {integrity: sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==} - engines: {node: '>= 12.0.0'} - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -6970,9 +3851,6 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -6986,22 +3864,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yauzl@3.2.0: - resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==} - engines: {node: '>=12'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - zeptomatch@2.1.0: resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} @@ -7011,61 +3873,10 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} -snapshots: - - '@angular-devkit/core@19.2.17(chokidar@4.0.3)': - dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - jsonc-parser: 3.3.1 - picomatch: 4.0.2 - rxjs: 7.8.1 - source-map: 0.7.4 - optionalDependencies: - chokidar: 4.0.3 - - '@angular-devkit/core@19.2.19(chokidar@4.0.3)': - dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - jsonc-parser: 3.3.1 - picomatch: 4.0.2 - rxjs: 7.8.1 - source-map: 0.7.4 - optionalDependencies: - chokidar: 4.0.3 - - '@angular-devkit/schematics-cli@19.2.19(@types/node@22.19.11)(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@inquirer/prompts': 7.3.2(@types/node@22.19.11) - ansi-colors: 4.1.3 - symbol-observable: 4.0.0 - yargs-parser: 21.1.1 - transitivePeerDependencies: - - '@types/node' - - chokidar - - '@angular-devkit/schematics@19.2.17(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.17(chokidar@4.0.3) - jsonc-parser: 3.3.1 - magic-string: 0.30.17 - ora: 5.4.1 - rxjs: 7.8.1 - transitivePeerDependencies: - - chokidar + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} - '@angular-devkit/schematics@19.2.19(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - jsonc-parser: 3.3.1 - magic-string: 0.30.17 - ora: 5.4.1 - rxjs: 7.8.1 - transitivePeerDependencies: - - chokidar +snapshots: '@babel/code-frame@7.29.0': dependencies: @@ -7073,201 +3884,10 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} - - '@babel/core@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.29.1': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.28.6': - dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.28.6': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.28.6': {} - - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.28.6': - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - - '@babel/parser@7.29.0': - dependencies: - '@babel/types': 7.29.0 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.28.6 - transitivePeerDependencies: - - supports-color - '@babel/runtime@7.28.6': {} - '@babel/template@7.28.6': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - - '@babel/traverse@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@bcoe/v8-coverage@0.2.3': {} - - '@borewit/text-codec@0.2.1': {} - '@changesets/apply-release-plan@7.0.14': dependencies: '@changesets/config': 3.1.2 @@ -7297,7 +3917,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.8(@types/node@22.19.11)': + '@changesets/cli@2.29.8(@types/node@26.6.3)': dependencies: '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 @@ -7313,7 +3933,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@22.19.11) + '@inquirer/external-editor': 1.0.3(@types/node@26.6.3) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -7412,31 +4032,11 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@chevrotain/cst-dts-gen@10.5.0': - dependencies: - '@chevrotain/gast': 10.5.0 - '@chevrotain/types': 10.5.0 - lodash: 4.17.21 - - '@chevrotain/gast@10.5.0': - dependencies: - '@chevrotain/types': 10.5.0 - lodash: 4.17.21 - - '@chevrotain/types@10.5.0': {} - - '@chevrotain/utils@10.5.0': {} - - '@colors/colors@1.5.0': - optional: true - - '@colors/colors@1.6.0': {} - - '@commitlint/cli@20.4.2(@types/node@22.19.11)(typescript@5.9.3)': + '@commitlint/cli@20.4.2(@types/node@26.6.3)(typescript@7.0.2)': dependencies: '@commitlint/format': 20.4.0 '@commitlint/lint': 20.4.2 - '@commitlint/load': 20.4.0(@types/node@22.19.11)(typescript@5.9.3) + '@commitlint/load': 20.4.0(@types/node@26.6.3)(typescript@7.0.2) '@commitlint/read': 20.4.0 '@commitlint/types': 20.4.0 tinyexec: 1.0.2 @@ -7483,14 +4083,14 @@ snapshots: '@commitlint/rules': 20.4.2 '@commitlint/types': 20.4.0 - '@commitlint/load@20.4.0(@types/node@22.19.11)(typescript@5.9.3)': + '@commitlint/load@20.4.0(@types/node@26.6.3)(typescript@7.0.2)': dependencies: '@commitlint/config-validator': 20.4.0 '@commitlint/execute-rule': 20.0.0 '@commitlint/resolve-extends': 20.4.0 '@commitlint/types': 20.4.0 - cosmiconfig: 9.0.0(typescript@5.9.3) - cosmiconfig-typescript-loader: 6.2.0(@types/node@22.19.11)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3) + cosmiconfig: 9.0.0(typescript@7.0.2) + cosmiconfig-typescript-loader: 6.2.0(@types/node@26.6.3)(cosmiconfig@9.0.0(typescript@7.0.2))(typescript@7.0.2) is-plain-obj: 4.1.0 lodash.mergewith: 4.6.2 picocolors: 1.1.1 @@ -7541,237 +4141,178 @@ snapshots: conventional-commits-parser: 6.2.1 picocolors: 1.1.1 - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@dabh/diagnostics@2.0.8': - dependencies: - '@so-ric/colorspace': 1.1.6 - enabled: 2.0.0 - kuler: 2.0.0 - '@dagrejs/dagre@2.0.4': dependencies: '@dagrejs/graphlib': 3.0.4 '@dagrejs/graphlib@3.0.4': {} - '@electric-sql/pglite-socket@0.0.20(@electric-sql/pglite@0.3.15)': + '@electric-sql/pglite-socket@0.1.3(@electric-sql/pglite@0.4.3)': dependencies: - '@electric-sql/pglite': 0.3.15 + '@electric-sql/pglite': 0.4.3 - '@electric-sql/pglite-tools@0.2.20(@electric-sql/pglite@0.3.15)': + '@electric-sql/pglite-tools@0.3.3(@electric-sql/pglite@0.4.3)': dependencies: - '@electric-sql/pglite': 0.3.15 + '@electric-sql/pglite': 0.4.3 - '@electric-sql/pglite@0.3.15': {} + '@electric-sql/pglite@0.4.3': {} - '@emnapi/core@1.10.0': - dependencies: - '@emnapi/wasi-threads': 1.2.1 - tslib: 2.8.1 - optional: true + '@epic-web/invariant@1.0.0': {} - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 + '@esbuild/aix-ppc64@0.27.3': optional: true - '@emnapi/wasi-threads@1.2.1': - dependencies: - tslib: 2.8.1 + '@esbuild/aix-ppc64@0.28.2': optional: true - '@epic-web/invariant@1.0.0': {} - - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/android-arm64@0.28.2': optional: true '@esbuild/android-arm@0.27.3': optional: true + '@esbuild/android-arm@0.28.2': + optional: true + '@esbuild/android-x64@0.27.3': optional: true + '@esbuild/android-x64@0.28.2': + optional: true + '@esbuild/darwin-arm64@0.27.3': optional: true + '@esbuild/darwin-arm64@0.28.2': + optional: true + '@esbuild/darwin-x64@0.27.3': optional: true + '@esbuild/darwin-x64@0.28.2': + optional: true + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/freebsd-arm64@0.28.2': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/freebsd-x64@0.28.2': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/linux-arm64@0.28.2': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/linux-arm@0.28.2': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/linux-ia32@0.28.2': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/linux-loong64@0.28.2': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/linux-mips64el@0.28.2': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/linux-ppc64@0.28.2': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/linux-riscv64@0.28.2': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/linux-s390x@0.28.2': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.3(jiti@2.7.0))': - dependencies: - eslint: 9.39.3(jiti@2.7.0) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.2': {} + '@esbuild/linux-x64@0.27.3': + optional: true - '@eslint/config-array@0.21.1': - dependencies: - '@eslint/object-schema': 2.1.7 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color + '@esbuild/linux-x64@0.28.2': + optional: true - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 + '@esbuild/netbsd-arm64@0.27.3': + optional: true - '@eslint/core@0.17.0': - dependencies: - '@types/json-schema': 7.0.15 + '@esbuild/netbsd-arm64@0.28.2': + optional: true - '@eslint/eslintrc@3.3.3': - dependencies: - ajv: 6.14.0 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color + '@esbuild/netbsd-x64@0.27.3': + optional: true - '@eslint/js@9.39.3': {} + '@esbuild/netbsd-x64@0.28.2': + optional: true - '@eslint/object-schema@2.1.7': {} + '@esbuild/openbsd-arm64@0.27.3': + optional: true - '@eslint/plugin-kit@0.4.1': - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 + '@esbuild/openbsd-arm64@0.28.2': + optional: true - '@fastify/accept-negotiator@2.0.1': {} + '@esbuild/openbsd-x64@0.27.3': + optional: true - '@fastify/ajv-compiler@4.0.5': - dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - fast-uri: 3.1.0 + '@esbuild/openbsd-x64@0.28.2': + optional: true - '@fastify/compress@8.3.1': - dependencies: - '@fastify/accept-negotiator': 2.0.1 - fastify-plugin: 5.1.0 - mime-db: 1.54.0 - minipass: 7.1.3 - peek-stream: 1.1.3 - pump: 3.0.3 - pumpify: 2.0.1 - readable-stream: 4.7.0 + '@esbuild/openharmony-arm64@0.27.3': + optional: true - '@fastify/cors@11.2.0': - dependencies: - fastify-plugin: 5.1.0 - toad-cache: 3.7.0 + '@esbuild/openharmony-arm64@0.28.2': + optional: true - '@fastify/error@4.2.0': {} + '@esbuild/sunos-x64@0.27.3': + optional: true - '@fastify/fast-json-stringify-compiler@5.0.3': - dependencies: - fast-json-stringify: 6.3.0 + '@esbuild/sunos-x64@0.28.2': + optional: true - '@fastify/formbody@8.0.2': - dependencies: - fast-querystring: 1.1.2 - fastify-plugin: 5.1.0 + '@esbuild/win32-arm64@0.27.3': + optional: true - '@fastify/forwarded@3.0.1': {} + '@esbuild/win32-arm64@0.28.2': + optional: true - '@fastify/merge-json-schemas@0.2.1': - dependencies: - dequal: 2.0.3 + '@esbuild/win32-ia32@0.27.3': + optional: true - '@fastify/proxy-addr@5.1.0': - dependencies: - '@fastify/forwarded': 3.0.1 - ipaddr.js: 2.3.0 + '@esbuild/win32-ia32@0.28.2': + optional: true - '@fastify/send@4.1.0': - dependencies: - '@lukeed/ms': 2.0.2 - escape-html: 1.0.3 - fast-decode-uri-component: 1.0.1 - http-errors: 2.0.1 - mime: 3.0.0 + '@esbuild/win32-x64@0.27.3': optional: true - '@fastify/static@8.3.0': - dependencies: - '@fastify/accept-negotiator': 2.0.1 - '@fastify/send': 4.1.0 - content-disposition: 0.5.4 - fastify-plugin: 5.1.0 - fastq: 1.20.1 - glob: 11.1.0 + '@esbuild/win32-x64@0.28.2': optional: true '@floating-ui/core@1.7.5': @@ -7787,426 +4328,26 @@ snapshots: '@hallmaster/docker.js@0.0.28': {} - '@hono/node-server@1.19.9(hono@4.11.4)': - dependencies: - hono: 4.11.4 - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.7': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@inquirer/ansi@1.0.2': {} - - '@inquirer/checkbox@4.3.2(@types/node@22.19.11)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.11) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/confirm@5.1.21(@types/node@22.19.11)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/core@10.3.2(@types/node@22.19.11)': + '@hono/node-server@2.1.1(hono@4.13.9)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.11) - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/editor@4.2.23(@types/node@22.19.11)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/external-editor': 1.0.3(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 + hono: 4.13.9 - '@inquirer/expand@4.0.23(@types/node@22.19.11)': + '@hono/standard-validator@0.4.0(@standard-schema/spec@1.1.0)(hono@4.13.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.11 + '@standard-schema/spec': 1.1.0 + hono: 4.13.9 - '@inquirer/external-editor@1.0.3(@types/node@22.19.11)': + '@inquirer/external-editor@1.0.3(@types/node@26.6.3)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/figures@1.0.15': {} - - '@inquirer/input@4.3.1(@types/node@22.19.11)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/number@3.0.23(@types/node@22.19.11)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/password@4.0.23(@types/node@22.19.11)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/prompts@7.10.1(@types/node@22.19.11)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.19.11) - '@inquirer/confirm': 5.1.21(@types/node@22.19.11) - '@inquirer/editor': 4.2.23(@types/node@22.19.11) - '@inquirer/expand': 4.0.23(@types/node@22.19.11) - '@inquirer/input': 4.3.1(@types/node@22.19.11) - '@inquirer/number': 3.0.23(@types/node@22.19.11) - '@inquirer/password': 4.0.23(@types/node@22.19.11) - '@inquirer/rawlist': 4.1.11(@types/node@22.19.11) - '@inquirer/search': 3.2.2(@types/node@22.19.11) - '@inquirer/select': 4.4.2(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/prompts@7.3.2(@types/node@22.19.11)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.19.11) - '@inquirer/confirm': 5.1.21(@types/node@22.19.11) - '@inquirer/editor': 4.2.23(@types/node@22.19.11) - '@inquirer/expand': 4.0.23(@types/node@22.19.11) - '@inquirer/input': 4.3.1(@types/node@22.19.11) - '@inquirer/number': 3.0.23(@types/node@22.19.11) - '@inquirer/password': 4.0.23(@types/node@22.19.11) - '@inquirer/rawlist': 4.1.11(@types/node@22.19.11) - '@inquirer/search': 3.2.2(@types/node@22.19.11) - '@inquirer/select': 4.4.2(@types/node@22.19.11) - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/rawlist@4.1.11(@types/node@22.19.11)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/type': 3.0.10(@types/node@22.19.11) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/search@3.2.2(@types/node@22.19.11)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.11) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/select@4.4.2(@types/node@22.19.11)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.11) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.11) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.11 - - '@inquirer/type@3.0.10(@types/node@22.19.11)': - optionalDependencies: - '@types/node': 22.19.11 + '@types/node': 26.6.3 '@internationalized/date@3.12.0': dependencies: '@swc/helpers': 0.5.18 - '@isaacs/cliui@9.0.0': - optional: true - - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.2 - resolve-from: 5.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/console@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - - '@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/diff-sequences@30.0.1': {} - - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - jest-mock: 29.7.0 - - '@jest/environment@30.2.0': - dependencies: - '@jest/fake-timers': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 22.19.11 - jest-mock: 30.2.0 - - '@jest/expect-utils@29.7.0': - dependencies: - jest-get-type: 29.6.3 - - '@jest/expect-utils@30.2.0': - dependencies: - '@jest/get-type': 30.1.0 - - '@jest/expect@29.7.0': - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/expect@30.2.0': - dependencies: - expect: 30.2.0 - jest-snapshot: 30.2.0 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.19.11 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - '@jest/fake-timers@30.2.0': - dependencies: - '@jest/types': 30.2.0 - '@sinonjs/fake-timers': 13.0.5 - '@types/node': 22.19.11 - jest-message-util: 30.2.0 - jest-mock: 30.2.0 - jest-util: 30.2.0 - - '@jest/get-type@30.1.0': {} - - '@jest/globals@29.7.0': - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/globals@30.2.0': - dependencies: - '@jest/environment': 30.2.0 - '@jest/expect': 30.2.0 - '@jest/types': 30.2.0 - jest-mock: 30.2.0 - transitivePeerDependencies: - - supports-color - - '@jest/pattern@30.0.1': - dependencies: - '@types/node': 22.19.11 - jest-regex-util: 30.0.1 - - '@jest/reporters@29.7.0': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 22.19.11 - chalk: 4.1.2 - collect-v8-coverage: 1.0.3 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.2.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.10 - - '@jest/schemas@30.0.5': - dependencies: - '@sinclair/typebox': 0.34.48 - - '@jest/snapshot-utils@30.2.0': - dependencies: - '@jest/types': 30.2.0 - chalk: 4.1.2 - graceful-fs: 4.2.11 - natural-compare: 1.4.0 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.3 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.29.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/transform@30.2.0': - dependencies: - '@babel/core': 7.29.0 - '@jest/types': 30.2.0 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 7.0.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.2.0 - jest-regex-util: 30.0.1 - jest-util: 30.2.0 - micromatch: 4.0.8 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.19.11 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - - '@jest/types@30.2.0': - dependencies: - '@jest/pattern': 30.0.1 - '@jest/schemas': 30.0.5 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.19.11 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -8223,6 +4364,7 @@ snapshots: dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 + optional: true '@jridgewell/sourcemap-codec@1.5.5': {} @@ -8231,11 +4373,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - '@layerstack/svelte-actions@1.0.1-next.18': dependencies: '@floating-ui/dom': 1.7.6 @@ -8263,11 +4400,6 @@ snapshots: dependencies: svelte: 5.56.4(@typescript-eslint/types@8.56.0) - '@lukeed/csprng@1.1.0': {} - - '@lukeed/ms@2.0.2': - optional: true - '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.28.6 @@ -8284,485 +4416,445 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@microsoft/tsdoc@0.16.0': {} - - '@mrleebo/prisma-ast@0.13.1': + '@nodelib/fs.scandir@2.1.5': dependencies: - chevrotain: 10.5.0 - lilconfig: 2.1.0 + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 - '@napi-rs/nice-android-arm-eabi@1.1.1': - optional: true + '@nodelib/fs.stat@2.0.5': {} - '@napi-rs/nice-android-arm64@1.1.1': - optional: true + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 - '@napi-rs/nice-darwin-arm64@1.1.1': + '@oxfmt/binding-android-arm-eabi@0.57.0': optional: true - '@napi-rs/nice-darwin-x64@1.1.1': + '@oxfmt/binding-android-arm-eabi@0.70.0': optional: true - '@napi-rs/nice-freebsd-x64@1.1.1': + '@oxfmt/binding-android-arm64@0.57.0': optional: true - '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': + '@oxfmt/binding-android-arm64@0.70.0': optional: true - '@napi-rs/nice-linux-arm64-gnu@1.1.1': + '@oxfmt/binding-darwin-arm64@0.57.0': optional: true - '@napi-rs/nice-linux-arm64-musl@1.1.1': + '@oxfmt/binding-darwin-arm64@0.70.0': optional: true - '@napi-rs/nice-linux-ppc64-gnu@1.1.1': + '@oxfmt/binding-darwin-x64@0.57.0': optional: true - '@napi-rs/nice-linux-riscv64-gnu@1.1.1': + '@oxfmt/binding-darwin-x64@0.70.0': optional: true - '@napi-rs/nice-linux-s390x-gnu@1.1.1': + '@oxfmt/binding-freebsd-x64@0.57.0': optional: true - '@napi-rs/nice-linux-x64-gnu@1.1.1': + '@oxfmt/binding-freebsd-x64@0.70.0': optional: true - '@napi-rs/nice-linux-x64-musl@1.1.1': + '@oxfmt/binding-linux-arm-gnueabihf@0.57.0': optional: true - '@napi-rs/nice-openharmony-arm64@1.1.1': + '@oxfmt/binding-linux-arm-gnueabihf@0.70.0': optional: true - '@napi-rs/nice-win32-arm64-msvc@1.1.1': + '@oxfmt/binding-linux-arm-musleabihf@0.57.0': optional: true - '@napi-rs/nice-win32-ia32-msvc@1.1.1': + '@oxfmt/binding-linux-arm-musleabihf@0.70.0': optional: true - '@napi-rs/nice-win32-x64-msvc@1.1.1': + '@oxfmt/binding-linux-arm64-gnu@0.57.0': optional: true - '@napi-rs/nice@1.1.1': - optionalDependencies: - '@napi-rs/nice-android-arm-eabi': 1.1.1 - '@napi-rs/nice-android-arm64': 1.1.1 - '@napi-rs/nice-darwin-arm64': 1.1.1 - '@napi-rs/nice-darwin-x64': 1.1.1 - '@napi-rs/nice-freebsd-x64': 1.1.1 - '@napi-rs/nice-linux-arm-gnueabihf': 1.1.1 - '@napi-rs/nice-linux-arm64-gnu': 1.1.1 - '@napi-rs/nice-linux-arm64-musl': 1.1.1 - '@napi-rs/nice-linux-ppc64-gnu': 1.1.1 - '@napi-rs/nice-linux-riscv64-gnu': 1.1.1 - '@napi-rs/nice-linux-s390x-gnu': 1.1.1 - '@napi-rs/nice-linux-x64-gnu': 1.1.1 - '@napi-rs/nice-linux-x64-musl': 1.1.1 - '@napi-rs/nice-openharmony-arm64': 1.1.1 - '@napi-rs/nice-win32-arm64-msvc': 1.1.1 - '@napi-rs/nice-win32-ia32-msvc': 1.1.1 - '@napi-rs/nice-win32-x64-msvc': 1.1.1 - optional: true - - '@napi-rs/wasm-runtime@0.2.12': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.1 - optional: true - - '@nestjs/cli@11.0.16(@swc/cli@0.6.0(@swc/core@1.15.11(@swc/helpers@0.5.18))(chokidar@4.0.3))(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.19(@types/node@22.19.11)(chokidar@4.0.3) - '@inquirer/prompts': 7.10.1(@types/node@22.19.11) - '@nestjs/schematics': 11.0.9(chokidar@4.0.3)(typescript@5.9.3) - ansis: 4.2.0 - chokidar: 4.0.3 - cli-table3: 0.6.5 - commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18))) - glob: 13.0.0 - node-emoji: 1.11.0 - ora: 5.4.1 - tsconfig-paths: 4.2.0 - tsconfig-paths-webpack-plugin: 4.2.0 - typescript: 5.9.3 - webpack: 5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18)) - webpack-node-externals: 3.0.0 - optionalDependencies: - '@swc/cli': 0.6.0(@swc/core@1.15.11(@swc/helpers@0.5.18))(chokidar@4.0.3) - '@swc/core': 1.15.11(@swc/helpers@0.5.18) - transitivePeerDependencies: - - '@types/node' - - esbuild - - uglify-js - - webpack-cli - - '@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - file-type: 21.3.0 - iterare: 1.2.1 - load-esm: 1.0.3 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - optionalDependencies: - class-transformer: 0.5.1 - class-validator: 0.14.3 - transitivePeerDependencies: - - supports-color - - '@nestjs/config@4.0.3(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - dotenv: 17.2.3 - dotenv-expand: 12.0.3 - lodash: 4.17.23 - rxjs: 7.8.2 - - '@nestjs/core@11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nuxt/opencollective': 0.4.1 - fast-safe-stringify: 2.1.1 - iterare: 1.2.1 - path-to-regexp: 8.3.0 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - optionalDependencies: - '@nestjs/platform-express': 11.1.13(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) - - '@nestjs/jwt@11.0.2(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))': - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@types/jsonwebtoken': 9.0.10 - jsonwebtoken: 9.0.3 - - '@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)': - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - reflect-metadata: 0.2.2 - optionalDependencies: - class-transformer: 0.5.1 - class-validator: 0.14.3 - - '@nestjs/platform-express@11.1.13(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) - cors: 2.8.6 - express: 5.2.1 - multer: 2.0.2 - path-to-regexp: 8.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color + '@oxfmt/binding-linux-arm64-gnu@0.70.0': optional: true - '@nestjs/platform-fastify@11.1.14(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)': - dependencies: - '@fastify/cors': 11.2.0 - '@fastify/formbody': 8.0.2 - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) - fast-querystring: 1.1.2 - fastify: 5.7.4 - fastify-plugin: 5.1.0 - find-my-way: 9.4.0 - light-my-request: 6.6.0 - path-to-regexp: 8.3.0 - reusify: 1.1.0 - tslib: 2.8.1 - optionalDependencies: - '@fastify/static': 8.3.0 - - '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)': - dependencies: - '@angular-devkit/core': 19.2.17(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) - comment-json: 4.4.1 - jsonc-parser: 3.3.1 - pluralize: 8.0.0 - typescript: 5.9.3 - transitivePeerDependencies: - - chokidar - - '@nestjs/swagger@11.2.6(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)': - dependencies: - '@microsoft/tsdoc': 0.16.0 - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.1.0(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2) - js-yaml: 4.1.1 - lodash: 4.17.23 - path-to-regexp: 8.3.0 - reflect-metadata: 0.2.2 - swagger-ui-dist: 5.31.0 - optionalDependencies: - '@fastify/static': 8.3.0 - class-transformer: 0.5.1 - class-validator: 0.14.3 - - '@nestjs/testing@11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(@nestjs/platform-express@11.1.13)': - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2) - tslib: 2.8.1 - optionalDependencies: - '@nestjs/platform-express': 11.1.13(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14) - - '@noble/hashes@1.8.0': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - - '@nuxt/opencollective@0.4.1': - dependencies: - consola: 3.4.2 - - '@oxfmt/binding-android-arm-eabi@0.57.0': + '@oxfmt/binding-linux-arm64-musl@0.57.0': optional: true - '@oxfmt/binding-android-arm64@0.57.0': + '@oxfmt/binding-linux-arm64-musl@0.70.0': optional: true - '@oxfmt/binding-darwin-arm64@0.57.0': + '@oxfmt/binding-linux-ppc64-gnu@0.57.0': optional: true - '@oxfmt/binding-darwin-x64@0.57.0': + '@oxfmt/binding-linux-ppc64-gnu@0.70.0': optional: true - '@oxfmt/binding-freebsd-x64@0.57.0': + '@oxfmt/binding-linux-riscv64-gnu@0.57.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.57.0': + '@oxfmt/binding-linux-riscv64-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.57.0': + '@oxfmt/binding-linux-riscv64-musl@0.57.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.57.0': + '@oxfmt/binding-linux-riscv64-musl@0.70.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.57.0': + '@oxfmt/binding-linux-s390x-gnu@0.57.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.57.0': + '@oxfmt/binding-linux-s390x-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.57.0': + '@oxfmt/binding-linux-x64-gnu@0.57.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.57.0': + '@oxfmt/binding-linux-x64-gnu@0.70.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.57.0': + '@oxfmt/binding-linux-x64-musl@0.57.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.57.0': + '@oxfmt/binding-linux-x64-musl@0.70.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.57.0': + '@oxfmt/binding-openharmony-arm64@0.57.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.57.0': + '@oxfmt/binding-openharmony-arm64@0.70.0': optional: true '@oxfmt/binding-win32-arm64-msvc@0.57.0': optional: true + '@oxfmt/binding-win32-arm64-msvc@0.70.0': + optional: true + '@oxfmt/binding-win32-ia32-msvc@0.57.0': optional: true + '@oxfmt/binding-win32-ia32-msvc@0.70.0': + optional: true + '@oxfmt/binding-win32-x64-msvc@0.57.0': optional: true + '@oxfmt/binding-win32-x64-msvc@0.70.0': + optional: true + '@oxlint-tsgolint/darwin-arm64@0.17.4': optional: true + '@oxlint-tsgolint/darwin-arm64@7.0.2003': + optional: true + '@oxlint-tsgolint/darwin-x64@0.17.4': optional: true + '@oxlint-tsgolint/darwin-x64@7.0.2003': + optional: true + '@oxlint-tsgolint/linux-arm64@0.17.4': optional: true + '@oxlint-tsgolint/linux-arm64@7.0.2003': + optional: true + '@oxlint-tsgolint/linux-x64@0.17.4': optional: true + '@oxlint-tsgolint/linux-x64@7.0.2003': + optional: true + '@oxlint-tsgolint/win32-arm64@0.17.4': optional: true + '@oxlint-tsgolint/win32-arm64@7.0.2003': + optional: true + '@oxlint-tsgolint/win32-x64@0.17.4': optional: true + '@oxlint-tsgolint/win32-x64@7.0.2003': + optional: true + '@oxlint/binding-android-arm-eabi@1.72.0': optional: true + '@oxlint/binding-android-arm-eabi@1.85.0': + optional: true + '@oxlint/binding-android-arm64@1.72.0': optional: true + '@oxlint/binding-android-arm64@1.85.0': + optional: true + '@oxlint/binding-darwin-arm64@1.72.0': optional: true + '@oxlint/binding-darwin-arm64@1.85.0': + optional: true + '@oxlint/binding-darwin-x64@1.72.0': optional: true + '@oxlint/binding-darwin-x64@1.85.0': + optional: true + '@oxlint/binding-freebsd-x64@1.72.0': optional: true + '@oxlint/binding-freebsd-x64@1.85.0': + optional: true + '@oxlint/binding-linux-arm-gnueabihf@1.72.0': optional: true + '@oxlint/binding-linux-arm-gnueabihf@1.85.0': + optional: true + '@oxlint/binding-linux-arm-musleabihf@1.72.0': optional: true + '@oxlint/binding-linux-arm-musleabihf@1.85.0': + optional: true + '@oxlint/binding-linux-arm64-gnu@1.72.0': optional: true + '@oxlint/binding-linux-arm64-gnu@1.85.0': + optional: true + '@oxlint/binding-linux-arm64-musl@1.72.0': optional: true + '@oxlint/binding-linux-arm64-musl@1.85.0': + optional: true + '@oxlint/binding-linux-ppc64-gnu@1.72.0': optional: true + '@oxlint/binding-linux-ppc64-gnu@1.85.0': + optional: true + '@oxlint/binding-linux-riscv64-gnu@1.72.0': optional: true + '@oxlint/binding-linux-riscv64-gnu@1.85.0': + optional: true + '@oxlint/binding-linux-riscv64-musl@1.72.0': optional: true + '@oxlint/binding-linux-riscv64-musl@1.85.0': + optional: true + '@oxlint/binding-linux-s390x-gnu@1.72.0': optional: true + '@oxlint/binding-linux-s390x-gnu@1.85.0': + optional: true + '@oxlint/binding-linux-x64-gnu@1.72.0': optional: true + '@oxlint/binding-linux-x64-gnu@1.85.0': + optional: true + '@oxlint/binding-linux-x64-musl@1.72.0': optional: true + '@oxlint/binding-linux-x64-musl@1.85.0': + optional: true + '@oxlint/binding-openharmony-arm64@1.72.0': optional: true + '@oxlint/binding-openharmony-arm64@1.85.0': + optional: true + '@oxlint/binding-win32-arm64-msvc@1.72.0': optional: true + '@oxlint/binding-win32-arm64-msvc@1.85.0': + optional: true + '@oxlint/binding-win32-ia32-msvc@1.72.0': optional: true + '@oxlint/binding-win32-ia32-msvc@1.85.0': + optional: true + '@oxlint/binding-win32-x64-msvc@1.72.0': optional: true - '@paralleldrive/cuid2@2.3.1': - dependencies: - '@noble/hashes': 1.8.0 + '@oxlint/binding-win32-x64-msvc@1.85.0': + optional: true '@phc/format@1.0.0': {} - '@pinojs/redact@0.4.0': {} - - '@pkgr/core@0.2.9': {} - '@polka/url@1.0.0-next.29': {} - '@prisma/adapter-pg@7.4.1': + '@prisma/adapter-pg@7.10.0': dependencies: - '@prisma/driver-adapter-utils': 7.4.1 - pg: 8.18.0 + '@prisma/driver-adapter-utils': 7.10.0 + '@types/pg': 8.23.1 + pg: 8.23.0 postgres-array: 3.0.4 transitivePeerDependencies: - pg-native '@prisma/client-runtime-utils@7.4.1': {} - '@prisma/client@7.4.1(prisma@7.4.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)': + '@prisma/client@7.4.1(prisma@7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2))(typescript@7.0.2)': dependencies: '@prisma/client-runtime-utils': 7.4.1 optionalDependencies: - prisma: 7.4.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - typescript: 5.9.3 + prisma: 7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) + typescript: 7.0.2 - '@prisma/config@7.4.1': + '@prisma/config@7.10.0': dependencies: - c12: 3.1.0 + c12: 3.3.4 deepmerge-ts: 7.1.5 - effect: 3.18.4 + effect: 3.20.0 empathic: 2.0.0 transitivePeerDependencies: - magicast - '@prisma/debug@7.2.0': {} + '@prisma/debug@7.10.0': {} - '@prisma/debug@7.4.1': {} + '@prisma/debug@7.2.0': {} - '@prisma/dev@0.20.0(typescript@5.9.3)': + '@prisma/dev@0.24.17(typescript@7.0.2)': dependencies: - '@electric-sql/pglite': 0.3.15 - '@electric-sql/pglite-socket': 0.0.20(@electric-sql/pglite@0.3.15) - '@electric-sql/pglite-tools': 0.2.20(@electric-sql/pglite@0.3.15) - '@hono/node-server': 1.19.9(hono@4.11.4) - '@mrleebo/prisma-ast': 0.13.1 + '@electric-sql/pglite': 0.4.3 + '@electric-sql/pglite-socket': 0.1.3(@electric-sql/pglite@0.4.3) + '@electric-sql/pglite-tools': 0.3.3(@electric-sql/pglite@0.4.3) '@prisma/get-platform': 7.2.0 '@prisma/query-plan-executor': 7.2.0 + '@prisma/streams-local': 0.1.11 + find-my-way: 9.7.0 foreground-child: 3.3.1 get-port-please: 3.2.0 - hono: 4.11.4 - http-status-codes: 2.3.0 pathe: 2.0.3 proper-lockfile: 4.1.2 remeda: 2.33.4 std-env: 3.10.0 - valibot: 1.2.0(typescript@5.9.3) + valibot: 1.4.2(typescript@7.0.2) zeptomatch: 2.1.0 transitivePeerDependencies: - typescript - '@prisma/driver-adapter-utils@7.4.1': + '@prisma/driver-adapter-utils@7.10.0': dependencies: - '@prisma/debug': 7.4.1 + '@prisma/debug': 7.10.0 - '@prisma/engines-version@7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3': {} + '@prisma/engines-version@7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3': {} + + '@prisma/engines@7.10.0': + dependencies: + '@prisma/debug': 7.10.0 + '@prisma/engines-version': 7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3 + '@prisma/fetch-engine': 7.10.0 + '@prisma/get-platform': 7.10.0 - '@prisma/engines@7.4.1': + '@prisma/fetch-engine@7.10.0': dependencies: - '@prisma/debug': 7.4.1 - '@prisma/engines-version': 7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3 - '@prisma/fetch-engine': 7.4.1 - '@prisma/get-platform': 7.4.1 + '@prisma/debug': 7.10.0 + '@prisma/engines-version': 7.10.0-4.0edf323efd1d98336f3f0a68684b56f689b900d3 + '@prisma/get-platform': 7.10.0 - '@prisma/fetch-engine@7.4.1': + '@prisma/get-platform@7.10.0': dependencies: - '@prisma/debug': 7.4.1 - '@prisma/engines-version': 7.5.0-4.55ae170b1ced7fc6ed07a15f110549408c501bb3 - '@prisma/get-platform': 7.4.1 + '@prisma/debug': 7.10.0 '@prisma/get-platform@7.2.0': dependencies: '@prisma/debug': 7.2.0 - '@prisma/get-platform@7.4.1': + '@prisma/query-plan-executor@7.2.0': {} + + '@prisma/streams-local@0.1.11': dependencies: - '@prisma/debug': 7.4.1 + ajv: 8.18.0 + better-result: 2.10.0 + env-paths: 3.0.0 + proper-lockfile: 4.1.2 - '@prisma/query-plan-executor@7.2.0': {} + '@prisma/studio-core@0.33.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-toggle': 1.1.10(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@types/react': 19.2.11 + '@visx/curve': 4.0.1-alpha.0 + '@visx/event': 4.0.1-alpha.0 + '@visx/grid': 4.0.1-alpha.0(react@19.2.4) + '@visx/group': 4.0.1-alpha.0(react@19.2.4) + '@visx/responsive': 4.0.1-alpha.0(react@19.2.4) + '@visx/scale': 4.0.1-alpha.0 + '@visx/shape': 4.0.1-alpha.0(react@19.2.4) + d3-array: 3.2.4 + d3-shape: 3.2.0 + elkjs: 0.11.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react-dom' + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.11)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.11 + + '@radix-ui/react-primitive@2.1.3(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.11)(react@19.2.4) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.11 - '@prisma/studio-core@0.13.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@radix-ui/react-slot@1.2.3(@types/react@19.2.11)(react@19.2.4)': dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.11)(react@19.2.4) + react: 19.2.4 + optionalDependencies: '@types/react': 19.2.11 + + '@radix-ui/react-toggle@1.1.10(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.11)(react@19.2.4) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.11 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.11)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.11)(react@19.2.4) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.11)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.11 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.11)(react@19.2.4)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.11)(react@19.2.4) + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.11 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.11)(react@19.2.4)': + dependencies: + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.11 '@rollup/plugin-commonjs@29.0.2(rollup@4.60.0)': dependencies: @@ -8875,27 +4967,33 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.0': optional: true - '@rtsao/scc@1.1.0': {} - - '@scarf/scarf@1.4.0': {} - - '@sinclair/typebox@0.27.10': {} - - '@sinclair/typebox@0.34.48': {} + '@scalar/client-side-rendering@0.4.5': + dependencies: + '@scalar/helpers': 0.15.0 + '@scalar/schemas': 0.12.1 + '@scalar/types': 0.22.1 + '@scalar/validation': 0.6.5 - '@sindresorhus/is@5.6.0': {} + '@scalar/helpers@0.15.0': {} - '@sinonjs/commons@3.0.1': + '@scalar/hono-api-reference@0.12.6(hono@4.13.9)': dependencies: - type-detect: 4.0.8 + '@scalar/client-side-rendering': 0.4.5 + hono: 4.13.9 - '@sinonjs/fake-timers@10.3.0': + '@scalar/schemas@0.12.1': dependencies: - '@sinonjs/commons': 3.0.1 + '@scalar/helpers': 0.15.0 + '@scalar/validation': 0.6.5 - '@sinonjs/fake-timers@13.0.5': + '@scalar/types@0.22.1': dependencies: - '@sinonjs/commons': 3.0.1 + '@scalar/helpers': 0.15.0 + nanoid: 5.1.16 + type-fest: 5.10.0 + zod: 4.6.5 + + '@scalar/validation@0.6.5': {} '@skeletonlabs/skeleton-common@4.15.2': {} @@ -8936,10 +5034,25 @@ snapshots: dependencies: tailwindcss: 4.3.1 - '@so-ric/colorspace@1.1.6': + '@standard-community/standard-json@0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(effect@3.20.0)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/json-schema': 7.0.15 + quansync: 0.2.11 + optionalDependencies: + effect: 3.20.0 + valibot: 1.4.2(typescript@7.0.2) + zod: 4.6.5 + + '@standard-community/standard-openapi@0.2.10(@standard-community/standard-json@0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(effect@3.20.0)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5))(@standard-schema/spec@1.1.0)(effect@3.20.0)(openapi-types@12.1.3)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5)': dependencies: - color: 5.0.3 - text-hex: 1.0.0 + '@standard-community/standard-json': 0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(effect@3.20.0)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5) + '@standard-schema/spec': 1.1.0 + openapi-types: 12.1.3 + optionalDependencies: + effect: 3.20.0 + valibot: 1.4.2(typescript@7.0.2) + zod: 4.6.5 '@standard-schema/spec@1.1.0': {} @@ -8951,19 +5064,19 @@ snapshots: dependencies: acorn: 8.16.0 - '@sveltejs/adapter-node@5.5.4(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))': + '@sveltejs/adapter-node@5.5.4(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))': dependencies: '@rollup/plugin-commonjs': 29.0.2(rollup@4.60.0) '@rollup/plugin-json': 6.1.0(rollup@4.60.0) '@rollup/plugin-node-resolve': 16.0.3(rollup@4.60.0) - '@sveltejs/kit': 2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + '@sveltejs/kit': 2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) rollup: 4.60.0 - '@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0))': + '@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) '@types/cookie': 0.6.0 acorn: 8.16.0 cookie: 0.6.0 @@ -8975,109 +5088,33 @@ snapshots: set-cookie-parser: 3.0.1 sirv: 3.0.2 svelte: 5.56.4(@typescript-eslint/types@8.56.0) - vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) optionalDependencies: typescript: 5.9.3 '@sveltejs/load-config@0.2.0': {} - '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) obug: 2.1.1 svelte: 5.56.4(@typescript-eslint/types@8.56.0) - vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) - '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.1 svelte: 5.56.4(@typescript-eslint/types@8.56.0) - vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0) - vitefu: 1.1.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) - - '@swc/cli@0.6.0(@swc/core@1.15.11(@swc/helpers@0.5.18))(chokidar@4.0.3)': - dependencies: - '@swc/core': 1.15.11(@swc/helpers@0.5.18) - '@swc/counter': 0.1.3 - '@xhmikosr/bin-wrapper': 13.2.0 - commander: 8.3.0 - fast-glob: 3.3.3 - minimatch: 9.0.5 - piscina: 4.9.2 - semver: 7.7.4 - slash: 3.0.0 - source-map: 0.7.6 - optionalDependencies: - chokidar: 4.0.3 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@swc/core-darwin-arm64@1.15.11': - optional: true - - '@swc/core-darwin-x64@1.15.11': - optional: true - - '@swc/core-linux-arm-gnueabihf@1.15.11': - optional: true - - '@swc/core-linux-arm64-gnu@1.15.11': - optional: true - - '@swc/core-linux-arm64-musl@1.15.11': - optional: true - - '@swc/core-linux-x64-gnu@1.15.11': - optional: true - - '@swc/core-linux-x64-musl@1.15.11': - optional: true - - '@swc/core-win32-arm64-msvc@1.15.11': - optional: true - - '@swc/core-win32-ia32-msvc@1.15.11': - optional: true - - '@swc/core-win32-x64-msvc@1.15.11': - optional: true - - '@swc/core@1.15.11(@swc/helpers@0.5.18)': - dependencies: - '@swc/counter': 0.1.3 - '@swc/types': 0.1.25 - optionalDependencies: - '@swc/core-darwin-arm64': 1.15.11 - '@swc/core-darwin-x64': 1.15.11 - '@swc/core-linux-arm-gnueabihf': 1.15.11 - '@swc/core-linux-arm64-gnu': 1.15.11 - '@swc/core-linux-arm64-musl': 1.15.11 - '@swc/core-linux-x64-gnu': 1.15.11 - '@swc/core-linux-x64-musl': 1.15.11 - '@swc/core-win32-arm64-msvc': 1.15.11 - '@swc/core-win32-ia32-msvc': 1.15.11 - '@swc/core-win32-x64-msvc': 1.15.11 - '@swc/helpers': 0.5.18 - - '@swc/counter@0.1.3': {} + vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) + vitefu: 1.1.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) '@swc/helpers@0.5.18': dependencies: tslib: 2.8.1 - '@swc/types@0.1.25': - dependencies: - '@swc/counter': 0.1.3 - - '@szmarczak/http-timer@5.0.1': - dependencies: - defer-to-connect: 2.0.1 - '@tailwindcss/node@4.3.1': dependencies: '@jridgewell/remapping': 2.3.5 @@ -9139,130 +5176,63 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 - '@tailwindcss/vite@4.3.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.1 '@tailwindcss/oxide': 4.3.1 tailwindcss: 4.3.1 - vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0) - - '@tokenizer/inflate@0.2.7': - dependencies: - debug: 4.4.3 - fflate: 0.8.2 - token-types: 6.1.2 - transitivePeerDependencies: - - supports-color - - '@tokenizer/inflate@0.4.1': - dependencies: - debug: 4.4.3 - token-types: 6.1.2 - transitivePeerDependencies: - - supports-color - - '@tokenizer/token@0.3.0': {} - - '@tsconfig/node10@1.0.12': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@tybys/wasm-util@0.10.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.0 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.0 - '@babel/types': 7.29.0 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.0 + vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) '@types/cookie@0.6.0': {} - '@types/cookiejar@2.1.5': {} + '@types/d3-array@3.0.3': {} '@types/d3-array@3.2.2': {} + '@types/d3-color@3.1.0': {} + '@types/d3-contour@3.0.6': dependencies: '@types/d3-array': 3.2.2 '@types/geojson': 7946.0.16 - '@types/d3-path@3.1.1': {} - - '@types/d3-shape@3.1.8': - dependencies: - '@types/d3-path': 3.1.1 + '@types/d3-delaunay@6.0.1': {} - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 9.6.1 - '@types/estree': 1.0.8 + '@types/d3-format@3.0.1': {} - '@types/eslint@9.6.1': + '@types/d3-geo@3.1.0': dependencies: - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - - '@types/estree@1.0.8': {} - - '@types/geojson@7946.0.16': {} + '@types/geojson': 7946.0.16 - '@types/graceful-fs@4.1.9': + '@types/d3-interpolate@3.0.1': dependencies: - '@types/node': 22.19.11 + '@types/d3-color': 3.1.0 - '@types/http-cache-semantics@4.2.0': {} - - '@types/istanbul-lib-coverage@2.0.6': {} + '@types/d3-path@3.1.1': {} - '@types/istanbul-lib-report@3.0.3': + '@types/d3-scale@4.0.2': dependencies: - '@types/istanbul-lib-coverage': 2.0.6 + '@types/d3-time': 3.0.0 - '@types/istanbul-reports@3.0.4': + '@types/d3-shape@3.1.7': dependencies: - '@types/istanbul-lib-report': 3.0.3 + '@types/d3-path': 3.1.1 - '@types/jest@30.0.0': + '@types/d3-shape@3.1.8': dependencies: - expect: 30.2.0 - pretty-format: 30.2.0 + '@types/d3-path': 3.1.1 - '@types/json-schema@7.0.15': {} + '@types/d3-time-format@2.1.0': {} - '@types/json5@0.0.29': {} + '@types/d3-time@3.0.0': {} - '@types/jsonwebtoken@9.0.10': - dependencies: - '@types/ms': 2.1.0 - '@types/node': 22.19.11 + '@types/estree@1.0.8': {} - '@types/methods@1.1.4': {} + '@types/geojson@7946.0.16': {} - '@types/mocha@10.0.10': {} + '@types/json-schema@7.0.15': {} - '@types/ms@2.1.0': {} + '@types/lodash@4.17.25': {} '@types/node@12.20.55': {} @@ -9270,7 +5240,11 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/pg@8.16.0': + '@types/node@26.6.3': + dependencies: + undici-types: 8.9.0 + + '@types/pg@8.23.1': dependencies: '@types/node': 22.19.11 pg-protocol: 1.11.0 @@ -9282,359 +5256,147 @@ snapshots: '@types/resolve@1.20.2': {} - '@types/stack-utils@2.0.3': {} - - '@types/superagent@8.1.9': - dependencies: - '@types/cookiejar': 2.1.5 - '@types/methods': 1.1.4 - '@types/node': 22.19.11 - form-data: 4.0.5 - - '@types/supertest@6.0.3': - dependencies: - '@types/methods': 1.1.4 - '@types/superagent': 8.1.9 - - '@types/triple-beam@1.3.5': {} - '@types/trusted-types@2.0.7': {} - '@types/validator@13.15.10': + '@typescript-eslint/types@8.56.0': optional: true - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.35': - dependencies: - '@types/yargs-parser': 21.0.3 - - '@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.56.0 - '@typescript-eslint/type-utils': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.56.0 - eslint: 9.39.3(jiti@2.7.0) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.56.0 - '@typescript-eslint/types': 8.56.0 - '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.56.0 - debug: 4.4.3 - eslint: 9.39.3(jiti@2.7.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.56.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.56.0(typescript@5.9.3) - '@typescript-eslint/types': 8.56.0 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.56.0': - dependencies: - '@typescript-eslint/types': 8.56.0 - '@typescript-eslint/visitor-keys': 8.56.0 - - '@typescript-eslint/tsconfig-utils@8.56.0(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.56.0 - '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.3(jiti@2.7.0) - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.56.0': {} - - '@typescript-eslint/typescript-estree@8.56.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.56.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.56.0(typescript@5.9.3) - '@typescript-eslint/types': 8.56.0 - '@typescript-eslint/visitor-keys': 8.56.0 - debug: 4.4.3 - minimatch: 9.0.5 - semver: 7.7.4 - tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.56.0 - '@typescript-eslint/types': 8.56.0 - '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - eslint: 9.39.3(jiti@2.7.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.56.0': - dependencies: - '@typescript-eslint/types': 8.56.0 - eslint-visitor-keys: 5.0.1 - - '@ungap/structured-clone@1.3.0': {} - - '@unrs/resolver-binding-android-arm-eabi@1.11.1': + '@typescript/typescript-aix-ppc64@7.0.2': optional: true - '@unrs/resolver-binding-android-arm64@1.11.1': + '@typescript/typescript-darwin-arm64@7.0.2': optional: true - '@unrs/resolver-binding-darwin-arm64@1.11.1': + '@typescript/typescript-darwin-x64@7.0.2': optional: true - '@unrs/resolver-binding-darwin-x64@1.11.1': + '@typescript/typescript-freebsd-arm64@7.0.2': optional: true - '@unrs/resolver-binding-freebsd-x64@1.11.1': + '@typescript/typescript-freebsd-x64@7.0.2': optional: true - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + '@typescript/typescript-linux-arm64@7.0.2': optional: true - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + '@typescript/typescript-linux-arm@7.0.2': optional: true - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + '@typescript/typescript-linux-loong64@7.0.2': optional: true - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + '@typescript/typescript-linux-mips64el@7.0.2': optional: true - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + '@typescript/typescript-linux-ppc64@7.0.2': optional: true - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + '@typescript/typescript-linux-riscv64@7.0.2': optional: true - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + '@typescript/typescript-linux-s390x@7.0.2': optional: true - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + '@typescript/typescript-linux-x64@7.0.2': optional: true - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + '@typescript/typescript-netbsd-arm64@7.0.2': optional: true - '@unrs/resolver-binding-linux-x64-musl@1.11.1': + '@typescript/typescript-netbsd-x64@7.0.2': optional: true - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - dependencies: - '@napi-rs/wasm-runtime': 0.2.12 + '@typescript/typescript-openbsd-arm64@7.0.2': optional: true - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + '@typescript/typescript-openbsd-x64@7.0.2': optional: true - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + '@typescript/typescript-sunos-x64@7.0.2': optional: true - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + '@typescript/typescript-win32-arm64@7.0.2': optional: true - '@webassemblyjs/ast@1.14.1': - dependencies: - '@webassemblyjs/helper-numbers': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - - '@webassemblyjs/floating-point-hex-parser@1.13.2': {} - - '@webassemblyjs/helper-api-error@1.13.2': {} - - '@webassemblyjs/helper-buffer@1.14.1': {} - - '@webassemblyjs/helper-numbers@1.13.2': - dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.13.2 - '@webassemblyjs/helper-api-error': 1.13.2 - '@xtuc/long': 4.2.2 - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} - - '@webassemblyjs/helper-wasm-section@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/wasm-gen': 1.14.1 - - '@webassemblyjs/ieee754@1.13.2': - dependencies: - '@xtuc/ieee754': 1.2.0 - - '@webassemblyjs/leb128@1.13.2': - dependencies: - '@xtuc/long': 4.2.2 - - '@webassemblyjs/utf8@1.13.2': {} - - '@webassemblyjs/wasm-edit@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/helper-wasm-section': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-opt': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - '@webassemblyjs/wast-printer': 1.14.1 - - '@webassemblyjs/wasm-gen@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wasm-opt@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - - '@webassemblyjs/wasm-parser@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-api-error': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wast-printer@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@xtuc/long': 4.2.2 + '@typescript/typescript-win32-x64@7.0.2': + optional: true - '@xhmikosr/archive-type@7.1.0': + '@visx/curve@4.0.1-alpha.0': dependencies: - file-type: 20.5.0 - transitivePeerDependencies: - - supports-color + '@visx/vendor': 4.0.0-alpha.0 - '@xhmikosr/bin-check@7.1.0': + '@visx/event@4.0.1-alpha.0': dependencies: - execa: 5.1.1 - isexe: 2.0.0 + '@types/react': 19.2.11 + '@visx/point': 4.0.1-alpha.0 - '@xhmikosr/bin-wrapper@13.2.0': + '@visx/grid@4.0.1-alpha.0(react@19.2.4)': dependencies: - '@xhmikosr/bin-check': 7.1.0 - '@xhmikosr/downloader': 15.2.0 - '@xhmikosr/os-filter-obj': 3.0.0 - bin-version-check: 5.1.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color + '@types/react': 19.2.11 + '@visx/curve': 4.0.1-alpha.0 + '@visx/group': 4.0.1-alpha.0(react@19.2.4) + '@visx/point': 4.0.1-alpha.0 + '@visx/scale': 4.0.1-alpha.0 + '@visx/shape': 4.0.1-alpha.0(react@19.2.4) + classnames: 2.5.1 + react: 19.2.4 - '@xhmikosr/decompress-tar@8.1.0': + '@visx/group@4.0.1-alpha.0(react@19.2.4)': dependencies: - file-type: 20.5.0 - is-stream: 2.0.1 - tar-stream: 3.1.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color + '@types/react': 19.2.11 + classnames: 2.5.1 + react: 19.2.4 - '@xhmikosr/decompress-tarbz2@8.1.0': - dependencies: - '@xhmikosr/decompress-tar': 8.1.0 - file-type: 20.5.0 - is-stream: 2.0.1 - seek-bzip: 2.0.0 - unbzip2-stream: 1.4.3 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color + '@visx/point@4.0.1-alpha.0': {} - '@xhmikosr/decompress-targz@8.1.0': + '@visx/responsive@4.0.1-alpha.0(react@19.2.4)': dependencies: - '@xhmikosr/decompress-tar': 8.1.0 - file-type: 20.5.0 - is-stream: 2.0.1 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color + '@types/lodash': 4.17.25 + '@types/react': 19.2.11 + lodash: 4.17.23 + react: 19.2.4 - '@xhmikosr/decompress-unzip@7.1.0': + '@visx/scale@4.0.1-alpha.0': dependencies: - file-type: 20.5.0 - get-stream: 6.0.1 - yauzl: 3.2.0 - transitivePeerDependencies: - - supports-color + '@visx/vendor': 4.0.0-alpha.0 - '@xhmikosr/decompress@10.2.0': + '@visx/shape@4.0.1-alpha.0(react@19.2.4)': dependencies: - '@xhmikosr/decompress-tar': 8.1.0 - '@xhmikosr/decompress-tarbz2': 8.1.0 - '@xhmikosr/decompress-targz': 8.1.0 - '@xhmikosr/decompress-unzip': 7.1.0 - graceful-fs: 4.2.11 - strip-dirs: 3.0.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color - - '@xhmikosr/downloader@15.2.0': - dependencies: - '@xhmikosr/archive-type': 7.1.0 - '@xhmikosr/decompress': 10.2.0 - content-disposition: 0.5.4 - defaults: 2.0.2 - ext-name: 5.0.0 - file-type: 20.5.0 - filenamify: 6.0.0 - get-stream: 6.0.1 - got: 13.0.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - supports-color + '@types/lodash': 4.17.25 + '@types/react': 19.2.11 + '@visx/curve': 4.0.1-alpha.0 + '@visx/group': 4.0.1-alpha.0(react@19.2.4) + '@visx/scale': 4.0.1-alpha.0 + '@visx/vendor': 4.0.0-alpha.0 + classnames: 2.5.1 + lodash: 4.17.23 + react: 19.2.4 - '@xhmikosr/os-filter-obj@3.0.0': + '@visx/vendor@4.0.0-alpha.0': dependencies: - arch: 3.0.0 - - '@xtuc/ieee754@1.2.0': {} - - '@xtuc/long@4.2.2': {} + '@types/d3-array': 3.0.3 + '@types/d3-color': 3.1.0 + '@types/d3-delaunay': 6.0.1 + '@types/d3-format': 3.0.1 + '@types/d3-geo': 3.1.0 + '@types/d3-interpolate': 3.0.1 + '@types/d3-path': 3.1.1 + '@types/d3-scale': 4.0.2 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.0 + '@types/d3-time-format': 2.1.0 + d3-array: 3.2.1 + d3-color: 3.1.0 + d3-delaunay: 6.0.2 + d3-format: 3.1.0 + d3-geo: 3.1.0 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + internmap: 2.0.3 '@zag-js/accordion@1.39.1': dependencies: @@ -9957,2498 +5719,640 @@ snapshots: '@zag-js/core': 1.39.1 '@zag-js/dom-query': 1.39.1 '@zag-js/types': 1.39.1 - '@zag-js/utils': 1.39.1 - - '@zag-js/types@1.39.1': - dependencies: - csstype: 3.2.3 - - '@zag-js/utils@1.39.1': {} - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - - abstract-logging@2.0.1: {} - - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.0.0 - optional: true - - acorn-import-phases@1.0.4(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - - acorn-jsx@5.3.2(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - - acorn-walk@8.3.5: - dependencies: - acorn: 8.16.0 - - acorn@8.16.0: {} - - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - - ajv-formats@3.0.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - - ajv-formats@3.0.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - - ajv-keywords@3.5.2(ajv@6.14.0): - dependencies: - ajv: 6.14.0 - - ajv-keywords@5.1.0(ajv@8.18.0): - dependencies: - ajv: 8.18.0 - fast-deep-equal: 3.1.3 - - ajv@6.14.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansis@4.2.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - append-field@1.0.0: - optional: true - - arch@3.0.0: {} - - arg@4.1.3: {} - - argon2@0.44.0: - dependencies: - '@phc/format': 1.0.0 - cross-env: 10.1.0 - node-addon-api: 8.5.0 - node-gyp-build: 4.8.4 - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - aria-query@5.3.1: {} - - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - - array-ify@1.0.0: {} - - array-includes@3.1.9: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - math-intrinsics: 1.1.0 - - array-timsort@1.0.3: {} - - array-union@2.1.0: {} - - array.prototype.findlastindex@1.2.6: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-shim-unscopables: 1.1.0 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - - asap@2.0.6: {} - - async-function@1.0.0: {} - - async@3.2.6: {} - - asynckit@0.4.0: {} - - atomic-sleep@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - avvio@9.2.0: - dependencies: - '@fastify/error': 4.2.0 - fastq: 1.20.1 - - aws-ssl-profiles@1.1.2: {} - - axobject-query@4.1.0: {} - - b4a@1.8.0: {} - - babel-jest@29.7.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.29.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.28.6 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@7.0.1: - dependencies: - '@babel/helper-plugin-utils': 7.28.6 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 6.0.3 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.28.0 - - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - - babel-preset-jest@29.6.3(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - - balanced-match@1.0.2: {} - - balanced-match@4.0.3: {} - - balanced-match@4.0.4: - optional: true - - bare-events@2.8.2: {} - - base64-js@1.5.1: {} - - baseline-browser-mapping@2.10.0: {} - - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - - bin-version-check@5.1.0: - dependencies: - bin-version: 6.0.0 - semver: 7.7.4 - semver-truncate: 3.0.0 - - bin-version@6.0.0: - dependencies: - execa: 5.1.1 - find-versions: 5.1.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - body-parser@2.3.0: - dependencies: - bytes: 3.1.2 - content-type: 2.0.0 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - on-finished: 2.4.1 - qs: 6.15.3 - raw-body: 3.0.2 - type-is: 2.1.0 - transitivePeerDependencies: - - supports-color - optional: true - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - brace-expansion@5.0.2: - dependencies: - balanced-match: 4.0.3 - - brace-expansion@5.0.7: - dependencies: - balanced-match: 4.0.4 - optional: true - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001770 - electron-to-chromium: 1.5.302 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - - bs-logger@0.2.6: - dependencies: - fast-json-stable-stringify: 2.1.0 - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - - buffer-crc32@0.2.13: {} - - buffer-equal-constant-time@1.0.1: {} - - buffer-from@1.1.2: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - busboy@1.6.0: - dependencies: - streamsearch: 1.1.0 - optional: true - - bytes@3.1.2: - optional: true - - c12@3.1.0: - dependencies: - chokidar: 4.0.3 - confbox: 0.2.4 - defu: 6.1.4 - dotenv: 16.6.1 - exsolve: 1.0.8 - giget: 2.0.0 - jiti: 2.6.1 - ohash: 2.0.11 - pathe: 2.0.3 - perfect-debounce: 1.0.0 - pkg-types: 2.3.0 - rc9: 2.1.2 - - cacheable-lookup@7.0.0: {} - - cacheable-request@10.2.14: - dependencies: - '@types/http-cache-semantics': 4.2.0 - get-stream: 6.0.1 - http-cache-semantics: 4.2.0 - keyv: 4.5.4 - mimic-response: 4.0.0 - normalize-url: 8.1.1 - responselike: 3.0.0 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.9: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001770: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - char-regex@1.0.2: {} - - chardet@2.1.1: {} - - chevrotain@10.5.0: - dependencies: - '@chevrotain/cst-dts-gen': 10.5.0 - '@chevrotain/gast': 10.5.0 - '@chevrotain/types': 10.5.0 - '@chevrotain/utils': 10.5.0 - lodash: 4.17.21 - regexp-to-ast: 0.5.0 - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - chrome-trace-event@1.0.4: {} - - ci-info@3.9.0: {} - - ci-info@4.4.0: {} - - citty@0.1.6: - dependencies: - consola: 3.4.2 - - citty@0.2.1: {} - - cjs-module-lexer@1.4.3: {} - - class-transformer@0.5.1: - optional: true - - class-validator@0.14.3: - dependencies: - '@types/validator': 13.15.10 - libphonenumber-js: 1.13.8 - validator: 13.15.35 - optional: true - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - - cli-spinners@2.9.2: {} - - cli-table3@0.6.5: - dependencies: - string-width: 4.2.3 - optionalDependencies: - '@colors/colors': 1.5.0 - - cli-width@4.1.0: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone@1.0.4: {} - - clsx@2.1.1: {} - - co@4.6.0: {} - - collect-v8-coverage@1.0.3: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-convert@3.1.3: - dependencies: - color-name: 2.1.0 - - color-name@1.1.4: {} - - color-name@2.1.0: {} - - color-string@2.1.4: - dependencies: - color-name: 2.1.0 - - color@5.0.3: - dependencies: - color-convert: 3.1.3 - color-string: 2.1.4 - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - commander@2.20.3: {} - - commander@4.1.1: {} - - commander@6.2.1: {} - - commander@7.2.0: {} - - commander@8.3.0: {} - - comment-json@4.4.1: - dependencies: - array-timsort: 1.0.3 - core-util-is: 1.0.3 - esprima: 4.0.1 - - commondir@1.0.1: {} - - compare-func@2.0.0: - dependencies: - array-ify: 1.0.0 - dot-prop: 5.3.0 - - component-emitter@1.3.1: {} - - concat-map@0.0.1: {} - - concat-stream@2.0.0: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - typedarray: 0.0.6 - optional: true - - confbox@0.2.4: {} - - consola@3.4.2: {} - - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - - content-disposition@1.1.0: - optional: true - - content-type@1.0.5: - optional: true - - content-type@2.0.0: - optional: true - - conventional-changelog-angular@8.1.0: - dependencies: - compare-func: 2.0.0 - - conventional-changelog-conventionalcommits@9.1.0: - dependencies: - compare-func: 2.0.0 - - conventional-commits-parser@6.2.1: - dependencies: - meow: 13.2.0 - - convert-source-map@2.0.0: {} - - cookie-signature@1.2.2: {} - - cookie@0.6.0: {} - - cookie@0.7.2: - optional: true - - cookie@1.1.1: {} - - cookiejar@2.1.4: {} - - core-util-is@1.0.3: {} - - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - optional: true - - cosmiconfig-typescript-loader@6.2.0(@types/node@22.19.11)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3): - dependencies: - '@types/node': 22.19.11 - cosmiconfig: 9.0.0(typescript@5.9.3) - jiti: 2.6.1 - typescript: 5.9.3 - - cosmiconfig@8.3.6(typescript@5.9.3): - dependencies: - import-fresh: 3.3.1 - js-yaml: 4.1.1 - parse-json: 5.2.0 - path-type: 4.0.0 - optionalDependencies: - typescript: 5.9.3 - - cosmiconfig@9.0.0(typescript@5.9.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 - - create-jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - create-require@1.1.1: {} - - cross-env@10.1.0: - dependencies: - '@epic-web/invariant': 1.0.0 - cross-spawn: 7.0.6 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - csstype@3.2.3: {} - - d3-array@2.12.1: - dependencies: - internmap: 1.0.1 - - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-chord@3.0.1: - dependencies: - d3-path: 3.1.0 - - d3-color@3.1.0: {} - - d3-contour@4.0.2: - dependencies: - d3-array: 3.2.4 - - d3-delaunay@6.0.4: - dependencies: - delaunator: 5.1.0 - - d3-dispatch@3.0.1: {} - - d3-dsv@3.0.1: - dependencies: - commander: 7.2.0 - iconv-lite: 0.6.3 - rw: 1.3.3 - - d3-force@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - - d3-format@3.1.2: {} - - d3-geo-voronoi@2.1.0: - dependencies: - d3-array: 3.2.4 - d3-delaunay: 6.0.4 - d3-geo: 3.1.1 - d3-tricontour: 1.1.0 - - d3-geo@3.1.1: - dependencies: - d3-array: 3.2.4 - - d3-hierarchy@3.1.2: {} - - d3-interpolate-path@2.3.0: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@1.0.9: {} - - d3-path@3.1.0: {} - - d3-quadtree@3.0.1: {} - - d3-random@3.0.1: {} - - d3-sankey@0.12.3: - dependencies: - d3-array: 2.12.1 - d3-shape: 1.3.7 - - d3-scale-chromatic@3.1.0: - dependencies: - d3-color: 3.1.0 - d3-interpolate: 3.0.1 - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.2 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-shape@1.3.7: - dependencies: - d3-path: 1.0.9 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-tile@1.0.0: {} - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - d3-tricontour@1.1.0: - dependencies: - d3-delaunay: 6.0.4 - d3-scale: 4.0.2 - - dargs@8.1.0: {} - - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - debug@3.2.7: - dependencies: - ms: 2.1.3 - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - dedent@1.7.1: {} - - deep-is@0.1.4: {} - - deepmerge-ts@7.1.5: {} - - deepmerge@4.3.1: {} - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - defaults@2.0.2: {} - - defer-to-connect@2.0.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - defu@6.1.4: {} - - delaunator@5.1.0: - dependencies: - robust-predicates: 3.0.3 - - delayed-stream@1.0.0: {} - - denque@2.1.0: {} - - depd@2.0.0: - optional: true - - dequal@2.0.3: {} - - destr@2.0.5: {} - - detect-indent@6.1.0: {} - - detect-libc@2.1.2: {} - - detect-newline@3.1.0: {} - - devalue@5.8.1: {} - - dezalgo@1.0.4: - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - - diff-sequences@29.6.3: {} - - diff@4.0.4: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - dot-prop@5.3.0: - dependencies: - is-obj: 2.0.0 - - dotenv-cli@10.0.0: - dependencies: - cross-spawn: 7.0.6 - dotenv: 17.3.1 - dotenv-expand: 11.0.7 - minimist: 1.2.8 - - dotenv-expand@11.0.7: - dependencies: - dotenv: 16.6.1 - - dotenv-expand@12.0.3: - dependencies: - dotenv: 16.6.1 - - dotenv@16.6.1: {} - - dotenv@17.2.3: {} - - dotenv@17.3.1: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - duplexify@3.7.1: - dependencies: - end-of-stream: 1.4.5 - inherits: 2.0.4 - readable-stream: 2.3.8 - stream-shift: 1.0.3 - - duplexify@4.1.3: - dependencies: - end-of-stream: 1.4.5 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.3 - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - ee-first@1.1.1: - optional: true - - effect@3.18.4: - dependencies: - '@standard-schema/spec': 1.1.0 - fast-check: 3.23.2 - - electron-to-chromium@1.5.302: {} - - emittery@0.13.1: {} - - emoji-regex@8.0.0: {} - - empathic@2.0.0: {} - - enabled@2.0.0: {} - - encodeurl@2.0.0: - optional: true - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - enhanced-resolve@5.19.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.0 - - enhanced-resolve@5.21.6: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.3 - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - env-paths@2.2.1: {} - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - - es-abstract@1.24.2: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.9 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-negative-zero: 2.0.3 - is-regex: 1.2.1 - is-set: 2.0.3 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@2.0.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - - es-toolkit@1.49.0: {} - - esbuild@0.27.3: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 - - escalade@3.2.0: {} - - escape-html@1.0.3: - optional: true - - escape-string-regexp@2.0.0: {} - - escape-string-regexp@4.0.0: {} - - eslint-config-prettier@10.1.8(eslint@9.39.3(jiti@2.7.0)): - dependencies: - eslint: 9.39.3(jiti@2.7.0) - - eslint-import-context@0.1.9(unrs-resolver@1.11.1): - dependencies: - get-tsconfig: 4.14.0 - stable-hash-x: 0.2.0 - optionalDependencies: - unrs-resolver: 1.11.1 - - eslint-import-resolver-node@0.3.10: - dependencies: - debug: 3.2.7 - is-core-module: 2.16.1 - resolve: 2.0.0-next.6 - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.7.0)): - dependencies: - debug: 4.4.3 - eslint: 9.39.3(jiti@2.7.0) - eslint-import-context: 0.1.9(unrs-resolver@1.11.1) - get-tsconfig: 4.14.0 - is-bun-module: 2.0.0 - stable-hash-x: 0.2.0 - tinyglobby: 0.2.15 - unrs-resolver: 1.11.1 - optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.3(jiti@2.7.0)) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.3(jiti@2.7.0)): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - eslint: 9.39.3(jiti@2.7.0) - eslint-import-resolver-node: 0.3.10 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.39.3(jiti@2.7.0)) - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.3(jiti@2.7.0)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.9 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.39.3(jiti@2.7.0) - eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.3(jiti@2.7.0)) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-jest@29.15.0(@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3): - dependencies: - '@typescript-eslint/utils': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - eslint: 9.39.3(jiti@2.7.0) - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - jest: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - eslint-plugin-prettier@5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.3(jiti@2.7.0)))(eslint@9.39.3(jiti@2.7.0))(prettier@3.8.1): - dependencies: - eslint: 9.39.3(jiti@2.7.0) - prettier: 3.8.1 - prettier-linter-helpers: 1.0.1 - synckit: 0.11.12 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@9.39.3(jiti@2.7.0)) - - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint-visitor-keys@5.0.1: {} - - eslint@9.39.3(jiti@2.7.0): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.3(jiti@2.7.0)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.3 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.14.0 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.7.0 - transitivePeerDependencies: - - supports-color - - esm-env@1.2.2: {} - - espree@10.4.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 4.2.1 - - esprima@4.0.1: {} - - esquery@1.7.0: - dependencies: - estraverse: 5.3.0 - - esrap@2.2.13(@typescript-eslint/types@8.56.0): - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - optionalDependencies: - '@typescript-eslint/types': 8.56.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@4.3.0: {} - - estraverse@5.3.0: {} - - estree-walker@2.0.2: {} - - esutils@2.0.3: {} - - etag@1.8.1: - optional: true - - event-target-shim@5.0.1: {} - - events-universal@1.0.1: - dependencies: - bare-events: 2.8.2 - transitivePeerDependencies: - - bare-abort-controller - - events@3.3.0: {} - - eventsource-parser@3.0.8: {} - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - exit@0.1.2: {} - - expect@29.7.0: - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - - expect@30.2.0: - dependencies: - '@jest/expect-utils': 30.2.0 - '@jest/get-type': 30.1.0 - jest-matcher-utils: 30.2.0 - jest-message-util: 30.2.0 - jest-mock: 30.2.0 - jest-util: 30.2.0 - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.3.0 - content-disposition: 1.1.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.15.3 - range-parser: 1.3.0 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.1.0 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - optional: true - - exsolve@1.0.8: {} - - ext-list@2.2.2: - dependencies: - mime-db: 1.54.0 - - ext-name@5.0.0: - dependencies: - ext-list: 2.2.2 - sort-keys-length: 1.0.1 - - extendable-error@0.1.7: {} - - fast-check@3.23.2: - dependencies: - pure-rand: 6.1.0 - - fast-decode-uri-component@1.0.1: {} - - fast-deep-equal@3.1.3: {} - - fast-diff@1.3.0: {} - - fast-fifo@1.3.2: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-json-stringify@6.3.0: - dependencies: - '@fastify/merge-json-schemas': 0.2.1 - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - fast-uri: 3.1.0 - json-schema-ref-resolver: 3.0.0 - rfdc: 1.4.1 - - fast-levenshtein@2.0.6: {} - - fast-querystring@1.1.2: - dependencies: - fast-decode-uri-component: 1.0.1 - - fast-safe-stringify@2.1.1: {} - - fast-uri@3.1.0: {} - - fastify-plugin@5.1.0: {} - - fastify@5.7.4: - dependencies: - '@fastify/ajv-compiler': 4.0.5 - '@fastify/error': 4.2.0 - '@fastify/fast-json-stringify-compiler': 5.0.3 - '@fastify/proxy-addr': 5.1.0 - abstract-logging: 2.0.1 - avvio: 9.2.0 - fast-json-stringify: 6.3.0 - find-my-way: 9.4.0 - light-my-request: 6.6.0 - pino: 10.3.1 - process-warning: 5.0.0 - rfdc: 1.4.1 - secure-json-parse: 4.1.0 - semver: 7.7.4 - toad-cache: 3.7.0 - - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - fecha@4.2.3: {} - - fflate@0.8.2: {} - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 + '@zag-js/utils': 1.39.1 - file-type@20.5.0: + '@zag-js/types@1.39.1': dependencies: - '@tokenizer/inflate': 0.2.7 - strtok3: 10.3.4 - token-types: 6.1.2 - uint8array-extras: 1.5.0 - transitivePeerDependencies: - - supports-color + csstype: 3.2.3 - file-type@21.3.0: - dependencies: - '@tokenizer/inflate': 0.4.1 - strtok3: 10.3.4 - token-types: 6.1.2 - uint8array-extras: 1.5.0 - transitivePeerDependencies: - - supports-color + '@zag-js/utils@1.39.1': {} - filename-reserved-regex@3.0.0: {} + acorn@8.16.0: {} - filenamify@6.0.0: + ajv@8.18.0: dependencies: - filename-reserved-regex: 3.0.0 + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 + ansi-colors@4.1.3: {} - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - 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 - optional: true + ansi-regex@5.0.1: {} - find-my-way@9.4.0: + ansi-styles@4.3.0: dependencies: - fast-deep-equal: 3.1.3 - fast-querystring: 1.1.2 - safe-regex2: 5.0.0 + color-convert: 2.0.1 - find-up@4.1.0: + argon2@0.45.1: dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 + '@phc/format': 1.0.0 + cross-env: 10.1.0 + node-addon-api: 8.9.2 + node-gyp-build: 4.8.4 - find-up@5.0.0: + argparse@1.0.10: dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 + sprintf-js: 1.0.3 - find-versions@5.1.0: - dependencies: - semver-regex: 4.0.5 + argparse@2.0.1: {} - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 + aria-query@5.3.1: {} - flatted@3.3.3: {} + array-ify@1.0.0: {} - fn.name@1.1.0: {} + array-union@2.1.0: {} - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 + aws-ssl-profiles@1.1.2: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 + axobject-query@4.1.0: {} - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18))): + better-path-resolve@1.0.0: dependencies: - '@babel/code-frame': 7.29.0 - chalk: 4.1.2 - chokidar: 4.0.3 - cosmiconfig: 8.3.6(typescript@5.9.3) - deepmerge: 4.3.1 - fs-extra: 10.1.0 - memfs: 3.5.3 - minimatch: 3.1.2 - node-abort-controller: 3.1.1 - schema-utils: 3.3.0 - semver: 7.7.4 - tapable: 2.3.0 - typescript: 5.9.3 - webpack: 5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18)) - - form-data-encoder@2.1.4: {} + is-windows: 1.0.2 - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 + better-result@2.10.0: {} - formidable@3.5.4: + braces@3.0.3: dependencies: - '@paralleldrive/cuid2': 2.3.1 - dezalgo: 1.0.4 - once: 1.4.0 - - forwarded@0.2.0: - optional: true + fill-range: 7.1.1 - fresh@2.0.0: + buffer-from@1.1.2: optional: true - fs-extra@10.1.0: + c12@3.3.4: dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 + chokidar: 5.0.0 + confbox: 0.2.4 + defu: 6.1.7 + dotenv: 17.3.1 + exsolve: 1.0.8 + giget: 3.3.1 + jiti: 2.7.0 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.0 + rc9: 3.1.0 - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 + callsites@3.1.0: {} - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 + chardet@2.1.1: {} - fs-monkey@1.1.0: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 - fs.realpath@1.0.0: {} + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 - fsevents@2.3.3: - optional: true + ci-info@3.9.0: {} - function-bind@1.1.2: {} + classnames@2.5.1: {} - function.prototype.name@1.1.8: + cliui@8.0.1: dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 - functions-have-names@1.2.3: {} + clsx@2.1.1: {} - generate-function@2.3.1: + color-convert@2.0.1: dependencies: - is-property: 1.0.2 + color-name: 1.1.4 + + color-name@1.1.4: {} - generator-function@2.0.1: {} + commander@2.20.3: + optional: true - gensync@1.0.0-beta.2: {} + commander@7.2.0: {} - get-caller-file@2.0.5: {} + commondir@1.0.1: {} - get-intrinsic@1.3.0: + compare-func@2.0.0: dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-package-type@0.1.0: {} + array-ify: 1.0.0 + dot-prop: 5.3.0 - get-port-please@3.2.0: {} + confbox@0.2.4: {} - get-proto@1.0.1: + conventional-changelog-angular@8.1.0: dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@6.0.1: {} + compare-func: 2.0.0 - get-symbol-description@1.1.0: + conventional-changelog-conventionalcommits@9.1.0: dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 + compare-func: 2.0.0 - get-tsconfig@4.14.0: + conventional-commits-parser@6.2.1: dependencies: - resolve-pkg-maps: 1.0.0 + meow: 13.2.0 + + cookie@0.6.0: {} - giget@2.0.0: + cosmiconfig-typescript-loader@6.2.0(@types/node@26.6.3)(cosmiconfig@9.0.0(typescript@7.0.2))(typescript@7.0.2): dependencies: - citty: 0.1.6 - consola: 3.4.2 - defu: 6.1.4 - node-fetch-native: 1.6.7 - nypm: 0.6.5 - pathe: 2.0.3 + '@types/node': 26.6.3 + cosmiconfig: 9.0.0(typescript@7.0.2) + jiti: 2.6.1 + typescript: 7.0.2 - git-raw-commits@4.0.0: + cosmiconfig@9.0.0(typescript@7.0.2): dependencies: - dargs: 8.1.0 - meow: 12.1.1 - split2: 4.2.0 + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 7.0.2 - glob-parent@5.1.2: + cross-env@10.1.0: dependencies: - is-glob: 4.0.3 + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 - glob-parent@6.0.2: + cross-spawn@7.0.6: dependencies: - is-glob: 4.0.3 + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - glob-to-regexp@0.4.1: {} + csstype@3.2.3: {} - glob@11.1.0: + d3-array@2.12.1: dependencies: - foreground-child: 3.3.1 - jackspeak: 4.2.3 - minimatch: 10.2.5 - minipass: 7.1.3 - package-json-from-dist: 1.0.1 - path-scurry: 2.0.2 - optional: true + internmap: 1.0.1 - glob@13.0.0: + d3-array@3.2.1: dependencies: - minimatch: 10.2.2 - minipass: 7.1.3 - path-scurry: 2.0.2 + internmap: 2.0.3 - glob@7.2.3: + d3-array@3.2.4: 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 + internmap: 2.0.3 - global-directory@4.0.1: + d3-chord@3.0.1: dependencies: - ini: 4.1.1 - - globals@14.0.0: {} + d3-path: 3.1.0 - globals@16.5.0: {} + d3-color@3.1.0: {} - globalthis@1.0.4: + d3-contour@4.0.2: dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 + d3-array: 3.2.4 - globby@11.1.0: + d3-delaunay@6.0.2: dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} + delaunator: 5.1.0 - got@13.0.0: + d3-delaunay@6.0.4: dependencies: - '@sindresorhus/is': 5.6.0 - '@szmarczak/http-timer': 5.0.1 - cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 - decompress-response: 6.0.0 - form-data-encoder: 2.1.4 - get-stream: 6.0.1 - http2-wrapper: 2.2.1 - lowercase-keys: 3.0.0 - p-cancelable: 3.0.0 - responselike: 3.0.0 - - graceful-fs@4.2.11: {} - - grammex@3.1.12: {} + delaunator: 5.1.0 - graphmatch@1.1.1: {} + d3-dispatch@3.0.1: {} - handlebars@4.7.8: + d3-dsv@3.0.1: dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 - - has-bigints@1.1.0: {} - - has-flag@4.0.0: {} + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 - has-property-descriptors@1.0.2: + d3-force@3.0.0: dependencies: - es-define-property: 1.0.1 + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 + d3-format@3.1.0: {} - has-symbols@1.1.0: {} + d3-format@3.1.2: {} - has-tostringtag@1.0.2: + d3-geo-voronoi@2.1.0: dependencies: - has-symbols: 1.1.0 + d3-array: 3.2.4 + d3-delaunay: 6.0.4 + d3-geo: 3.1.1 + d3-tricontour: 1.1.0 - hasown@2.0.2: + d3-geo@3.1.0: dependencies: - function-bind: 1.1.2 + d3-array: 3.2.4 - hono@4.11.4: {} + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 - html-escaper@2.0.2: {} + d3-hierarchy@3.1.2: {} - http-cache-semantics@4.2.0: {} + d3-interpolate-path@2.3.0: {} - http-errors@2.0.1: + d3-interpolate@3.0.1: dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.2 - toidentifier: 1.0.1 - optional: true - - http-status-codes@2.3.0: {} + d3-color: 3.1.0 - http2-wrapper@2.2.1: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 + d3-path@1.0.9: {} - human-id@4.1.3: {} + d3-path@3.1.0: {} - human-signals@2.1.0: {} + d3-quadtree@3.0.1: {} - husky@9.1.7: {} + d3-random@3.0.1: {} - iconv-lite@0.6.3: + d3-sankey@0.12.3: dependencies: - safer-buffer: 2.1.2 + d3-array: 2.12.1 + d3-shape: 1.3.7 - iconv-lite@0.7.2: + d3-scale-chromatic@3.1.0: dependencies: - safer-buffer: 2.1.2 + d3-color: 3.1.0 + d3-interpolate: 3.0.1 - iconv-lite@0.7.3: + d3-scale@4.0.2: dependencies: - safer-buffer: 2.1.2 - optional: true + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 - ieee754@1.2.1: {} + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 - ignore@5.3.2: {} + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 - ignore@7.0.5: {} + d3-tile@1.0.0: {} - import-fresh@3.3.1: + d3-time-format@4.1.0: dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 + d3-time: 3.1.0 - import-local@3.2.0: + d3-time@3.1.0: dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - import-meta-resolve@4.2.0: {} + d3-array: 3.2.4 - imurmurhash@0.1.4: {} + d3-timer@3.0.1: {} - inflight@1.0.6: + d3-tricontour@1.1.0: dependencies: - once: 1.4.0 - wrappy: 1.0.2 + d3-delaunay: 6.0.4 + d3-scale: 4.0.2 - inherits@2.0.4: {} + dargs@8.1.0: {} - ini@4.1.1: {} + deepmerge-ts@7.1.5: {} - inspect-with-kind@1.0.5: - dependencies: - kind-of: 6.0.3 + deepmerge@4.3.1: {} - internal-slot@1.1.0: + defu@6.1.7: {} + + delaunator@5.1.0: dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 + robust-predicates: 3.0.3 - internmap@1.0.1: {} + denque@2.1.0: {} - internmap@2.0.3: {} + dequal@2.0.3: {} - ipaddr.js@1.9.1: - optional: true + destr@2.0.5: {} - ipaddr.js@2.3.0: {} + detect-indent@6.1.0: {} - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 + detect-libc@2.1.2: {} - is-arrayish@0.2.1: {} + devalue@5.8.1: {} - is-async-function@2.1.1: + dir-glob@3.0.1: dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 + path-type: 4.0.0 - is-bigint@1.1.0: + dot-prop@5.3.0: dependencies: - has-bigints: 1.1.0 + is-obj: 2.0.0 - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 + dotenv@17.3.1: {} + + dotenv@18.0.4: {} - is-bun-module@2.0.0: + effect@3.20.0: dependencies: - semver: 7.7.4 + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 - is-callable@1.2.7: {} + elkjs@0.11.1: {} - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 + emoji-regex@8.0.0: {} - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 + empathic@2.0.0: {} - is-date-object@1.1.0: + enhanced-resolve@5.21.6: dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} + graceful-fs: 4.2.11 + tapable: 2.3.3 - is-finalizationregistry@1.1.1: + enquirer@2.4.1: dependencies: - call-bound: 1.0.4 + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 - is-fullwidth-code-point@3.0.0: {} + env-paths@2.2.1: {} - is-generator-fn@2.1.0: {} + env-paths@3.0.0: {} - is-generator-function@1.1.2: + error-ex@1.3.4: dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 + is-arrayish: 0.2.1 - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 + es-toolkit@1.49.0: {} + + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 - is-interactive@1.0.0: {} + esbuild@0.28.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.2 + '@esbuild/android-arm': 0.28.2 + '@esbuild/android-arm64': 0.28.2 + '@esbuild/android-x64': 0.28.2 + '@esbuild/darwin-arm64': 0.28.2 + '@esbuild/darwin-x64': 0.28.2 + '@esbuild/freebsd-arm64': 0.28.2 + '@esbuild/freebsd-x64': 0.28.2 + '@esbuild/linux-arm': 0.28.2 + '@esbuild/linux-arm64': 0.28.2 + '@esbuild/linux-ia32': 0.28.2 + '@esbuild/linux-loong64': 0.28.2 + '@esbuild/linux-mips64el': 0.28.2 + '@esbuild/linux-ppc64': 0.28.2 + '@esbuild/linux-riscv64': 0.28.2 + '@esbuild/linux-s390x': 0.28.2 + '@esbuild/linux-x64': 0.28.2 + '@esbuild/netbsd-arm64': 0.28.2 + '@esbuild/netbsd-x64': 0.28.2 + '@esbuild/openbsd-arm64': 0.28.2 + '@esbuild/openbsd-x64': 0.28.2 + '@esbuild/openharmony-arm64': 0.28.2 + '@esbuild/sunos-x64': 0.28.2 + '@esbuild/win32-arm64': 0.28.2 + '@esbuild/win32-ia32': 0.28.2 + '@esbuild/win32-x64': 0.28.2 - is-map@2.0.3: {} + escalade@3.2.0: {} - is-module@1.0.0: {} + esm-env@1.2.2: {} - is-negative-zero@2.0.3: {} + esprima@4.0.1: {} - is-number-object@1.1.1: + esrap@2.2.13(@typescript-eslint/types@8.56.0): dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 + '@jridgewell/sourcemap-codec': 1.5.5 + optionalDependencies: + '@typescript-eslint/types': 8.56.0 - is-number@7.0.0: {} + estree-walker@2.0.2: {} - is-obj@2.0.0: {} + eventsource-parser@3.0.8: {} - is-plain-obj@1.1.0: {} + exsolve@1.0.8: {} - is-plain-obj@4.1.0: {} + extendable-error@0.1.7: {} - is-promise@4.0.0: - optional: true + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 - is-property@1.0.2: {} + fast-decode-uri-component@1.0.1: {} - is-reference@1.2.1: - dependencies: - '@types/estree': 1.0.8 + fast-deep-equal@3.1.3: {} - is-reference@3.0.3: + fast-glob@3.3.3: dependencies: - '@types/estree': 1.0.8 + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 - is-regex@1.2.1: + fast-querystring@1.1.2: dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 + fast-decode-uri-component: 1.0.1 - is-set@2.0.3: {} + fast-uri@3.1.0: {} - is-shared-array-buffer@1.0.4: + fastq@1.20.1: dependencies: - call-bound: 1.0.4 + reusify: 1.1.0 - is-stream@2.0.1: {} + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 - is-string@1.1.1: + fill-range@7.1.1: dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 + to-regex-range: 5.0.1 - is-subdir@1.2.0: + find-my-way@9.7.0: dependencies: - better-path-resolve: 1.0.0 + fast-deep-equal: 3.1.3 + fast-querystring: 1.1.2 + safe-regex2: 5.0.0 - is-symbol@1.1.1: + find-up@4.1.0: dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 + locate-path: 5.0.0 + path-exists: 4.0.0 - is-typed-array@1.1.15: + foreground-child@3.3.1: dependencies: - which-typed-array: 1.1.20 - - is-unicode-supported@0.1.0: {} - - is-weakmap@2.0.2: {} + cross-spawn: 7.0.6 + signal-exit: 4.1.0 - is-weakref@1.1.1: + fs-extra@7.0.1: dependencies: - call-bound: 1.0.4 + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 - is-weakset@2.0.4: + fs-extra@8.1.0: dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-windows@1.0.2: {} - - isarray@1.0.0: {} - - isarray@2.0.5: {} + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 - isexe@2.0.0: {} + fsevents@2.3.3: + optional: true - istanbul-lib-coverage@3.2.2: {} + function-bind@1.1.2: {} - istanbul-lib-instrument@5.2.1: + generate-function@2.3.1: dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + is-property: 1.0.2 - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color + get-caller-file@2.0.5: {} - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 + get-port-please@3.2.0: {} - istanbul-lib-source-maps@4.0.1: - dependencies: - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 - transitivePeerDependencies: - - supports-color + giget@3.3.1: {} - istanbul-reports@3.2.0: + git-raw-commits@4.0.0: dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - iterare@1.2.1: {} + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 - jackspeak@4.2.3: + glob-parent@5.1.2: dependencies: - '@isaacs/cliui': 9.0.0 - optional: true + is-glob: 4.0.3 - jest-changed-files@29.7.0: + global-directory@4.0.1: dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 + ini: 4.1.1 - jest-circus@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.7.1 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node + globals@16.5.0: {} - jest-config@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)): + globby@11.1.0: dependencies: - '@babel/core': 7.29.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.29.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.19.11 - ts-node: 10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - jest-diff@30.2.0: - dependencies: - '@jest/diff-sequences': 30.0.1 - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - pretty-format: 30.2.0 + graceful-fs@4.2.11: {} - jest-docblock@29.7.0: - dependencies: - detect-newline: 3.1.0 + grammex@3.1.12: {} - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 + graphmatch@1.1.1: {} - jest-environment-node@29.7.0: + hasown@2.0.2: dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - jest-mock: 29.7.0 - jest-util: 29.7.0 + function-bind: 1.1.2 - jest-esm-transformer@1.0.0: + hono-openapi@1.3.3(effect@3.20.0)(hono@4.13.9)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5): dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@hono/standard-validator': 0.4.0(@standard-schema/spec@1.1.0)(hono@4.13.9) + '@standard-community/standard-json': 0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(effect@3.20.0)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5) + '@standard-community/standard-openapi': 0.2.10(@standard-community/standard-json@0.3.6(@standard-schema/spec@1.1.0)(@types/json-schema@7.0.15)(effect@3.20.0)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5))(@standard-schema/spec@1.1.0)(effect@3.20.0)(openapi-types@12.1.3)(valibot@1.4.2(typescript@7.0.2))(zod@4.6.5) + '@standard-schema/spec': 1.1.0 + '@types/json-schema': 7.0.15 + hono: 4.13.9 + openapi-types: 12.1.3 transitivePeerDependencies: - - supports-color - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 22.19.11 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 + - '@valibot/to-json-schema' + - arktype + - effect + - sury + - typebox + - valibot + - zod + - zod-openapi + - zod-to-json-schema - jest-haste-map@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.11 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 30.0.1 - jest-util: 30.2.0 - jest-worker: 30.2.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 + hono@4.13.9: {} - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 + human-id@4.1.3: {} - jest-matcher-utils@29.7.0: - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 + husky@9.1.7: {} - jest-matcher-utils@30.2.0: + iconv-lite@0.6.3: dependencies: - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - jest-diff: 30.2.0 - pretty-format: 30.2.0 + safer-buffer: 2.1.2 - jest-message-util@29.7.0: + iconv-lite@0.7.2: dependencies: - '@babel/code-frame': 7.29.0 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 + safer-buffer: 2.1.2 - jest-message-util@30.2.0: + iconv-lite@0.7.3: dependencies: - '@babel/code-frame': 7.29.0 - '@jest/types': 30.2.0 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 30.2.0 - slash: 3.0.0 - stack-utils: 2.0.6 + safer-buffer: 2.1.2 - jest-mock-extended@4.0.0(@jest/globals@30.2.0)(jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3): - dependencies: - '@jest/globals': 30.2.0 - jest: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - ts-essentials: 10.1.1(typescript@5.9.3) - typescript: 5.9.3 + ignore@5.3.2: {} - jest-mock@29.7.0: + import-fresh@3.3.1: dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - jest-util: 29.7.0 + parent-module: 1.0.1 + resolve-from: 4.0.0 - jest-mock@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.11 - jest-util: 30.2.0 + import-meta-resolve@4.2.0: {} - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 + ini@4.1.1: {} - jest-regex-util@29.6.3: {} + internmap@1.0.1: {} - jest-regex-util@30.0.1: {} + internmap@2.0.3: {} - jest-resolve-dependencies@29.7.0: - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color + is-arrayish@0.2.1: {} - jest-resolve@29.7.0: + is-core-module@2.16.1: dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.11 - resolve.exports: 2.0.3 - slash: 3.0.0 + hasown: 2.0.2 - jest-runner@29.7.0: - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color + is-extglob@2.1.1: {} - jest-runtime@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - chalk: 4.1.2 - cjs-module-lexer: 1.4.3 - collect-v8-coverage: 1.0.3 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color - - jest-snapshot@30.2.0: - dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 - '@jest/expect-utils': 30.2.0 - '@jest/get-type': 30.1.0 - '@jest/snapshot-utils': 30.2.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - chalk: 4.1.2 - expect: 30.2.0 - graceful-fs: 4.2.11 - jest-diff: 30.2.0 - jest-matcher-utils: 30.2.0 - jest-message-util: 30.2.0 - jest-util: 30.2.0 - pretty-format: 30.2.0 - semver: 7.7.4 - synckit: 0.11.12 - transitivePeerDependencies: - - supports-color + is-fullwidth-code-point@3.0.0: {} - jest-util@29.7.0: + is-glob@4.0.3: dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 + is-extglob: 2.1.1 - jest-util@30.2.0: - dependencies: - '@jest/types': 30.2.0 - '@types/node': 22.19.11 - chalk: 4.1.2 - ci-info: 4.4.0 - graceful-fs: 4.2.11 - picomatch: 4.0.3 + is-module@1.0.0: {} - jest-validate@29.7.0: - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 + is-number@7.0.0: {} - jest-watcher@29.7.0: - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.19.11 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 + is-obj@2.0.0: {} - jest-worker@27.5.1: - dependencies: - '@types/node': 22.19.11 - merge-stream: 2.0.0 - supports-color: 8.1.1 + is-plain-obj@4.1.0: {} + + is-property@1.0.2: {} - jest-worker@29.7.0: + is-reference@1.2.1: dependencies: - '@types/node': 22.19.11 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 + '@types/estree': 1.0.8 - jest-worker@30.2.0: + is-reference@3.0.3: dependencies: - '@types/node': 22.19.11 - '@ungap/structured-clone': 1.3.0 - jest-util: 30.2.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 + '@types/estree': 1.0.8 - jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)): + is-subdir@1.2.0: dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node + better-path-resolve: 1.0.0 + + is-windows@1.0.2: {} + + isexe@2.0.0: {} jiti@2.6.1: {} @@ -12465,77 +6369,17 @@ snapshots: dependencies: argparse: 2.0.1 - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-ref-resolver@3.0.0: - dependencies: - dequal: 2.0.3 - - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@1.0.2: - dependencies: - minimist: 1.2.8 - - json5@2.2.3: {} - - jsonc-parser@3.3.1: {} - jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jsonwebtoken@9.0.3: - dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.4 - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kind-of@6.0.3: {} - - kleur@3.0.3: {} - kleur@4.1.5: {} - kuler@2.0.0: {} - - layerchart@2.0.0-next.65(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6): + layerchart@2.0.0-next.65(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6): dependencies: '@dagrejs/dagre': 2.0.4 '@layerstack/svelte-actions': 1.0.1-next.18 @@ -12565,28 +6409,12 @@ snapshots: d3-tile: 1.0.0 d3-time: 3.1.0 memoize: 10.2.0 - runed: 0.37.1(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6) + runed: 0.37.1(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6) svelte: 5.56.4(@typescript-eslint/types@8.56.0) transitivePeerDependencies: - '@sveltejs/kit' - zod - leven@3.1.0: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - libphonenumber-js@1.13.8: - optional: true - - light-my-request@6.6.0: - dependencies: - cookie: 1.1.1 - process-warning: 4.0.1 - set-cookie-parser: 2.7.2 - lightningcss-android-arm64@1.32.0: optional: true @@ -12633,386 +6461,96 @@ snapshots: lightningcss-linux-arm64-musl: 1.32.0 lightningcss-linux-x64-gnu: 1.32.0 lightningcss-linux-x64-musl: 1.32.0 - lightningcss-win32-arm64-msvc: 1.32.0 - lightningcss-win32-x64-msvc: 1.32.0 - - lilconfig@2.1.0: {} - - lines-and-columns@1.2.4: {} - - load-esm@1.0.3: {} - - loader-runner@4.3.1: {} - - locate-character@3.0.0: {} - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.camelcase@4.3.0: {} - - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - - lodash.kebabcase@4.1.1: {} - - lodash.memoize@4.1.2: {} - - lodash.merge@4.6.2: {} - - lodash.mergewith@4.6.2: {} - - lodash.once@4.1.1: {} - - lodash.snakecase@4.1.1: {} - - lodash.startcase@4.4.0: {} - - lodash.upperfirst@4.3.1: {} - - lodash@4.17.21: {} - - lodash@4.17.23: {} - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - logform@2.7.0: - dependencies: - '@colors/colors': 1.6.0 - '@types/triple-beam': 1.3.5 - fecha: 4.2.3 - ms: 2.1.3 - safe-stable-stringify: 2.5.0 - triple-beam: 1.4.1 - - long@5.3.2: {} - - lowercase-keys@3.0.0: {} - - lru-cache@11.2.6: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lru.min@1.1.4: {} - - lz-string@1.5.0: {} - - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - make-dir@4.0.0: - dependencies: - semver: 7.7.4 - - make-error@1.3.6: {} - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - - math-intrinsics@1.1.0: {} - - media-typer@0.3.0: - optional: true - - media-typer@1.1.0: - optional: true - - memfs@3.5.3: - dependencies: - fs-monkey: 1.1.0 - - memoize@10.2.0: - dependencies: - mimic-function: 5.0.1 - - meow@12.1.1: {} - - meow@13.2.0: {} - - merge-descriptors@2.0.0: - optional: true - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - methods@1.1.2: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: {} - - mime-db@1.54.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - optional: true - - mime@2.6.0: {} - - mime@3.0.0: - optional: true - - mimic-fn@2.1.0: {} - - mimic-function@5.0.1: {} - - mimic-response@3.1.0: {} - - mimic-response@4.0.0: {} - - minimatch@10.2.2: - dependencies: - brace-expansion: 5.0.2 - - minimatch@10.2.5: - dependencies: - brace-expansion: 5.0.7 - optional: true - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass@7.1.3: {} - - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - optional: true - - mri@1.2.0: {} - - mrmime@2.0.1: {} - - ms@2.1.3: {} - - multer@2.0.2: - dependencies: - append-field: 1.0.0 - busboy: 1.6.0 - concat-stream: 2.0.0 - mkdirp: 0.5.6 - object-assign: 4.1.1 - type-is: 1.6.18 - xtend: 4.0.2 - optional: true - - mute-stream@2.0.0: {} - - mysql2@3.15.3: - dependencies: - aws-ssl-profiles: 1.1.2 - denque: 2.1.0 - generate-function: 2.3.1 - iconv-lite: 0.7.2 - long: 5.3.2 - lru.min: 1.1.4 - named-placeholders: 1.1.6 - seq-queue: 0.0.5 - sqlstring: 2.3.3 - - named-placeholders@1.1.6: - dependencies: - lru.min: 1.1.4 - - nanoid@3.3.11: {} - - napi-postinstall@0.3.4: {} - - natural-compare@1.4.0: {} - - negotiator@1.0.0: - optional: true + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 - neo-async@2.6.2: {} + lines-and-columns@1.2.4: {} - nest-winston@1.10.2(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(winston@3.19.0): - dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - fast-safe-stringify: 2.1.1 - winston: 3.19.0 + locate-character@3.0.0: {} - nestjs-zod@5.1.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/swagger@11.2.6(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2))(rxjs@7.8.2)(zod@4.3.6): + locate-path@5.0.0: dependencies: - '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - deepmerge: 4.3.1 - rxjs: 7.8.2 - zod: 4.3.6 - optionalDependencies: - '@nestjs/swagger': 11.2.6(@fastify/static@8.3.0)(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.14)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2) + p-locate: 4.1.0 - node-abort-controller@3.1.1: {} + lodash.camelcase@4.3.0: {} - node-addon-api@8.5.0: {} + lodash.kebabcase@4.1.1: {} - node-emoji@1.11.0: - dependencies: - lodash: 4.17.23 + lodash.mergewith@4.6.2: {} - node-exports-info@1.6.0: - dependencies: - array.prototype.flatmap: 1.3.3 - es-errors: 1.3.0 - object.entries: 1.1.9 - semver: 6.3.1 + lodash.snakecase@4.1.1: {} - node-fetch-native@1.6.7: {} + lodash.startcase@4.4.0: {} - node-gyp-build@4.8.4: {} + lodash.upperfirst@4.3.1: {} - node-int64@0.4.0: {} + lodash@4.17.23: {} - node-releases@2.0.27: {} + long@5.3.2: {} - normalize-path@3.0.0: {} + lru.min@1.1.4: {} - normalize-url@8.1.1: {} + lz-string@1.5.0: {} - npm-run-path@4.0.1: + magic-string@0.30.21: dependencies: - path-key: 3.1.1 + '@jridgewell/sourcemap-codec': 1.5.5 - nypm@0.6.5: + memoize@10.2.0: dependencies: - citty: 0.2.1 - pathe: 2.0.3 - tinyexec: 1.0.2 + mimic-function: 5.0.1 - object-assign@4.1.1: - optional: true + meow@12.1.1: {} - object-inspect@1.13.4: {} + meow@13.2.0: {} - object-keys@1.1.1: {} + merge2@1.4.1: {} - object.assign@4.1.7: + micromatch@4.0.8: dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 + braces: 3.0.3 + picomatch: 2.3.1 - object.entries@1.1.9: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 + mimic-function@5.0.1: {} - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-object-atoms: 1.1.1 + minimist@1.2.8: {} - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 + mri@1.2.0: {} - object.values@1.2.1: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 + mrmime@2.0.1: {} - obug@2.1.1: {} + mysql2@3.15.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.3 + long: 5.3.2 + lru.min: 1.1.4 + named-placeholders: 1.1.6 + seq-queue: 0.0.5 + sqlstring: 2.3.3 - ohash@2.0.11: {} + named-placeholders@1.1.6: + dependencies: + lru.min: 1.1.4 - on-exit-leak-free@2.1.2: {} + nanoid@3.3.11: {} - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - optional: true + nanoid@5.1.16: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 + node-addon-api@8.9.2: {} - one-time@1.0.0: - dependencies: - fn.name: 1.1.0 + node-gyp-build@4.8.4: {} - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 + obug@2.1.1: {} - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 + ohash@2.0.11: {} - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 + openapi-types@12.1.3: {} outdent@0.5.0: {} - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - oxfmt@0.57.0(svelte@5.56.4(@typescript-eslint/types@8.56.0)): dependencies: tinypool: 2.1.0 @@ -13038,6 +6576,31 @@ snapshots: '@oxfmt/binding-win32-x64-msvc': 0.57.0 svelte: 5.56.4(@typescript-eslint/types@8.56.0) + oxfmt@0.70.0(svelte@5.56.4(@typescript-eslint/types@8.56.0)): + dependencies: + tinypool: 2.1.2 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.70.0 + '@oxfmt/binding-android-arm64': 0.70.0 + '@oxfmt/binding-darwin-arm64': 0.70.0 + '@oxfmt/binding-darwin-x64': 0.70.0 + '@oxfmt/binding-freebsd-x64': 0.70.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.70.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.70.0 + '@oxfmt/binding-linux-arm64-gnu': 0.70.0 + '@oxfmt/binding-linux-arm64-musl': 0.70.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.70.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.70.0 + '@oxfmt/binding-linux-riscv64-musl': 0.70.0 + '@oxfmt/binding-linux-s390x-gnu': 0.70.0 + '@oxfmt/binding-linux-x64-gnu': 0.70.0 + '@oxfmt/binding-linux-x64-musl': 0.70.0 + '@oxfmt/binding-openharmony-arm64': 0.70.0 + '@oxfmt/binding-win32-arm64-msvc': 0.70.0 + '@oxfmt/binding-win32-ia32-msvc': 0.70.0 + '@oxfmt/binding-win32-x64-msvc': 0.70.0 + svelte: 5.56.4(@typescript-eslint/types@8.56.0) + oxlint-tsgolint@0.17.4: optionalDependencies: '@oxlint-tsgolint/darwin-arm64': 0.17.4 @@ -13047,6 +6610,15 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 0.17.4 '@oxlint-tsgolint/win32-x64': 0.17.4 + oxlint-tsgolint@7.0.2003: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 7.0.2003 + '@oxlint-tsgolint/darwin-x64': 7.0.2003 + '@oxlint-tsgolint/linux-arm64': 7.0.2003 + '@oxlint-tsgolint/linux-x64': 7.0.2003 + '@oxlint-tsgolint/win32-arm64': 7.0.2003 + '@oxlint-tsgolint/win32-x64': 7.0.2003 + oxlint@1.72.0(oxlint-tsgolint@0.17.4): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.72.0 @@ -13070,7 +6642,28 @@ snapshots: '@oxlint/binding-win32-x64-msvc': 1.72.0 oxlint-tsgolint: 0.17.4 - p-cancelable@3.0.0: {} + oxlint@1.85.0(oxlint-tsgolint@7.0.2003): + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.85.0 + '@oxlint/binding-android-arm64': 1.85.0 + '@oxlint/binding-darwin-arm64': 1.85.0 + '@oxlint/binding-darwin-x64': 1.85.0 + '@oxlint/binding-freebsd-x64': 1.85.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.85.0 + '@oxlint/binding-linux-arm-musleabihf': 1.85.0 + '@oxlint/binding-linux-arm64-gnu': 1.85.0 + '@oxlint/binding-linux-arm64-musl': 1.85.0 + '@oxlint/binding-linux-ppc64-gnu': 1.85.0 + '@oxlint/binding-linux-riscv64-gnu': 1.85.0 + '@oxlint/binding-linux-riscv64-musl': 1.85.0 + '@oxlint/binding-linux-s390x-gnu': 1.85.0 + '@oxlint/binding-linux-x64-gnu': 1.85.0 + '@oxlint/binding-linux-x64-musl': 1.85.0 + '@oxlint/binding-openharmony-arm64': 1.85.0 + '@oxlint/binding-win32-arm64-msvc': 1.85.0 + '@oxlint/binding-win32-ia32-msvc': 1.85.0 + '@oxlint/binding-win32-x64-msvc': 1.85.0 + oxlint-tsgolint: 7.0.2003 p-filter@2.1.0: dependencies: @@ -13080,25 +6673,14 @@ snapshots: dependencies: p-try: 2.2.0 - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - p-map@2.1.0: {} p-try@2.2.0: {} - package-json-from-dist@1.0.1: - optional: true - package-manager-detector@0.2.11: dependencies: quansync: 0.2.11 @@ -13114,51 +6696,33 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - parseurl@1.3.3: - optional: true - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} path-parse@1.0.7: {} - path-scurry@2.0.2: - dependencies: - lru-cache: 11.2.6 - minipass: 7.1.3 - - path-to-regexp@8.3.0: {} - path-type@4.0.0: {} pathe@2.0.3: {} - peek-stream@1.1.3: - dependencies: - buffer-from: 1.1.2 - duplexify: 3.7.1 - through2: 2.0.5 - - pend@1.2.0: {} + perfect-debounce@2.1.0: {} - perfect-debounce@1.0.0: {} - - pg-cloudflare@1.3.0: + pg-cloudflare@1.4.0: optional: true - pg-connection-string@2.11.0: {} + pg-connection-string@2.14.0: {} pg-int8@1.0.1: {} - pg-pool@3.11.0(pg@8.18.0): + pg-pool@3.14.0(pg@8.23.0): dependencies: - pg: 8.18.0 + pg: 8.23.0 pg-protocol@1.11.0: {} + pg-protocol@1.16.0: {} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 @@ -13167,15 +6731,15 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg@8.18.0: + pg@8.23.0: dependencies: - pg-connection-string: 2.11.0 - pg-pool: 3.11.0(pg@8.18.0) - pg-protocol: 1.11.0 + pg-connection-string: 2.14.0 + pg-pool: 3.14.0(pg@8.23.0) + pg-protocol: 1.16.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.3.0 + pg-cloudflare: 1.4.0 pgpass@1.0.5: dependencies: @@ -13185,52 +6749,16 @@ snapshots: picomatch@2.3.1: {} - picomatch@4.0.2: {} - picomatch@4.0.3: {} pify@4.0.1: {} - pino-abstract-transport@3.0.0: - dependencies: - split2: 4.2.0 - - pino-std-serializers@7.1.0: {} - - pino@10.3.1: - dependencies: - '@pinojs/redact': 0.4.0 - atomic-sleep: 1.0.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pino-std-serializers: 7.1.0 - process-warning: 5.0.0 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.1 - thread-stream: 4.0.0 - - pirates@4.0.7: {} - - piscina@4.9.2: - optionalDependencies: - '@napi-rs/nice': 1.1.1 - - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - pkg-types@2.3.0: dependencies: confbox: 0.2.4 exsolve: 1.0.8 pathe: 2.0.3 - pluralize@8.0.0: {} - - possible-typed-array-names@1.1.0: {} - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -13251,122 +6779,42 @@ snapshots: postgres@3.4.7: {} - prelude-ls@1.2.1: {} - - prettier-linter-helpers@1.0.1: - dependencies: - fast-diff: 1.3.0 - prettier@2.8.8: {} - prettier@3.8.1: {} - - pretty-format@29.7.0: + prisma@7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2): dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - - pretty-format@30.2.0: - dependencies: - '@jest/schemas': 30.0.5 - ansi-styles: 5.2.0 - react-is: 18.3.1 - - prisma@7.4.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3): - dependencies: - '@prisma/config': 7.4.1 - '@prisma/dev': 0.20.0(typescript@5.9.3) - '@prisma/engines': 7.4.1 - '@prisma/studio-core': 0.13.1(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@prisma/config': 7.10.0 + '@prisma/dev': 0.24.17(typescript@7.0.2) + '@prisma/engines': 7.10.0 + '@prisma/studio-core': 0.33.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) mysql2: 3.15.3 postgres: 3.4.7 optionalDependencies: - typescript: 5.9.3 + typescript: 7.0.2 transitivePeerDependencies: - '@types/react' + - '@types/react-dom' - magicast - react - react-dom - process-nextick-args@2.0.1: {} - - process-warning@4.0.1: {} - - process-warning@5.0.0: {} - - process@0.11.10: {} - - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 retry: 0.12.0 signal-exit: 3.0.7 - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - optional: true - proxy-compare@3.0.1: {} - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - pumpify@2.0.1: - dependencies: - duplexify: 4.1.3 - inherits: 2.0.4 - pump: 3.0.3 - - punycode@2.3.1: {} - pure-rand@6.1.0: {} - qs@6.15.0: - dependencies: - side-channel: 1.1.0 - - qs@6.15.3: - dependencies: - es-define-property: 1.0.1 - side-channel: 1.1.1 - optional: true - quansync@0.2.11: {} queue-microtask@1.2.3: {} - quick-format-unescaped@4.0.4: {} - - quick-lru@5.1.1: {} - - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - - range-parser@1.3.0: - optional: true - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.3 - unpipe: 1.0.0 - optional: true - - rc9@2.1.2: + rc9@3.1.0: dependencies: - defu: 6.1.4 + defu: 6.1.7 destr: 2.0.5 react-dom@19.2.4(react@19.2.4): @@ -13374,8 +6822,6 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 - react-is@18.3.1: {} - react@19.2.4: {} read-yaml-file@1.1.0: @@ -13385,57 +6831,9 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 - 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@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - readdirp@4.1.2: {} - real-require@0.2.0: {} - - reflect-metadata@0.2.2: {} - - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - - regexp-to-ast@0.5.0: {} - - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 + readdirp@5.1.1: {} remeda@2.33.4: {} @@ -13443,52 +6841,22 @@ snapshots: require-from-string@2.0.2: {} - resolve-alpn@1.2.1: {} - - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - resolve-from@4.0.0: {} resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - - resolve.exports@2.0.3: {} - resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.6: - dependencies: - es-errors: 1.3.0 - is-core-module: 2.16.1 - node-exports-info: 1.6.0 - object-keys: 1.1.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - responselike@3.0.0: - dependencies: - lowercase-keys: 3.0.0 - - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - ret@0.5.0: {} retry@0.12.0: {} reusify@1.1.0: {} - rfdc@1.4.1: {} - robust-predicates@3.0.3: {} rollup@4.60.0: @@ -13522,217 +6890,45 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.0 fsevents: 2.3.3 - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.3.0 - transitivePeerDependencies: - - supports-color - optional: true - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - runed@0.37.1(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6): - dependencies: - dequal: 2.0.3 - esm-env: 1.2.2 - lz-string: 1.5.0 - svelte: 5.56.4(@typescript-eslint/types@8.56.0) - optionalDependencies: - '@sveltejs/kit': 2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)) - zod: 4.3.6 - - rw@1.3.3: {} - - rxjs@7.8.1: - dependencies: - tslib: 2.8.1 - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - sade@1.8.1: - dependencies: - mri: 1.2.0 - - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - - safe-buffer@5.1.2: {} - - safe-buffer@5.2.1: {} - - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - - safe-regex2@5.0.0: - dependencies: - ret: 0.5.0 - - safe-stable-stringify@2.5.0: {} - - safer-buffer@2.1.2: {} - - scheduler@0.27.0: {} - - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.14.0 - ajv-keywords: 3.5.2(ajv@6.14.0) - - schema-utils@4.3.3: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) - - secure-json-parse@4.1.0: {} - - seek-bzip@2.0.0: - dependencies: - commander: 6.2.1 - - semver-regex@4.0.5: {} - - semver-truncate@3.0.0: - dependencies: - semver: 7.7.4 - - semver@6.3.1: {} - - semver@7.7.4: {} - - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.3.0 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - optional: true - - seq-queue@0.0.5: {} - - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - optional: true - - set-cookie-parser@2.7.2: {} - - set-cookie-parser@3.0.1: {} - - set-function-length@1.2.2: + runed@0.37.1(@sveltejs/kit@2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(zod@4.3.6): dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 + dequal: 2.0.3 + esm-env: 1.2.2 + lz-string: 1.5.0 + svelte: 5.56.4(@typescript-eslint/types@8.56.0) + optionalDependencies: + '@sveltejs/kit': 2.69.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.56.0))(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3)(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) + zod: 4.3.6 - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 + rw@1.3.3: {} - set-proto@1.0.0: + sade@1.8.1: dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - - setprototypeof@1.2.0: - optional: true + mri: 1.2.0 - shebang-command@2.0.0: + safe-regex2@5.0.0: dependencies: - shebang-regex: 3.0.0 + ret: 0.5.0 - shebang-regex@3.0.0: {} + safer-buffer@2.1.2: {} - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 + scheduler@0.27.0: {} - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - optional: true + semver@7.7.4: {} - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 + seq-queue@0.0.5: {} - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 + set-cookie-parser@3.0.1: {} - side-channel@1.1.0: + shebang-command@2.0.0: dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 + shebang-regex: 3.0.0 - side-channel@1.1.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - optional: true + shebang-regex@3.0.0: {} signal-exit@3.0.7: {} @@ -13744,39 +6940,18 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 - sisteransi@1.0.5: {} - slash@3.0.0: {} - sonic-boom@4.2.1: - dependencies: - atomic-sleep: 1.0.0 - - sort-keys-length@1.0.1: - dependencies: - sort-keys: 1.1.2 - - sort-keys@1.1.2: - dependencies: - is-plain-obj: 1.1.0 - source-map-js@1.2.1: {} - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + optional: true - source-map@0.6.1: {} - - source-map@0.7.4: {} - - source-map@0.7.6: {} + source-map@0.6.1: + optional: true spawndamnit@3.0.1: dependencies: @@ -13789,131 +6964,20 @@ snapshots: sqlstring@2.3.3: {} - stable-hash-x@0.2.0: {} - - stack-trace@0.0.10: {} - - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - - statuses@2.0.2: - optional: true - std-env@3.10.0: {} - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - - stream-shift@1.0.3: {} - - streamsearch@1.1.0: - optional: true - - streamx@2.23.0: - dependencies: - events-universal: 1.0.1 - fast-fifo: 1.3.2 - text-decoder: 1.2.7 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.2 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 strip-bom@3.0.0: {} - strip-bom@4.0.0: {} - - strip-dirs@3.0.0: - dependencies: - inspect-with-kind: 1.0.5 - is-plain-obj: 1.1.0 - - strip-final-newline@2.0.0: {} - - strip-json-comments@3.1.1: {} - - strtok3@10.3.4: - dependencies: - '@tokenizer/token': 0.3.0 - - superagent@10.3.0: - dependencies: - component-emitter: 1.3.1 - cookiejar: 2.1.4 - debug: 4.4.3 - fast-safe-stringify: 2.1.1 - form-data: 4.0.5 - formidable: 3.5.4 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.15.0 - transitivePeerDependencies: - - supports-color - - supertest@7.2.2: - dependencies: - cookie-signature: 1.2.2 - methods: 1.1.2 - superagent: 10.3.0 - transitivePeerDependencies: - - supports-color - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} svelte-check@4.7.2(picomatch@4.0.3)(svelte@5.56.4(@typescript-eslint/types@8.56.0))(typescript@5.9.3): @@ -13950,79 +7014,23 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' - swagger-themes@1.4.3: {} - - swagger-ui-dist@5.31.0: - dependencies: - '@scarf/scarf': 1.4.0 - - symbol-observable@4.0.0: {} - - synckit@0.11.12: - dependencies: - '@pkgr/core': 0.2.9 + tagged-tag@1.0.0: {} tailwind-merge@3.6.0: {} tailwindcss@4.3.1: {} - tapable@2.3.0: {} - tapable@2.3.3: {} - tar-stream@3.1.7: - dependencies: - b4a: 1.8.0 - fast-fifo: 1.3.2 - streamx: 2.23.0 - transitivePeerDependencies: - - bare-abort-controller - - react-native-b4a - term-size@2.2.1: {} - terser-webpack-plugin@5.3.16(@swc/core@1.15.11(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18))): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - terser: 5.46.0 - webpack: 5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18)) - optionalDependencies: - '@swc/core': 1.15.11(@swc/helpers@0.5.18) - terser@5.46.0: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.16.0 commander: 2.20.3 source-map-support: 0.5.21 - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 - - text-decoder@1.2.7: - dependencies: - b4a: 1.8.0 - transitivePeerDependencies: - - react-native-b4a - - text-hex@1.0.0: {} - - thread-stream@4.0.0: - dependencies: - real-require: 0.2.0 - - through2@2.0.5: - dependencies: - readable-stream: 2.3.8 - xtend: 4.0.2 - - through@2.3.8: {} + optional: true tinyexec@1.0.2: {} @@ -14033,262 +7041,60 @@ snapshots: tinypool@2.1.0: {} - tmpl@1.0.5: {} + tinypool@2.1.2: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - toad-cache@3.7.0: {} - - toidentifier@1.0.1: - optional: true - - token-types@6.1.2: - dependencies: - '@borewit/text-codec': 0.2.1 - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - totalist@3.0.1: {} - triple-beam@1.4.1: {} - - ts-api-utils@2.4.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - ts-essentials@10.1.1(typescript@5.9.3): - optionalDependencies: - typescript: 5.9.3 - - ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.29.0))(jest-util@30.2.0)(jest@29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)))(typescript@5.9.3): - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 - jest: 29.7.0(@types/node@22.19.11)(ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3)) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.4 - type-fest: 4.41.0 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.2.0 - '@jest/types': 30.2.0 - babel-jest: 29.7.0(@babel/core@7.29.0) - jest-util: 30.2.0 - - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18))): - dependencies: - chalk: 4.1.2 - enhanced-resolve: 5.19.0 - micromatch: 4.0.8 - semver: 7.7.4 - source-map: 0.7.6 - typescript: 5.9.3 - webpack: 5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18)) - - ts-node@10.9.2(@swc/core@1.15.11(@swc/helpers@0.5.18))(@types/node@22.19.11)(typescript@5.9.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.19.11 - acorn: 8.16.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 5.9.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optionalDependencies: - '@swc/core': 1.15.11(@swc/helpers@0.5.18) - - tsconfig-paths-webpack-plugin@4.2.0: - dependencies: - chalk: 4.1.2 - enhanced-resolve: 5.19.0 - tapable: 2.3.0 - tsconfig-paths: 4.2.0 - - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - tslib@2.8.1: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-detect@4.0.8: {} - - type-fest@0.21.3: {} - - type-fest@4.41.0: {} - - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - optional: true - - type-is@2.1.0: - dependencies: - content-type: 2.0.0 - media-typer: 1.1.0 - mime-types: 3.0.2 - optional: true - - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.9 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.9 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: + tsx@4.23.15: dependencies: - call-bind: 1.0.9 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 - - typedarray@0.0.6: - optional: true + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 - typescript-eslint@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3): + type-fest@5.10.0: dependencies: - '@typescript-eslint/eslint-plugin': 8.56.0(@typescript-eslint/parser@8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/parser': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.56.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.0(eslint@9.39.3(jiti@2.7.0))(typescript@5.9.3) - eslint: 9.39.3(jiti@2.7.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color + tagged-tag: 1.0.0 typescript@5.9.3: {} - uglify-js@3.19.3: - optional: true - - uid@2.0.2: - dependencies: - '@lukeed/csprng': 1.1.0 - - uint8array-extras@1.5.0: {} - - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 - - undici-types@6.21.0: {} - - universalify@0.1.2: {} - - universalify@2.0.1: {} - - unpipe@1.0.0: - optional: true - - unrs-resolver@1.11.1: - dependencies: - napi-postinstall: 0.3.4 + typescript@7.0.2: optionalDependencies: - '@unrs/resolver-binding-android-arm-eabi': 1.11.1 - '@unrs/resolver-binding-android-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-x64': 1.11.1 - '@unrs/resolver-binding-freebsd-x64': 1.11.1 - '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 - '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 - '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-musl': 1.11.1 - '@unrs/resolver-binding-wasm32-wasi': 1.11.1 - '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 - '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 - '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 - util-deprecate@1.0.2: {} + undici-types@6.21.0: {} - v8-compile-cache-lib@3.0.1: {} + undici-types@8.9.0: {} - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 + universalify@0.1.2: {} - valibot@1.2.0(typescript@5.9.3): + valibot@1.4.2(typescript@7.0.2): optionalDependencies: - typescript: 5.9.3 - - validator@13.15.35: - optional: true - - vary@1.1.2: - optional: true + typescript: 7.0.2 virtua@0.49.2(patch_hash=2c0d482dc06ad381cab34a6d5581eefb4327fc24a8dd80dad7562bd8922f879c)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(svelte@5.56.4(@typescript-eslint/types@8.56.0)): optionalDependencies: @@ -14296,7 +7102,7 @@ snapshots: react-dom: 19.2.4(react@19.2.4) svelte: 5.56.4(@typescript-eslint/types@8.56.0) - vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0): + vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -14310,160 +7116,27 @@ snapshots: jiti: 2.7.0 lightningcss: 1.32.0 terser: 5.46.0 + tsx: 4.23.15 yaml: 2.9.0 - vitefu@1.1.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0)): + vitefu@1.1.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)): optionalDependencies: - vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(yaml@2.9.0) - - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - - watchpack@2.5.1: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - - webpack-node-externals@3.0.0: {} - - webpack-sources@3.3.4: {} - - webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18)): - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.16.0 - acorn-import-phases: 1.0.4(acorn@8.16.0) - browserslist: 4.28.1 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.19.0 - es-module-lexer: 2.0.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.1 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 4.3.3 - tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.15.11(@swc/helpers@0.5.18))(webpack@5.104.1(@swc/core@1.15.11(@swc/helpers@0.5.18))) - watchpack: 2.5.1 - webpack-sources: 3.3.4 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.2 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.20 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.20: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.9 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 + vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) which@2.0.2: dependencies: isexe: 2.0.0 - winston-transport@4.9.0: - dependencies: - logform: 2.7.0 - readable-stream: 3.6.2 - triple-beam: 1.4.1 - - winston@3.19.0: - dependencies: - '@colors/colors': 1.6.0 - '@dabh/diagnostics': 2.0.8 - async: 3.2.6 - is-stream: 2.0.1 - logform: 2.7.0 - one-time: 1.0.0 - readable-stream: 3.6.2 - safe-stable-stringify: 2.5.0 - stack-trace: 0.0.10 - triple-beam: 1.4.1 - winston-transport: 4.9.0 - - word-wrap@1.2.5: {} - - wordwrap@1.0.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - wrappy@1.0.2: {} - - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - xtend@4.0.2: {} y18n@5.0.8: {} - yallist@3.1.1: {} - yaml@2.9.0: optional: true @@ -14479,17 +7152,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yauzl@3.2.0: - dependencies: - buffer-crc32: 0.2.13 - pend: 1.2.0 - - yn@3.1.1: {} - - yocto-queue@0.1.0: {} - - yoctocolors-cjs@2.1.3: {} - zeptomatch@2.1.0: dependencies: grammex: 3.1.12 @@ -14497,4 +7159,7 @@ snapshots: zimmerframe@1.1.4: {} - zod@4.3.6: {} + zod@4.3.6: + optional: true + + zod@4.6.5: {} From 135527d468659d1d2bcbb97ff9a543dc7137ccba Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:20:43 +0000 Subject: [PATCH 02/34] refactor: add `GET /clusters/:id/logs` --- .../src/controllers/cluster.controller.ts | 60 ++++++++++++++++--- .../backend/src/middlewares/auth.middlware.ts | 2 +- .../backend/src/schemas/cluster.schema.ts | 5 +- .../backend/src/services/cluster.service.ts | 36 ++++++++--- .../backend/src/services/docker.service.ts | 33 ++++++++-- 5 files changed, 112 insertions(+), 24 deletions(-) diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index 0c0c887..efab256 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -33,7 +33,7 @@ const clusterController = new Hono() }, }, 404: { - description: "No cluster found", + description: "Not Found", content: { "application/json": { example: "Clusters not found" }, }, @@ -147,7 +147,7 @@ const clusterController = new Hono() }, }, 404: { - description: "Not found", + description: "Not Found", content: { "text/plain": { example: "Cluster not found" }, }, @@ -176,7 +176,10 @@ const clusterController = new Hono() }, }, 404: { - description: "Cluster not found", + description: "Not Found", + content: { + "text/plain": { example: "Cluster not found" }, + }, }, }, }), @@ -201,7 +204,18 @@ const clusterController = new Hono() }, }, 404: { - description: "Cluster not found", + description: "Not Found", + content: { + "text/plain": { example: "Cluster not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, }, }, }), @@ -226,12 +240,31 @@ const clusterController = new Hono() }, }, 404: { - description: "Cluster not found", + description: "Not Found", + content: { + "text/plain": { example: "Cluster not found" }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, }, }, }), validator("param", ClusterIdParamSchema), validator("query", ClusterLogsQuerySchema), + async (c) => { + const param = c.req.valid("param"); + const query = c.req.valid("query"); + + const logs = await ClusterService.logs(param.id, query); + + return c.json(logs, 200); + }, ) .get( "/:id/logs/sse", @@ -246,7 +279,10 @@ const clusterController = new Hono() }, }, 404: { - description: "Cluster not found", + description: "Not Found", + content: { + "text/plain": { example: "Cluster not found" }, + }, }, }, }), @@ -265,7 +301,10 @@ const clusterController = new Hono() }, }, 404: { - description: "Cluster not found", + description: "Not Found", + content: { + "text/plain": { example: "Cluster not found" }, + }, }, }, }), @@ -284,7 +323,10 @@ const clusterController = new Hono() }, }, 404: { - description: "Cluster not found", + description: "Not Found", + content: { + "text/plain": { example: "Cluster not found" }, + }, }, }, }), @@ -304,7 +346,7 @@ const clusterController = new Hono() }, }, 404: { - description: "Not found", + description: "Not Found", content: { "text/plain": { example: "Cluster not found" }, }, diff --git a/packages/backend/src/middlewares/auth.middlware.ts b/packages/backend/src/middlewares/auth.middlware.ts index c91838b..fac9fb0 100644 --- a/packages/backend/src/middlewares/auth.middlware.ts +++ b/packages/backend/src/middlewares/auth.middlware.ts @@ -1,6 +1,6 @@ +import env from "#lib/env"; import { describeRoute } from "hono-openapi"; import { jwt } from "hono/jwt"; -import env from "../lib/env"; const authMiddlewares = [ describeRoute({ diff --git a/packages/backend/src/schemas/cluster.schema.ts b/packages/backend/src/schemas/cluster.schema.ts index a035bf4..7191c81 100644 --- a/packages/backend/src/schemas/cluster.schema.ts +++ b/packages/backend/src/schemas/cluster.schema.ts @@ -83,16 +83,17 @@ export const ClusterIdParamSchema = z.object({ }); export const ClusterLogsQuerySchema = z.object({ - since: z.iso.datetime().optional().meta({ + since: z.coerce.date().optional().meta({ description: "At which point in time does the logs collection start", }), - until: z.iso.datetime().optional().meta({ + until: z.coerce.date().optional().meta({ description: "At which point in time does the logs collection end", }), tail: z.coerce.number().positive().min(1).or(z.literal("all")).optional().default("all").meta({ description: "How many logs to fetch from latest to oldest ('all' gets all the logs)", }), }); +export type ClusterLogsQuery = z.infer; export const ClusterLogSchema = z.object({ content: z.string().meta({ diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index a9b2271..b02d839 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -1,5 +1,5 @@ import type { Bot, ContainerImage } from "#prisma/client"; -import type { Cluster, ClusterId, UpdateClusters } from "#schemas/cluster.schema"; +import type { Cluster, ClusterId, ClusterLogsQuery, UpdateClusters } from "#schemas/cluster.schema"; import { BadRequestException, NotFoundException } from "../exceptions/http-exceptions"; import BotRepository from "../repositories/bot.repository"; import ClusterRepository from "../repositories/cluster.repository"; @@ -46,6 +46,21 @@ export default class ClusterService { return cluster; } + public static async logs(id: ClusterId, options: ClusterLogsQuery) { + const cluster = await ClusterRepository.findFirstContainerIdById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return DockerService.containerLogs(cluster.containerId, { + tail: options.tail, + since: options.since?.getTime(), + until: options.until?.getTime(), + stdout: true, + stderr: true, + timestamps: true, + follow: false, + }); + } + public static async start(id: ClusterId) { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -53,7 +68,7 @@ export default class ClusterService { if ((await ClusterRepository.updateStatusById("STARTING", id)) === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await DockerService.start(cluster.containerId).catch(async (error) => { + await DockerService.startContainer(cluster.containerId).catch(async (error) => { if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -70,7 +85,7 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await DockerService.stop(cluster.containerId).catch(async (error) => { + await DockerService.stopContainer(cluster.containerId).catch(async (error) => { if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -90,7 +105,7 @@ export default class ClusterService { if ((await ClusterRepository.updateStatusById("STARTING", id)) === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await DockerService.restart(cluster.containerId).catch(async (error) => { + await DockerService.restartContainer(cluster.containerId).catch(async (error) => { if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -107,7 +122,7 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await DockerService.remove(cluster.containerId); + await DockerService.removeContainer(cluster.containerId); return ClusterRepository.deleteById(id); } @@ -123,16 +138,21 @@ export default class ClusterService { const clusters = await ClusterRepository.findManyIdAndContainerId(); if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); - await Promise.all(clusters.map(({ containerId }) => DockerService.stop(containerId))); + await Promise.all(clusters.map(({ containerId }) => DockerService.stopContainer(containerId))); - return ClusterRepository.updateManyStatusById("STOPPED", clusters.map(({ id }) => id)); + return ClusterRepository.updateManyStatusById( + "STOPPED", + clusters.map(({ id }) => id), + ); } public static async removeAll() { const clusters = await ClusterRepository.findManyIdAndContainerId(); if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); - await Promise.all(clusters.map(({ containerId }) => DockerService.remove(containerId))); + await Promise.all( + clusters.map(({ containerId }) => DockerService.removeContainer(containerId)), + ); return ClusterRepository.deleteManyById(clusters.map(({ id }) => id)); } diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index a1954b7..742c8c4 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -48,7 +48,9 @@ export default class DockerService { }); } - public static async start(...options: MethodParameters) { + public static async startContainer( + ...options: MethodParameters + ) { return this.containers.start(...options).catch((error) => { if (!(error instanceof DockerAPIHttpError)) throw new FailedDependancyException({ @@ -65,7 +67,9 @@ export default class DockerService { }); } - public static async stop(...options: MethodParameters) { + public static async stopContainer( + ...options: MethodParameters + ) { return this.containers.stop(...options).catch((error) => { if (!(error instanceof DockerAPIHttpError)) throw new FailedDependancyException({ @@ -82,7 +86,9 @@ export default class DockerService { }); } - public static async restart(...options: MethodParameters) { + public static async restartContainer( + ...options: MethodParameters + ) { return this.containers.restart(...options).catch((error) => { if (!(error instanceof DockerAPIHttpError)) throw new FailedDependancyException({ @@ -97,7 +103,9 @@ export default class DockerService { }); } - public static async remove(...options: MethodParameters) { + public static async removeContainer( + ...options: MethodParameters + ) { return this.containers.remove(...options).catch((error) => { if (!(error instanceof DockerAPIHttpError)) throw new FailedDependancyException({ @@ -113,4 +121,21 @@ export default class DockerService { }); }); } + + public static async containerLogs( + ...options: MethodParameters + ) { + return this.containers.logs(...options).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } } From 4f5046e7db11393c7eafa7c9a628065b7d1af0cf Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:33:14 +0000 Subject: [PATCH 03/34] refactor(auth): add JWT expiration --- packages/backend/src/services/auth.service.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/services/auth.service.ts b/packages/backend/src/services/auth.service.ts index fa5788f..662398d 100644 --- a/packages/backend/src/services/auth.service.ts +++ b/packages/backend/src/services/auth.service.ts @@ -7,13 +7,23 @@ import env from "../lib/env.js"; import UserRepository from "../repositories/user.repository.js"; export class AuthService { + private static async sign(sub: string) { + return await sign( + { + sub, + exp: Math.floor(Date.now() / 1000) + 3600 * 12, // 12 hours + }, + env.SECRET, + ); + } + public static async register({ username, password }: User): Promise { const userCount = await prisma.user.count(); if (userCount !== 0) throw new ConflictException({ message: "A user is already registered" }); const user = await UserRepository.create({ username, passwordHash: await hash(password) }); - return { token: await sign({ sub: user.username }, env.SECRET) }; + return { token: await this.sign(user.username) }; } public static async login({ username, password }: User): Promise { @@ -22,8 +32,6 @@ export class AuthService { if (user === null || !(await verify(user.passwordHash, password))) throw new UnauthorizedException({ message: "Invalid username or password" }); - return { - token: await sign({ sub: user.username }, env.SECRET), - }; + return { token: await this.sign(user.username) }; } } From 5a875ad7afdecd0ec42aea3626947662bf3c168c Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Wed, 7 Oct 2026 20:45:19 +0000 Subject: [PATCH 04/34] style: organize and sort imports --- packages/backend/.oxfmtrc.json | 3 ++- packages/backend/src/controllers/auth.controller.ts | 5 +++-- packages/backend/src/controllers/bot.controller.ts | 7 ++++--- .../backend/src/controllers/cluster.controller.ts | 7 ++++--- packages/backend/src/index.ts | 1 + packages/backend/src/lib/database.ts | 1 + packages/backend/src/middlewares/auth.middlware.ts | 3 ++- .../src/repositories/container-image.repository.ts | 4 +++- packages/backend/src/schemas/bot.schema.ts | 1 + packages/backend/src/schemas/discord.schema.ts | 1 + packages/backend/src/services/auth.service.ts | 11 ++++++----- packages/backend/src/services/bot.service.ts | 12 +++++++----- packages/backend/src/services/cluster.service.ts | 9 +++++---- packages/backend/src/services/discord.service.ts | 5 +++-- packages/backend/src/services/docker.service.ts | 3 ++- packages/backend/tsconfig.json | 11 ++++++----- 16 files changed, 51 insertions(+), 33 deletions(-) diff --git a/packages/backend/.oxfmtrc.json b/packages/backend/.oxfmtrc.json index 55c15df..98338d1 100644 --- a/packages/backend/.oxfmtrc.json +++ b/packages/backend/.oxfmtrc.json @@ -1,4 +1,5 @@ { "$schema": "./node_modules/oxfmt/configuration_schema.json", - "ignorePatterns": [] + "ignorePatterns": [], + "sortImports": true } diff --git a/packages/backend/src/controllers/auth.controller.ts b/packages/backend/src/controllers/auth.controller.ts index fb730fc..9509ea3 100644 --- a/packages/backend/src/controllers/auth.controller.ts +++ b/packages/backend/src/controllers/auth.controller.ts @@ -1,8 +1,9 @@ -import { UserSchema, UserTokenResponseSchema } from "#schemas/auth.schema.js"; -import { AuthService } from "#services/auth.service"; import { Hono } from "hono"; import { describeRoute, resolver, validator } from "hono-openapi"; +import { UserSchema, UserTokenResponseSchema } from "#schemas/auth.schema"; +import { AuthService } from "#services/auth.service"; + const tags = ["Authentication"]; const authController = new Hono() diff --git a/packages/backend/src/controllers/bot.controller.ts b/packages/backend/src/controllers/bot.controller.ts index bc2da9c..2f74191 100644 --- a/packages/backend/src/controllers/bot.controller.ts +++ b/packages/backend/src/controllers/bot.controller.ts @@ -1,9 +1,10 @@ +import { Hono } from "hono"; +import { describeRoute, resolver, validator } from "hono-openapi"; + +import authMiddlewares from "#middlewares/auth.middlware"; import { BotSchema, ContainerImageSchema, CreateBotSchema } from "#schemas/bot.schema"; import { DiscordUserSchema } from "#schemas/discord.schema"; import BotService from "#services/bot.service"; -import { Hono } from "hono"; -import { describeRoute, resolver, validator } from "hono-openapi"; -import authMiddlewares from "../middlewares/auth.middlware"; const tags = ["Bot"]; diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index efab256..0183b3c 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -1,3 +1,7 @@ +import { Hono } from "hono"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { streamSSE } from "hono/streaming"; + import authMiddlewares from "#middlewares/auth.middlware"; import { ClusterIdParamSchema, @@ -12,9 +16,6 @@ import { UpdateClustersSchema, } from "#schemas/cluster.schema"; import ClusterService from "#services/cluster.service"; -import { Hono } from "hono"; -import { describeRoute, resolver, validator } from "hono-openapi"; -import { streamSSE } from "hono/streaming"; const tags = ["Clusters"]; diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 1ec23fb..cb94298 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -1,4 +1,5 @@ import { serve } from "@hono/node-server"; + import app from "./app"; serve( diff --git a/packages/backend/src/lib/database.ts b/packages/backend/src/lib/database.ts index a642319..5662a14 100644 --- a/packages/backend/src/lib/database.ts +++ b/packages/backend/src/lib/database.ts @@ -1,5 +1,6 @@ import "dotenv/config"; import { PrismaPg } from "@prisma/adapter-pg"; + import { PrismaClient } from "#prisma/client"; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL }); diff --git a/packages/backend/src/middlewares/auth.middlware.ts b/packages/backend/src/middlewares/auth.middlware.ts index fac9fb0..2c8b24a 100644 --- a/packages/backend/src/middlewares/auth.middlware.ts +++ b/packages/backend/src/middlewares/auth.middlware.ts @@ -1,7 +1,8 @@ -import env from "#lib/env"; import { describeRoute } from "hono-openapi"; import { jwt } from "hono/jwt"; +import env from "#lib/env"; + const authMiddlewares = [ describeRoute({ responses: { diff --git a/packages/backend/src/repositories/container-image.repository.ts b/packages/backend/src/repositories/container-image.repository.ts index 10d2b4c..b18b8a2 100644 --- a/packages/backend/src/repositories/container-image.repository.ts +++ b/packages/backend/src/repositories/container-image.repository.ts @@ -1,6 +1,8 @@ +import { HTTPException } from "hono/http-exception"; + import prisma from "#lib/database"; import type { ContainerImage } from "#prisma/client"; -import { HTTPException } from "hono/http-exception"; + import BotRepository from "./bot.repository"; export default class ContainerImageRepository { diff --git a/packages/backend/src/schemas/bot.schema.ts b/packages/backend/src/schemas/bot.schema.ts index e5e264d..b9f321c 100644 --- a/packages/backend/src/schemas/bot.schema.ts +++ b/packages/backend/src/schemas/bot.schema.ts @@ -1,4 +1,5 @@ import z from "zod"; + import { ClusterSchema } from "./cluster.schema"; export const ContainerImageSchema = z.object({ diff --git a/packages/backend/src/schemas/discord.schema.ts b/packages/backend/src/schemas/discord.schema.ts index 5cdd40c..2eaf3c2 100644 --- a/packages/backend/src/schemas/discord.schema.ts +++ b/packages/backend/src/schemas/discord.schema.ts @@ -1,4 +1,5 @@ import z from "zod"; + import { BotSchema } from "./bot.schema"; export const DiscordGatewayBotSchema = BotSchema.pick({ shards: true }); diff --git a/packages/backend/src/services/auth.service.ts b/packages/backend/src/services/auth.service.ts index 662398d..e2f6f27 100644 --- a/packages/backend/src/services/auth.service.ts +++ b/packages/backend/src/services/auth.service.ts @@ -1,10 +1,11 @@ -import prisma from "#lib/database.js"; -import type { User, UserTokenResponse } from "#schemas/auth.schema.js"; import { hash, verify } from "argon2"; import { sign } from "hono/jwt"; -import { ConflictException, UnauthorizedException } from "../exceptions/http-exceptions.js"; -import env from "../lib/env.js"; -import UserRepository from "../repositories/user.repository.js"; + +import { ConflictException, UnauthorizedException } from "#exceptions/http-exceptions"; +import prisma from "#lib/database"; +import env from "#lib/env"; +import UserRepository from "#repositories/user.repository"; +import type { User, UserTokenResponse } from "#schemas/auth.schema"; export class AuthService { private static async sign(sub: string) { diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts index bb1d9bb..79c829e 100644 --- a/packages/backend/src/services/bot.service.ts +++ b/packages/backend/src/services/bot.service.ts @@ -1,11 +1,13 @@ -import type { Bot, ContainerImage, CreateBot } from "#schemas/bot.schema"; import { HTTPException } from "hono/http-exception"; -import { NotFoundException } from "../exceptions/http-exceptions"; -import BotRepository from "../repositories/bot.repository"; -import ContainerImageRepository from "../repositories/container-image.repository"; + +import { NotFoundException } from "#exceptions/http-exceptions"; +import BotRepository from "#repositories/bot.repository"; +import ContainerImageRepository from "#repositories/container-image.repository"; +import type { Bot, ContainerImage, CreateBot } from "#schemas/bot.schema"; + +import ClusterService from "./cluster.service"; import DiscordService from "./discord.service"; import DockerService from "./docker.service"; -import ClusterService from "./cluster.service"; export default class BotService { // FIX this doesn't work for local images without a host (example: "hallmaster-bot:latest") diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index b02d839..e662c8c 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -1,9 +1,10 @@ +import { BadRequestException, NotFoundException } from "#exceptions/http-exceptions"; import type { Bot, ContainerImage } from "#prisma/client"; +import BotRepository from "#repositories/bot.repository"; +import ClusterRepository from "#repositories/cluster.repository"; +import ContainerImageRepository from "#repositories/container-image.repository"; import type { Cluster, ClusterId, ClusterLogsQuery, UpdateClusters } from "#schemas/cluster.schema"; -import { BadRequestException, NotFoundException } from "../exceptions/http-exceptions"; -import BotRepository from "../repositories/bot.repository"; -import ClusterRepository from "../repositories/cluster.repository"; -import ContainerImageRepository from "../repositories/container-image.repository"; + import DockerService from "./docker.service"; export default class ClusterService { diff --git a/packages/backend/src/services/discord.service.ts b/packages/backend/src/services/discord.service.ts index 0f8657d..ae3aa27 100644 --- a/packages/backend/src/services/discord.service.ts +++ b/packages/backend/src/services/discord.service.ts @@ -1,11 +1,12 @@ +import { HTTPException } from "hono/http-exception"; +import type z from "zod"; + import type { BotDiscordProfile } from "#schemas/bot.schema"; import { DiscordGatewayBotSchema, DiscordUserSchema, type DiscordGatewayBot, } from "#schemas/discord.schema"; -import { HTTPException } from "hono/http-exception"; -import type z from "zod"; export default class DiscordService { private static readonly DISCORD_API_BASE_URL = "https://discord.com/api/v10"; diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index 742c8c4..356d6fb 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -4,7 +4,8 @@ import { DockerImagesAPI, DockerSocket, } from "@hallmaster/docker.js"; -import { FailedDependancyException } from "../exceptions/http-exceptions"; + +import { FailedDependancyException } from "#exceptions/http-exceptions"; type MethodParameters< C extends abstract new (...args: any[]) => any, diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 456471e..36402b1 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -15,13 +15,14 @@ "rootDir": ".", "types": ["node"], "paths": { - "#prisma/client": ["./prisma/generated/prisma/client"], "#controllers/*": ["./src/controllers/*"], - "#middlewares/*": ["./src/middlewares/*"], - "#database/*": ["./src/database/*"], + "#exceptions/*": ["./src/exceptions/*"], "#lib/*": ["./src/lib/*"], - "#services/*": ["./src/services/*"], - "#schemas/*": ["./src/schemas/*"] + "#middlewares/*": ["./src/middlewares/*"], + "#prisma/client": ["./prisma/generated/prisma/client"], + "#repositories/*": ["./src/repositories/*"], + "#schemas/*": ["./src/schemas/*"], + "#services/*": ["./src/services/*"] } } } From 74190162e09823b7ba8bf119603a56d54ff13fe1 Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:31:44 +0000 Subject: [PATCH 05/34] refactor: exceptions --- .../container-image.repository.ts | 5 ++--- packages/backend/src/services/bot.service.ts | 6 ++---- .../backend/src/services/cluster.service.ts | 19 +++++++++++++++++-- .../backend/src/services/discord.service.ts | 10 +++++----- .../backend/src/services/docker.service.ts | 18 ++++++++++++++++++ packages/backend/tests/.gitkeep | 0 6 files changed, 44 insertions(+), 14 deletions(-) delete mode 100644 packages/backend/tests/.gitkeep diff --git a/packages/backend/src/repositories/container-image.repository.ts b/packages/backend/src/repositories/container-image.repository.ts index b18b8a2..89c2418 100644 --- a/packages/backend/src/repositories/container-image.repository.ts +++ b/packages/backend/src/repositories/container-image.repository.ts @@ -1,5 +1,4 @@ -import { HTTPException } from "hono/http-exception"; - +import { ConflictException } from "#exceptions/http-exceptions"; import prisma from "#lib/database"; import type { ContainerImage } from "#prisma/client"; @@ -25,7 +24,7 @@ export default class ContainerImageRepository { if (bot === null) return null; if (await this.count()) - throw new HTTPException(409, { message: "A container image is already registered" }); + throw new ConflictException({ message: "A container image is already registered" }); return prisma.containerImage.create({ data: { diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts index 79c829e..bf270e9 100644 --- a/packages/backend/src/services/bot.service.ts +++ b/packages/backend/src/services/bot.service.ts @@ -1,6 +1,4 @@ -import { HTTPException } from "hono/http-exception"; - -import { NotFoundException } from "#exceptions/http-exceptions"; +import { ConflictException, NotFoundException } from "#exceptions/http-exceptions"; import BotRepository from "#repositories/bot.repository"; import ContainerImageRepository from "#repositories/container-image.repository"; import type { Bot, ContainerImage, CreateBot } from "#schemas/bot.schema"; @@ -22,7 +20,7 @@ export default class BotService { await DiscordService.getGatewayBot(token); const botCount = await BotRepository.count(); - if (botCount !== 0) throw new HTTPException(409, { message: "A bot is already registered" }); + if (botCount !== 0) throw new ConflictException({ message: "A bot is already registered" }); const { id, totalShards } = await BotRepository.create({ id: Buffer.from(token.split(".")[0], "base64").toString("ascii"), diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index e662c8c..81aae37 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -62,6 +62,18 @@ export default class ClusterService { }); } + public static async streamLogs(id: ClusterId) { + const cluster = await ClusterRepository.findFirstContainerIdById(id); + if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return DockerService.containerLogs(cluster.containerId, { + tail: 0, + stdout: true, + stderr: true, + timestamps: true, + }); + } + public static async start(id: ClusterId) { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -149,7 +161,7 @@ export default class ClusterService { public static async removeAll() { const clusters = await ClusterRepository.findManyIdAndContainerId(); - if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); + if (clusters.length === 0) return; await Promise.all( clusters.map(({ containerId }) => DockerService.removeContainer(containerId)), @@ -204,7 +216,10 @@ export default class ClusterService { if (containerImage === null) throw new NotFoundException({ message: "Container image not found" }); - await this.removeAll(); + if (previous.length !== 0) { + await this.stopAll(); + await this.removeAll(); + } const previousStatuses = new Map(previous.map(({ id, status }) => [id, status])); const updated = await Promise.all( diff --git a/packages/backend/src/services/discord.service.ts b/packages/backend/src/services/discord.service.ts index ae3aa27..0251ecc 100644 --- a/packages/backend/src/services/discord.service.ts +++ b/packages/backend/src/services/discord.service.ts @@ -1,6 +1,6 @@ -import { HTTPException } from "hono/http-exception"; import type z from "zod"; +import { FailedDependancyException } from "#exceptions/http-exceptions"; import type { BotDiscordProfile } from "#schemas/bot.schema"; import { DiscordGatewayBotSchema, @@ -21,14 +21,14 @@ export default class DiscordService { headers: { Authorization: `Bot ${token}` }, signal: AbortSignal.timeout(10_000), }).catch(() => { - throw new HTTPException(424, { message: "Discord API is unreachable or timed out" }); + throw new FailedDependancyException({ message: "Discord API is unreachable or timed out" }); }); if (response.status === 401) - throw new HTTPException(424, { message: "Invalid Discord bot token" }); + throw new FailedDependancyException({ message: "Invalid Discord bot token" }); if (!response.ok) { - throw new HTTPException(424, { + throw new FailedDependancyException({ message: `Discord API returned a ${response.status} ${response.statusText}`, }); } @@ -36,7 +36,7 @@ export default class DiscordService { try { return schema.parse(await response.json()); } catch (error) { - throw new HTTPException(424, { + throw new FailedDependancyException({ message: "Discord API returned an unexpected response body", cause: error, }); diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index 356d6fb..3618ff2 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -139,4 +139,22 @@ export default class DockerService { }); }); } + + public static async streamContainerLogs( + containerId: string, + options: Omit[1]>, "follow">, + ) { + return this.containers.logs(containerId, { ...options, follow: true }).catch((error) => { + if (!(error instanceof DockerAPIHttpError)) + throw new FailedDependancyException({ + message: "Docker Engine API returned an unexpected response", + cause: error, + }); + + throw new FailedDependancyException({ + message: `Docker Engine API returned a ${error.status} ${error.message}`, + cause: error, + }); + }); + } } diff --git a/packages/backend/tests/.gitkeep b/packages/backend/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 From 95bf3fed54af98c959f4e303f250af9082fdf5b7 Mon Sep 17 00:00:00 2001 From: zowks <67444066+zowks@users.noreply.github.com> Date: Sun, 11 Oct 2026 15:33:35 +0000 Subject: [PATCH 06/34] refactor: missing `await` --- .../backend/src/controllers/cluster.controller.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index 0183b3c..a4f0141 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -109,9 +109,11 @@ const clusterController = new Hono() return streamSSE(c, async (stream) => { while (!stream.aborted) { const clusters = await ClusterService.getAll(); + await stream.writeSSE({ data: JSON.stringify(clusters), }); + await stream.sleep(query.interval * 1000); } }); @@ -187,7 +189,7 @@ const clusterController = new Hono() validator("param", ClusterIdParamSchema), async (c) => { const param = c.req.valid("param"); - const cluster = ClusterService.get(param.id); + const cluster = await ClusterService.get(param.id); return c.json(cluster, 200); }, @@ -288,6 +290,11 @@ const clusterController = new Hono() }, }), validator("param", ClusterIdParamSchema), + async (c) => { + const param = c.req.valid("param"); + + await ClusterService.streamLogs(param.id); + }, ) .get( "/:id/stats", @@ -340,7 +347,7 @@ const clusterController = new Hono() tags, description: "Start, stop or restart a cluster", responses: { - 204: { + 200: { description: "OK", content: { "application/json": { schema: resolver(ClusterSchema) }, From 7c31097dee831045a4c443e3e96c057c3fca3e04 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:13:43 +0200 Subject: [PATCH 07/34] fix(bot): await bot lookup before updating its token --- packages/backend/src/services/bot.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts index bf270e9..0df430f 100644 --- a/packages/backend/src/services/bot.service.ts +++ b/packages/backend/src/services/bot.service.ts @@ -34,7 +34,7 @@ export default class BotService { } public static async update({ token }: CreateBot): Promise { - const bot = BotRepository.findFirst(); + const bot = await BotRepository.findFirstId(); if (bot === null) throw new NotFoundException({ message: "Bot not found" }); await DiscordService.getGatewayBot(token); From 26437f72676a7082f34212bfc5c355b8837dfa5f Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:14:02 +0200 Subject: [PATCH 08/34] fix(clusters): keep each cluster status when updating the layout --- packages/backend/src/services/cluster.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 81aae37..70eab84 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -204,12 +204,12 @@ export default class ClusterService { } } - let id = 0; + let nextId = 0; for (const cluster of clusters) { if (cluster.id !== undefined) continue; - while (ids.has(id)) id++; - cluster.id = id++; + while (ids.has(nextId)) nextId++; + cluster.id = nextId++; } const containerImage = await ContainerImageRepository.findFirst(); @@ -228,7 +228,7 @@ export default class ClusterService { { id: cluster.id!, shardIds: cluster.shardIds, - status: previousStatuses.get(id!) === "STOPPED" ? "STOPPED" : "UPDATING", + status: previousStatuses.get(cluster.id!) === "STOPPED" ? "STOPPED" : "UPDATING", }, { ...bot, From 9e746385fb8f23d355774b79f00b99b81b7b551b Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:14:14 +0200 Subject: [PATCH 09/34] fix(clusters): return an empty list when no cluster exists --- packages/backend/src/controllers/cluster.controller.ts | 6 ------ packages/backend/src/services/cluster.service.ts | 5 +---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index a4f0141..1fce768 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -33,12 +33,6 @@ const clusterController = new Hono() "application/json": { schema: resolver(ClustersSchema) }, }, }, - 404: { - description: "Not Found", - content: { - "application/json": { example: "Clusters not found" }, - }, - }, }, }), async (c) => { diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 70eab84..f118a65 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -141,10 +141,7 @@ export default class ClusterService { } public static async getAll() { - const clusters = await ClusterRepository.findMany(); - if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); - - return clusters; + return ClusterRepository.findMany(); } public static async stopAll() { From cbb9f7b26efbf233add429cc0db49bf6ebfcb29e Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:14:24 +0200 Subject: [PATCH 10/34] fix(clusters): allow stopping every cluster when none exists --- packages/backend/src/services/cluster.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index f118a65..65867b1 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -146,7 +146,9 @@ export default class ClusterService { public static async stopAll() { const clusters = await ClusterRepository.findManyIdAndContainerId(); - if (clusters.length === 0) throw new NotFoundException({ message: "Clusters not found" }); + if (clusters.length === 0) { + return; + } await Promise.all(clusters.map(({ containerId }) => DockerService.stopContainer(containerId))); @@ -213,10 +215,8 @@ export default class ClusterService { if (containerImage === null) throw new NotFoundException({ message: "Container image not found" }); - if (previous.length !== 0) { - await this.stopAll(); - await this.removeAll(); - } + await this.stopAll(); + await this.removeAll(); const previousStatuses = new Map(previous.map(({ id, status }) => [id, status])); const updated = await Promise.all( From 2eb139937433b2e22cd29f6996b0c8f2b71540cd Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:14:30 +0200 Subject: [PATCH 11/34] fix(docker): treat a missing container as already stopped --- packages/backend/src/services/docker.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index 3618ff2..ac4ae84 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -78,7 +78,9 @@ export default class DockerService { cause: error, }); - if (error.status === 304) return; + if (error.status === 304 || error.status === 404) { + return; + } throw new FailedDependancyException({ message: `Docker Engine API returned a ${error.status} ${error.message}`, From d76cbba6cebfa4573ae45e1fc4b0bf0b9dba6e85 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:14:35 +0200 Subject: [PATCH 12/34] fix(clusters): stop a running cluster before removing it --- packages/backend/src/services/cluster.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 65867b1..2c7380d 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -135,6 +135,7 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + await DockerService.stopContainer(cluster.containerId); await DockerService.removeContainer(cluster.containerId); return ClusterRepository.deleteById(id); From 612c2b235776d577413680fc097ea1a9923e2031 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:14:42 +0200 Subject: [PATCH 13/34] fix(clusters): send logs time bounds to docker in seconds --- packages/backend/src/services/cluster.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 2c7380d..6ac93a5 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -53,8 +53,8 @@ export default class ClusterService { return DockerService.containerLogs(cluster.containerId, { tail: options.tail, - since: options.since?.getTime(), - until: options.until?.getTime(), + since: options.since && Math.floor(options.since.getTime() / 1000), + until: options.until && Math.ceil(options.until.getTime() / 1000), stdout: true, stderr: true, timestamps: true, From 5e85ade14cc40f374409f12c03fec1f605e02798 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:15:08 +0200 Subject: [PATCH 14/34] fix(clusters): stream cluster logs over server-sent events --- .../src/controllers/cluster.controller.ts | 28 +++++++++++++++++-- .../backend/src/services/cluster.service.ts | 2 +- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index 1fce768..1596c76 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -5,6 +5,7 @@ import { streamSSE } from "hono/streaming"; import authMiddlewares from "#middlewares/auth.middlware"; import { ClusterIdParamSchema, + ClusterLogSchema, ClusterLogsQuerySchema, ClusterLogsSchema, ClustersBulkActionResultSchema, @@ -272,7 +273,7 @@ const clusterController = new Hono() 200: { description: "Server-Sent Events with cluster logs", content: { - "text/event-stream": { schema: resolver(ClusterLogsSchema) }, + "text/event-stream": { schema: resolver(ClusterLogSchema) }, }, }, 404: { @@ -281,13 +282,36 @@ const clusterController = new Hono() "text/plain": { example: "Cluster not found" }, }, }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, }, }), validator("param", ClusterIdParamSchema), async (c) => { const param = c.req.valid("param"); + const logs = await ClusterService.streamLogs(param.id); + + return streamSSE(c, async (stream) => { + stream.onAbort(() => { + logs.destroy(); + }); - await ClusterService.streamLogs(param.id); + try { + for await (const log of logs) { + await stream.writeSSE({ data: JSON.stringify(log) }); + } + } catch (error) { + if (!stream.aborted) { + throw error; + } + } + }); }, ) .get( diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 6ac93a5..339729b 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -66,7 +66,7 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - return DockerService.containerLogs(cluster.containerId, { + return DockerService.streamContainerLogs(cluster.containerId, { tail: 0, stdout: true, stderr: true, From 9df84562e4d567cb59fa921f42e8fb6244cd0b2a Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:15:24 +0200 Subject: [PATCH 15/34] fix(clusters): persist the bot total shards when updating the layout --- .../backend/src/repositories/bot.repository.ts | 15 +++++++++++++++ packages/backend/src/services/cluster.service.ts | 10 ++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/repositories/bot.repository.ts b/packages/backend/src/repositories/bot.repository.ts index 75b78d3..cc2af88 100644 --- a/packages/backend/src/repositories/bot.repository.ts +++ b/packages/backend/src/repositories/bot.repository.ts @@ -76,6 +76,21 @@ export default class BotRepository { }); } + public static async updateTotalShards(data: Pick) { + const bot = await this.findFirstId(); + if (bot === null) { + return null; + } + + return prisma.bot.update({ + data, + where: { + id: bot.id, + }, + select: this.DEFAULT_SELECT, + }); + } + // public static async createClusters(data: Pick[]) { // const bot = await this.findFirstId(); // if (bot === null) return null; diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 339729b..40f38c5 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -219,6 +219,11 @@ export default class ClusterService { await this.stopAll(); await this.removeAll(); + const updatedBot = await BotRepository.updateTotalShards({ totalShards: shards.length }); + if (updatedBot === null) { + throw new NotFoundException({ message: "Bot not found" }); + } + const previousStatuses = new Map(previous.map(({ id, status }) => [id, status])); const updated = await Promise.all( clusters.map((cluster) => @@ -228,10 +233,7 @@ export default class ClusterService { shardIds: cluster.shardIds, status: previousStatuses.get(cluster.id!) === "STOPPED" ? "STOPPED" : "UPDATING", }, - { - ...bot, - totalShards: shards.length, - }, + { ...bot, totalShards: updatedBot.totalShards }, containerImage, ), ), From a80809de656f1028ee3f5c2cb4dde11482799b35 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:15:36 +0200 Subject: [PATCH 16/34] fix(discord): build profile cdn urls with the correct paths --- packages/backend/src/schemas/discord.schema.ts | 4 ++-- packages/backend/src/services/discord.service.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/schemas/discord.schema.ts b/packages/backend/src/schemas/discord.schema.ts index 2eaf3c2..41796e4 100644 --- a/packages/backend/src/schemas/discord.schema.ts +++ b/packages/backend/src/schemas/discord.schema.ts @@ -11,6 +11,6 @@ export const DiscordUserSchema = z.object({ global_name: z.string().nullable(), discriminator: z.string(), avatar: z.string().nullable(), - banner: z.string().nullable(), - accent_color: z.number().int().nullable(), + banner: z.string().nullish(), + accent_color: z.number().int().nullish(), }); diff --git a/packages/backend/src/services/discord.service.ts b/packages/backend/src/services/discord.service.ts index 0251ecc..f7d8a2b 100644 --- a/packages/backend/src/services/discord.service.ts +++ b/packages/backend/src/services/discord.service.ts @@ -43,7 +43,7 @@ export default class DiscordService { } } - private static cdnUrl(kind: "avatar" | "banner", id: string, hash: string) { + private static cdnUrl(kind: "avatars" | "banners", id: string, hash: string) { const extension = hash.startsWith("a_") ? "gif" : "png"; return `${this.DISCORD_CDN_BASE_URL}/${kind}/${id}/${hash}.${extension}`; @@ -60,9 +60,9 @@ export default class DiscordService { name: user.username, displayName: user.global_name, discriminator: user.discriminator, - avatarUrl: user.avatar ? this.cdnUrl("avatar", user.id, user.avatar) : null, - bannerUrl: user.banner ? this.cdnUrl("banner", user.id, user.banner) : null, - accentColor: user.accent_color, + avatarUrl: user.avatar ? this.cdnUrl("avatars", user.id, user.avatar) : null, + bannerUrl: user.banner ? this.cdnUrl("banners", user.id, user.banner) : null, + accentColor: user.accent_color ?? null, }; } } From 34acc5a5c1abaea7cabe1154e3d352196db9431c Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:16:09 +0200 Subject: [PATCH 17/34] build(lint): enable type-aware promise safety rules --- packages/backend/.oxlintrc.json | 8 +++++++- packages/backend/src/controllers/cluster.controller.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/backend/.oxlintrc.json b/packages/backend/.oxlintrc.json index e73870c..833bbad 100644 --- a/packages/backend/.oxlintrc.json +++ b/packages/backend/.oxlintrc.json @@ -4,7 +4,13 @@ "categories": { "correctness": "error" }, - "rules": {}, + "rules": { + "typescript/await-thenable": "error", + "typescript/no-floating-promises": "error", + "typescript/no-misused-promises": "error", + "typescript/no-unnecessary-condition": "error", + "typescript/require-await": "error" + }, "env": { "builtin": true }, diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index 1596c76..fc386ee 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -98,7 +98,7 @@ const clusterController = new Hono() }, }), validator("query", SseIntervalQuerySchema), - async (c) => { + (c) => { const query = c.req.valid("query"); return streamSSE(c, async (stream) => { From 9af025f2ff87a3dc346a4977fd9ac5c8a3b20623 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:30:51 +0200 Subject: [PATCH 18/34] refactor(docker): factor docker engine error handling --- .../backend/src/services/docker.service.ts | 119 ++++-------------- 1 file changed, 24 insertions(+), 95 deletions(-) diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index ac4ae84..76d281c 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -19,13 +19,23 @@ export default class DockerService { private static readonly images = new DockerImagesAPI(socket); private static readonly containers = new DockerContainersAPI(socket); - public static async pullImage(...options: MethodParameters) { - return this.images.create(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) + private static async call(request: Promise): Promise; + private static async call( + request: Promise, + ignoredStatuses: number[], + ): Promise; + private static async call(request: Promise, ignoredStatuses: number[] = []) { + return request.catch((error: unknown) => { + if (!(error instanceof DockerAPIHttpError)) { throw new FailedDependancyException({ message: "Docker Engine API returned an unexpected response", cause: error, }); + } + + if (ignoredStatuses.includes(error.status)) { + return undefined; + } throw new FailedDependancyException({ message: `Docker Engine API returned a ${error.status} ${error.message}`, @@ -34,129 +44,48 @@ export default class DockerService { }); } - public static async create(...options: MethodParameters) { - return this.containers.create(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); + public static async pullImage(...options: MethodParameters) { + return this.call(this.images.create(...options)); + } - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + public static async create(...options: MethodParameters) { + return this.call(this.containers.create(...options)); } public static async startContainer( ...options: MethodParameters ) { - return this.containers.start(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); - - if (error.status === 304) return; - - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + return this.call(this.containers.start(...options), [304]); } public static async stopContainer( ...options: MethodParameters ) { - return this.containers.stop(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); - - if (error.status === 304 || error.status === 404) { - return; - } - - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + return this.call(this.containers.stop(...options), [304, 404]); } public static async restartContainer( ...options: MethodParameters ) { - return this.containers.restart(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); - - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + return this.call(this.containers.restart(...options)); } public static async removeContainer( ...options: MethodParameters ) { - return this.containers.remove(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); - - if (error.status === 404) return; - - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + return this.call(this.containers.remove(...options), [404]); } public static async containerLogs( ...options: MethodParameters ) { - return this.containers.logs(...options).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); - - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + return this.call(this.containers.logs(...options)); } public static async streamContainerLogs( containerId: string, options: Omit[1]>, "follow">, ) { - return this.containers.logs(containerId, { ...options, follow: true }).catch((error) => { - if (!(error instanceof DockerAPIHttpError)) - throw new FailedDependancyException({ - message: "Docker Engine API returned an unexpected response", - cause: error, - }); - - throw new FailedDependancyException({ - message: `Docker Engine API returned a ${error.status} ${error.message}`, - cause: error, - }); - }); + return this.call(this.containers.logs(containerId, { ...options, follow: true })); } } From 122d4b60137742485687375f5e11676e3704bfd4 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:33:06 +0200 Subject: [PATCH 19/34] feat(clusters): implement cluster action and stats handlers --- .../src/controllers/cluster.controller.ts | 110 ++++++++++++++---- .../src/repositories/cluster.repository.ts | 18 +++ .../backend/src/schemas/cluster.schema.ts | 14 +++ packages/backend/src/schemas/docker.schema.ts | 26 +++++ .../backend/src/services/cluster.service.ts | 78 ++++++++++++- .../backend/src/services/docker.service.ts | 88 ++++++++++++++ 6 files changed, 312 insertions(+), 22 deletions(-) create mode 100644 packages/backend/src/schemas/docker.schema.ts diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index fc386ee..6ca9727 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -4,6 +4,8 @@ import { streamSSE } from "hono/streaming"; import authMiddlewares from "#middlewares/auth.middlware"; import { + ClusterActionParamSchema, + ClusterIdActionParamSchema, ClusterIdParamSchema, ClusterLogSchema, ClusterLogsQuerySchema, @@ -129,10 +131,24 @@ const clusterController = new Hono() }, }), validator("query", SseIntervalQuerySchema), + (c) => { + const query = c.req.valid("query"); + + return streamSSE(c, async (stream) => { + while (!stream.aborted) { + const stats = await ClusterService.getAllStats(); + + await stream.writeSSE({ + data: JSON.stringify(stats), + }); + + await stream.sleep(query.interval * 1000); + } + }); + }, ) - .on( - "POST", - ["/start", "/stop", "/restart"], + .post( + "/:action{(?:start|stop|restart)}", describeRoute({ tags, description: @@ -144,22 +160,15 @@ const clusterController = new Hono() "application/json": { schema: resolver(ClustersBulkActionResultSchema) }, }, }, - 404: { - description: "Not Found", - content: { - "text/plain": { example: "Cluster not found" }, - }, - }, - 424: { - description: "Failed Dependency", - content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", - }, - }, - }, }, }), + validator("param", ClusterActionParamSchema), + async (c) => { + const param = c.req.valid("param"); + const result = await ClusterService.runActionOnAll(param.action); + + return c.json(result, 200); + }, ) .get( "/:id", @@ -326,15 +335,35 @@ const clusterController = new Hono() "application/json": { schema: resolver(ClusterStatsSchema) }, }, }, + 400: { + description: "Bad Request", + content: { + "text/plain": { example: "Cluster 0 is not running" }, + }, + }, 404: { description: "Not Found", content: { "text/plain": { example: "Cluster not found" }, }, }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, }, }), validator("param", ClusterIdParamSchema), + async (c) => { + const param = c.req.valid("param"); + const stats = await ClusterService.stats(param.id); + + return c.json(stats, 200); + }, ) .get( "/:id/stats/sse", @@ -348,19 +377,52 @@ const clusterController = new Hono() "text/event-stream": { schema: resolver(ClusterStatsSchema) }, }, }, + 400: { + description: "Bad Request", + content: { + "text/plain": { example: "Cluster 0 is not running" }, + }, + }, 404: { description: "Not Found", content: { "text/plain": { example: "Cluster not found" }, }, }, + 424: { + description: "Failed Dependency", + content: { + "text/plain": { + example: "Docker Engine API returned an unexpected response", + }, + }, + }, }, }), validator("param", ClusterIdParamSchema), + async (c) => { + const param = c.req.valid("param"); + const stats = await ClusterService.streamStats(param.id); + + return streamSSE(c, async (stream) => { + stream.onAbort(() => { + stats.destroy(); + }); + + try { + for await (const sample of stats) { + await stream.writeSSE({ data: JSON.stringify(sample) }); + } + } catch (error) { + if (!stream.aborted) { + throw error; + } + } + }); + }, ) - .on( - "POST", - ["/:id/start", "/:id/stop", "/:id/restart"], + .post( + "/:id/:action{(?:start|stop|restart)}", describeRoute({ tags, description: "Start, stop or restart a cluster", @@ -387,7 +449,13 @@ const clusterController = new Hono() }, }, }), - validator("param", ClusterIdParamSchema), + validator("param", ClusterIdActionParamSchema), + async (c) => { + const param = c.req.valid("param"); + const cluster = await ClusterService.runAction(param.id, param.action); + + return c.json(cluster, 200); + }, ); export default clusterController; diff --git a/packages/backend/src/repositories/cluster.repository.ts b/packages/backend/src/repositories/cluster.repository.ts index 53f70f3..89e01af 100644 --- a/packages/backend/src/repositories/cluster.repository.ts +++ b/packages/backend/src/repositories/cluster.repository.ts @@ -75,6 +75,24 @@ export default class ClusterRepository { }); } + public static async findFirstContainerIdAndStatusById(id: Cluster["id"]) { + return prisma.cluster.findFirst({ + where: { id }, + select: { containerId: true, status: true }, + }); + } + + public static async findManyId() { + return prisma.cluster.findMany({ select: { id: true }, orderBy: { id: "asc" } }); + } + + public static async findManyIdAndContainerIdByStatus(status: Cluster["status"]) { + return prisma.cluster.findMany({ + where: { status }, + select: { id: true, containerId: true }, + }); + } + public static async findManyIdAndContainerId() { return prisma.cluster.findMany({ select: { id: true, containerId: true } }); } diff --git a/packages/backend/src/schemas/cluster.schema.ts b/packages/backend/src/schemas/cluster.schema.ts index 7191c81..2841ea0 100644 --- a/packages/backend/src/schemas/cluster.schema.ts +++ b/packages/backend/src/schemas/cluster.schema.ts @@ -30,6 +30,11 @@ export const SseIntervalQuerySchema = z.object({ }), }); +export const ClusterActionSchema = z.enum(["start", "stop", "restart"]).meta({ + description: "Action to apply", +}); +export type ClusterAction = z.infer; + export const ClusterStatsSchema = z.object({ cpuPercentage: z.number().min(0).nonnegative().meta({ description: "CPU usage percentage of the cluster", @@ -77,11 +82,20 @@ export const ClustersBulkActionResultSchema = z.object({ description: "Failed clusters IDs with the reason of failure", }), }); +export type ClustersBulkActionResult = z.infer; export const ClusterIdParamSchema = z.object({ id: ClusterIdSchema, }); +export const ClusterActionParamSchema = z.object({ + action: ClusterActionSchema, +}); + +export const ClusterIdActionParamSchema = ClusterIdParamSchema.extend( + ClusterActionParamSchema.shape, +); + export const ClusterLogsQuerySchema = z.object({ since: z.coerce.date().optional().meta({ description: "At which point in time does the logs collection start", diff --git a/packages/backend/src/schemas/docker.schema.ts b/packages/backend/src/schemas/docker.schema.ts new file mode 100644 index 0000000..ed46866 --- /dev/null +++ b/packages/backend/src/schemas/docker.schema.ts @@ -0,0 +1,26 @@ +import z from "zod"; + +const DockerCpuStatsSchema = z + .object({ + cpu_usage: z.object({ total_usage: z.number().default(0) }).prefault({}), + system_cpu_usage: z.number().default(0), + online_cpus: z.number().default(1), + }) + .prefault({}); +export type DockerCpuStats = z.infer; + +export const DockerContainerStatsSchema = z.object({ + cpu_stats: DockerCpuStatsSchema, + precpu_stats: DockerCpuStatsSchema, + memory_stats: z + .object({ + usage: z.number().default(0), + limit: z.number().default(0), + stats: z.object({ inactive_file: z.number().default(0) }).prefault({}), + }) + .prefault({}), + networks: z + .record(z.string(), z.object({ rx_bytes: z.number(), tx_bytes: z.number() })) + .default({}), +}); +export type DockerContainerStats = z.infer; diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 40f38c5..acb1a5e 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -3,7 +3,14 @@ import type { Bot, ContainerImage } from "#prisma/client"; import BotRepository from "#repositories/bot.repository"; import ClusterRepository from "#repositories/cluster.repository"; import ContainerImageRepository from "#repositories/container-image.repository"; -import type { Cluster, ClusterId, ClusterLogsQuery, UpdateClusters } from "#schemas/cluster.schema"; +import type { + Cluster, + ClusterAction, + ClusterId, + ClusterLogsQuery, + ClustersBulkActionResult, + UpdateClusters, +} from "#schemas/cluster.schema"; import DockerService from "./docker.service"; @@ -40,6 +47,19 @@ export default class ClusterService { }); } + private static async findRunningContainerId(id: ClusterId) { + const cluster = await ClusterRepository.findFirstContainerIdAndStatusById(id); + if (cluster === null) { + throw new NotFoundException({ message: `Cluster ${id} not found` }); + } + + if (cluster.status !== "RUNNING") { + throw new BadRequestException({ message: `Cluster ${id} is not running` }); + } + + return cluster.containerId; + } + public static async get(id: ClusterId) { const cluster = await ClusterRepository.findFirstById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -74,6 +94,32 @@ export default class ClusterService { }); } + public static async stats(id: ClusterId) { + const containerId = await this.findRunningContainerId(id); + + return DockerService.containerStats(containerId); + } + + public static async getAllStats() { + const clusters = await ClusterRepository.findManyIdAndContainerIdByStatus("RUNNING"); + const results = await Promise.allSettled( + clusters.map( + async ({ id, containerId }) => + [id, await DockerService.containerStats(containerId)] as const, + ), + ); + + return Object.fromEntries( + results.flatMap((result) => (result.status === "fulfilled" ? [result.value] : [])), + ); + } + + public static async streamStats(id: ClusterId) { + const containerId = await this.findRunningContainerId(id); + + return DockerService.streamContainerStats(containerId); + } + public static async start(id: ClusterId) { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); @@ -131,6 +177,36 @@ export default class ClusterService { return updated; } + public static async runAction(id: ClusterId, action: ClusterAction) { + const actions = { + start: () => this.start(id), + stop: () => this.stop(id), + restart: () => this.restart(id), + } satisfies Record Promise>; + + return actions[action](); + } + + public static async runActionOnAll(action: ClusterAction): Promise { + const clusters = await ClusterRepository.findManyId(); + const outcomes = await Promise.all( + clusters.map(({ id }) => + this.runAction(id, action).then( + () => ({ id, reason: null }), + (error: unknown) => ({ + id, + reason: error instanceof Error ? error.message : String(error), + }), + ), + ), + ); + + return { + succeeded: outcomes.filter(({ reason }) => reason === null).map(({ id }) => id), + failed: outcomes.flatMap(({ id, reason }) => (reason === null ? [] : [{ id, reason }])), + }; + } + public static async remove(id: ClusterId) { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index 76d281c..976af31 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -1,3 +1,5 @@ +import { Transform } from "node:stream"; + import { DockerAPIHttpError, DockerContainersAPI, @@ -6,6 +8,11 @@ import { } from "@hallmaster/docker.js"; import { FailedDependancyException } from "#exceptions/http-exceptions"; +import { + DockerContainerStatsSchema, + type DockerContainerStats, + type DockerCpuStats, +} from "#schemas/docker.schema"; type MethodParameters< C extends abstract new (...args: any[]) => any, @@ -44,6 +51,47 @@ export default class DockerService { }); } + private static percentage(value: number, total: number) { + return total > 0 ? (value / total) * 100 : 0; + } + + private static cpuPercentage(cpu: DockerCpuStats, precpu: DockerCpuStats) { + const cpuDelta = cpu.cpu_usage.total_usage - precpu.cpu_usage.total_usage; + const systemDelta = cpu.system_cpu_usage - precpu.system_cpu_usage; + if (precpu.system_cpu_usage === 0 || cpuDelta <= 0) { + return 0; + } + + return this.percentage(cpuDelta, systemDelta) * cpu.online_cpus; + } + + private static toContainerStats(stats: DockerContainerStats) { + const memoryUsage = Math.max( + stats.memory_stats.usage - stats.memory_stats.stats.inactive_file, + 0, + ); + + return { + cpuPercentage: this.cpuPercentage(stats.cpu_stats, stats.precpu_stats), + memory: { + usage: memoryUsage, + percentage: this.percentage(memoryUsage, stats.memory_stats.limit), + }, + network: Object.entries(stats.networks).map(([name, network]) => ({ + interface: name, + transmitted: network.tx_bytes, + received: network.rx_bytes, + })), + }; + } + + private static unexpectedContainerStats(cause: unknown) { + return new FailedDependancyException({ + message: "Docker Engine API returned unexpected container stats", + cause, + }); + } + public static async pullImage(...options: MethodParameters) { return this.call(this.images.create(...options)); } @@ -88,4 +136,44 @@ export default class DockerService { ) { return this.call(this.containers.logs(containerId, { ...options, follow: true })); } + + public static async containerStats(containerId: string) { + const sample = await this.call( + this.containers.stats(containerId, { stream: false, oneShot: false }), + ); + + const result = DockerContainerStatsSchema.safeParse(sample); + if (!result.success) { + throw this.unexpectedContainerStats(result.error); + } + + return this.toContainerStats(result.data); + } + + public static async streamContainerStats(containerId: string) { + const samples = await this.call(this.containers.stats(containerId, { stream: true })); + const stats = new Transform({ + objectMode: true, + transform: (sample: unknown, _encoding, callback) => { + const result = DockerContainerStatsSchema.safeParse(sample); + if (!result.success) { + callback(this.unexpectedContainerStats(result.error)); + return; + } + + if (result.data.precpu_stats.system_cpu_usage === 0) { + callback(); + return; + } + + callback(null, this.toContainerStats(result.data)); + }, + }); + + samples.pipe(stats); + samples.on("error", (error) => stats.destroy(error)); + stats.on("close", () => samples.destroy()); + + return stats; + } } From 334ae229117229d1f68c88062279d128aa127232 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:54:15 +0200 Subject: [PATCH 20/34] feat(api): return every error as a json body --- packages/backend/src/app.ts | 36 ++++++++ .../src/controllers/auth.controller.ts | 14 ++- .../backend/src/controllers/bot.controller.ts | 90 ++++++++++++------ .../src/controllers/cluster.controller.ts | 91 +++++++++++++------ .../backend/src/exceptions/http-exceptions.ts | 8 ++ .../backend/src/middlewares/auth.middlware.ts | 8 +- .../src/middlewares/validator.middleware.ts | 29 ++++++ packages/backend/src/schemas/error.schema.ts | 15 +++ 8 files changed, 233 insertions(+), 58 deletions(-) create mode 100644 packages/backend/src/middlewares/validator.middleware.ts create mode 100644 packages/backend/src/schemas/error.schema.ts diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index 7b51efa..78f0d9f 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -1,19 +1,55 @@ import { Scalar } from "@scalar/hono-api-reference"; import { Hono } from "hono"; import { openAPIRouteHandler } from "hono-openapi"; +import { HTTPException } from "hono/http-exception"; import { logger } from "hono/logger"; +import z from "zod"; import authController from "#controllers/auth.controller"; import botController from "#controllers/bot.controller"; import clusterController from "#controllers/cluster.controller"; +import { ValidationException } from "#exceptions/http-exceptions"; +import { ApiErrorSchema, type ApiError } from "#schemas/error.schema"; const app = new Hono(); app.use("/api/*", logger()); +app.onError((error, c) => { + if (error instanceof ValidationException) { + return c.json( + { message: error.message, issues: error.issues } satisfies ApiError, + error.status, + ); + } + + if (error instanceof HTTPException) { + const authenticate = error.res?.headers.get("WWW-Authenticate"); + if (authenticate) { + c.header("WWW-Authenticate", authenticate); + } + + return c.json({ message: error.message } satisfies ApiError, error.status); + } + + console.error(error); + + return c.json({ message: "Internal Server Error" } satisfies ApiError, 500); +}); + +app.notFound((c) => c.json({ message: "Route not found" } satisfies ApiError, 404)); + app.get( "/openapi.json", openAPIRouteHandler(app, { + defaultValidationErrorResponse: { + description: "Bad Request", + content: { + "application/json": { + schema: z.toJSONSchema(ApiErrorSchema) as Record, + }, + }, + }, documentation: { info: { title: "Hallmaster API Documentation", diff --git a/packages/backend/src/controllers/auth.controller.ts b/packages/backend/src/controllers/auth.controller.ts index 9509ea3..beda30a 100644 --- a/packages/backend/src/controllers/auth.controller.ts +++ b/packages/backend/src/controllers/auth.controller.ts @@ -1,7 +1,9 @@ import { Hono } from "hono"; -import { describeRoute, resolver, validator } from "hono-openapi"; +import { describeRoute, resolver } from "hono-openapi"; +import validator from "#middlewares/validator.middleware"; import { UserSchema, UserTokenResponseSchema } from "#schemas/auth.schema"; +import { ApiErrorSchema } from "#schemas/error.schema"; import { AuthService } from "#services/auth.service"; const tags = ["Authentication"]; @@ -22,7 +24,10 @@ const authController = new Hono() 409: { description: "Conflict", content: { - "text/plain": { example: "A user is already registered" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "A user is already registered" }, + }, }, }, }, @@ -50,7 +55,10 @@ const authController = new Hono() 401: { description: "Unauthorized", content: { - "text/plain": { example: "Invalid username or password" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Invalid username or password" }, + }, }, }, }, diff --git a/packages/backend/src/controllers/bot.controller.ts b/packages/backend/src/controllers/bot.controller.ts index 2f74191..7540f3b 100644 --- a/packages/backend/src/controllers/bot.controller.ts +++ b/packages/backend/src/controllers/bot.controller.ts @@ -1,9 +1,11 @@ import { Hono } from "hono"; -import { describeRoute, resolver, validator } from "hono-openapi"; +import { describeRoute, resolver } from "hono-openapi"; import authMiddlewares from "#middlewares/auth.middlware"; +import validator from "#middlewares/validator.middleware"; import { BotSchema, ContainerImageSchema, CreateBotSchema } from "#schemas/bot.schema"; import { DiscordUserSchema } from "#schemas/discord.schema"; +import { ApiErrorSchema } from "#schemas/error.schema"; import BotService from "#services/bot.service"; const tags = ["Bot"]; @@ -25,19 +27,25 @@ const botController = new Hono() 409: { description: "Conflict", content: { - "text/plain": { - example: "A bot is already registered", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "A bot is already registered" }, }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { + "application/json": { + schema: resolver(ApiErrorSchema), examples: { - "Unreachable or timed out": { value: "Discord API is unreachable or timed out" }, - "Invalid token": { value: "Invalid Discord bot token" }, - Unexpected: { value: "Discord API returned an unexpected response body" }, + "Unreachable or timed out": { + value: { message: "Discord API is unreachable or timed out" }, + }, + "Invalid token": { value: { message: "Invalid Discord bot token" } }, + Unexpected: { + value: { message: "Discord API returned an unexpected response body" }, + }, }, }, }, @@ -67,7 +75,10 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Bot not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Bot not found" }, + }, }, }, }, @@ -93,14 +104,18 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Bot not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Bot not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Invalid response from the Discord API", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Invalid response from the Discord API" }, }, }, }, @@ -129,7 +144,10 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Bot not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Bot not found" }, + }, }, }, }, @@ -155,17 +173,25 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Bot not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Bot not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { + "application/json": { + schema: resolver(ApiErrorSchema), examples: { - "Unreachable or timed out": { value: "Discord API is unreachable or timed out" }, - "Invalid token": { value: "Invalid Discord bot token" }, - Unexpected: { value: "Discord API returned an unexpected response body" }, + "Unreachable or timed out": { + value: { message: "Discord API is unreachable or timed out" }, + }, + "Invalid token": { value: { message: "Invalid Discord bot token" } }, + Unexpected: { + value: { message: "Discord API returned an unexpected response body" }, + }, }, }, }, @@ -193,7 +219,10 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Container image not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Container image not found" }, + }, }, }, }, @@ -219,22 +248,27 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Bot not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Bot not found" }, + }, }, }, 409: { description: "Conflict", content: { - "text/plain": { - example: "A container image is already registered", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "A container image is already registered" }, }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -263,14 +297,18 @@ const botController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Container image not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Container image not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, diff --git a/packages/backend/src/controllers/cluster.controller.ts b/packages/backend/src/controllers/cluster.controller.ts index 6ca9727..5510f49 100644 --- a/packages/backend/src/controllers/cluster.controller.ts +++ b/packages/backend/src/controllers/cluster.controller.ts @@ -1,8 +1,9 @@ import { Hono } from "hono"; -import { describeRoute, resolver, validator } from "hono-openapi"; +import { describeRoute, resolver } from "hono-openapi"; import { streamSSE } from "hono/streaming"; import authMiddlewares from "#middlewares/auth.middlware"; +import validator from "#middlewares/validator.middleware"; import { ClusterActionParamSchema, ClusterIdActionParamSchema, @@ -18,6 +19,7 @@ import { SseIntervalQuerySchema, UpdateClustersSchema, } from "#schemas/cluster.schema"; +import { ApiErrorSchema } from "#schemas/error.schema"; import ClusterService from "#services/cluster.service"; const tags = ["Clusters"]; @@ -59,10 +61,11 @@ const clusterController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { + "application/json": { + schema: resolver(ApiErrorSchema), examples: { - "Bot not found": { value: "Bot not found" }, - "Container image not found": { value: "Container image not found" }, + "Bot not found": { value: { message: "Bot not found" } }, + "Container image not found": { value: { message: "Container image not found" } }, }, }, }, @@ -70,8 +73,9 @@ const clusterController = new Hono() 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -185,7 +189,10 @@ const clusterController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, }, @@ -213,14 +220,18 @@ const clusterController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -249,14 +260,18 @@ const clusterController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -288,14 +303,18 @@ const clusterController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -338,20 +357,27 @@ const clusterController = new Hono() 400: { description: "Bad Request", content: { - "text/plain": { example: "Cluster 0 is not running" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster 0 is not running" }, + }, }, }, 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -380,20 +406,27 @@ const clusterController = new Hono() 400: { description: "Bad Request", content: { - "text/plain": { example: "Cluster 0 is not running" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster 0 is not running" }, + }, }, }, 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, @@ -436,14 +469,18 @@ const clusterController = new Hono() 404: { description: "Not Found", content: { - "text/plain": { example: "Cluster not found" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Cluster not found" }, + }, }, }, 424: { description: "Failed Dependency", content: { - "text/plain": { - example: "Docker Engine API returned an unexpected response", + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Docker Engine API returned an unexpected response" }, }, }, }, diff --git a/packages/backend/src/exceptions/http-exceptions.ts b/packages/backend/src/exceptions/http-exceptions.ts index 87ad3fd..cb481ef 100644 --- a/packages/backend/src/exceptions/http-exceptions.ts +++ b/packages/backend/src/exceptions/http-exceptions.ts @@ -1,5 +1,7 @@ import { HTTPException } from "hono/http-exception"; +import type { ApiIssue } from "#schemas/error.schema"; + type HTTPExceptionOptions = Omit< NonNullable[1]>, "res" @@ -37,3 +39,9 @@ export class FailedDependancyException extends HTTPException { super(424, options); } } + +export class ValidationException extends BadRequestException { + constructor(public readonly issues: ApiIssue[]) { + super({ message: "Validation failed" }); + } +} diff --git a/packages/backend/src/middlewares/auth.middlware.ts b/packages/backend/src/middlewares/auth.middlware.ts index 2c8b24a..d82f675 100644 --- a/packages/backend/src/middlewares/auth.middlware.ts +++ b/packages/backend/src/middlewares/auth.middlware.ts @@ -1,7 +1,8 @@ -import { describeRoute } from "hono-openapi"; +import { describeRoute, resolver } from "hono-openapi"; import { jwt } from "hono/jwt"; import env from "#lib/env"; +import { ApiErrorSchema } from "#schemas/error.schema"; const authMiddlewares = [ describeRoute({ @@ -10,7 +11,10 @@ const authMiddlewares = [ description: "Route is protected by an Authorization header that is either not provided or invalid", content: { - "text/plain": { example: "Unauthorized" }, + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Unauthorized" }, + }, }, }, }, diff --git a/packages/backend/src/middlewares/validator.middleware.ts b/packages/backend/src/middlewares/validator.middleware.ts new file mode 100644 index 0000000..9227a6f --- /dev/null +++ b/packages/backend/src/middlewares/validator.middleware.ts @@ -0,0 +1,29 @@ +import type { ValidationTargets } from "hono"; +import { validator as openAPIValidator } from "hono-openapi"; + +import { ValidationException } from "#exceptions/http-exceptions"; + +type ValidatorSchema = Parameters[1]; +type ValidationResult = Parameters[2]>>[0]; + +function validationHook(result: ValidationResult) { + if (result.success) { + return; + } + + throw new ValidationException( + result.error.map((issue) => ({ + path: (issue.path ?? []) + .map((segment) => String(typeof segment === "object" ? segment.key : segment)) + .join("."), + message: issue.message, + })), + ); +} + +export default function validator< + Schema extends ValidatorSchema, + Target extends keyof ValidationTargets, +>(target: Target, schema: Schema) { + return openAPIValidator(target, schema, validationHook); +} diff --git a/packages/backend/src/schemas/error.schema.ts b/packages/backend/src/schemas/error.schema.ts new file mode 100644 index 0000000..20fa7ea --- /dev/null +++ b/packages/backend/src/schemas/error.schema.ts @@ -0,0 +1,15 @@ +import z from "zod"; + +export const ApiIssueSchema = z.object({ + path: z.string().meta({ description: "Dot-separated path of the invalid field" }), + message: z.string().meta({ description: "Reason why the field is invalid" }), +}); +export type ApiIssue = z.infer; + +export const ApiErrorSchema = z.object({ + message: z.string().meta({ description: "Error message" }), + issues: z.array(ApiIssueSchema).optional().meta({ + description: "Invalid fields, only present on validation errors", + }), +}); +export type ApiError = z.infer; From 29b0d44bab2571d5728cefba723989d27977f7c1 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:55:33 +0200 Subject: [PATCH 21/34] fix(bot): keep the registry password only for the same registry and user --- packages/backend/src/services/bot.service.ts | 47 +++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts index 0df430f..2639f27 100644 --- a/packages/backend/src/services/bot.service.ts +++ b/packages/backend/src/services/bot.service.ts @@ -16,6 +16,18 @@ export default class BotService { return { host, image, tag }; } + private static registryAuth( + serveraddress: string, + username: string | null | undefined, + password: string | null | undefined, + ) { + if (!username || !password) { + return undefined; + } + + return { serveraddress, username, password }; + } + public static async create({ token }: CreateBot): Promise { await DiscordService.getGatewayBot(token); @@ -88,19 +100,10 @@ export default class BotService { }: ContainerImage): Promise> { const { host, image, tag } = this.parseContainerImage(url); - const auth = - username && password - ? { - serveraddress: host, - username: username, - password: password, - } - : undefined; - await DockerService.pullImage({ fromImage: [host, image].join("/"), tag, - auth, + auth: this.registryAuth(host, username, password), }); const containerImage = await ContainerImageRepository.create({ @@ -135,29 +138,29 @@ export default class BotService { username, password, }: ContainerImage): Promise> { - const { host, image, tag } = this.parseContainerImage(url); + const current = await ContainerImageRepository.findFirst(); + if (current === null) { + throw new NotFoundException({ message: "Container image not found" }); + } - const auth = - username && password - ? { - serveraddress: host, - username: username, - password: password, - } - : undefined; + const { host, image, tag } = this.parseContainerImage(url); + const keepsCredentials = current.serverName === host && current.username === (username ?? null); + const credentials = { + username: username ?? null, + password: password ?? (keepsCredentials ? current.password : null), + }; await DockerService.pullImage({ fromImage: [host, image].join("/"), tag, - auth, + auth: this.registryAuth(host, credentials.username, credentials.password), }); const containerImage = await ContainerImageRepository.update({ image, tag, serverName: host, - username: username ?? null, - password: password ?? null, + ...credentials, }); if (containerImage === null) From ffdb95d720ac7266c4b8e6b239eb9598a00098c5 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:56:14 +0200 Subject: [PATCH 22/34] feat(api): read the port from the environment and enable cors --- packages/backend/.env.example | 2 ++ packages/backend/src/app.ts | 2 ++ packages/backend/src/index.ts | 4 +++- packages/backend/src/lib/env.ts | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/backend/.env.example b/packages/backend/.env.example index 1411c3a..25dd757 100644 --- a/packages/backend/.env.example +++ b/packages/backend/.env.example @@ -2,3 +2,5 @@ DATABASE_URL="postgres://postgres:postgres@localhost:51214/template1?sslmode=dis # openssl rand -base64 32 SECRET="change_me" + +PORT=3000 diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index 78f0d9f..11d2a4c 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -1,6 +1,7 @@ import { Scalar } from "@scalar/hono-api-reference"; import { Hono } from "hono"; import { openAPIRouteHandler } from "hono-openapi"; +import { cors } from "hono/cors"; import { HTTPException } from "hono/http-exception"; import { logger } from "hono/logger"; import z from "zod"; @@ -14,6 +15,7 @@ import { ApiErrorSchema, type ApiError } from "#schemas/error.schema"; const app = new Hono(); app.use("/api/*", logger()); +app.use("/api/*", cors()); app.onError((error, c) => { if (error instanceof ValidationException) { diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index cb94298..d082160 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -1,11 +1,13 @@ import { serve } from "@hono/node-server"; +import env from "#lib/env"; + import app from "./app"; serve( { fetch: app.fetch, - port: 3000, + port: env.PORT, }, (info) => { console.log(`Server is running on http://localhost:${info.port}`); diff --git a/packages/backend/src/lib/env.ts b/packages/backend/src/lib/env.ts index 167454e..6fbe807 100644 --- a/packages/backend/src/lib/env.ts +++ b/packages/backend/src/lib/env.ts @@ -1,7 +1,9 @@ +import "dotenv/config"; import z from "zod"; const EnvironmentSchema = z.object({ SECRET: z.string(), + PORT: z.coerce.number().int().positive().default(3000), }); const env = EnvironmentSchema.parse(process.env); From 2addc253c9c0436d33d70a00f8e1030fedd530b9 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 22:58:03 +0200 Subject: [PATCH 23/34] fix(bot): accept image names without registry and pull images only when missing --- packages/backend/src/services/bot.service.ts | 34 ++++--------- .../backend/src/services/cluster.service.ts | 1 + .../backend/src/services/docker.service.ts | 50 ++++++++++++++++++- 3 files changed, 58 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts index 2639f27..4e317db 100644 --- a/packages/backend/src/services/bot.service.ts +++ b/packages/backend/src/services/bot.service.ts @@ -8,26 +8,18 @@ import DiscordService from "./discord.service"; import DockerService from "./docker.service"; export default class BotService { - // FIX this doesn't work for local images without a host (example: "hallmaster-bot:latest") private static parseContainerImage(url: ContainerImage["url"]) { - const [host, ...path] = url.trim().split("/"); - const [image, tag = "latest"] = path.join("/").split(":"); + const [first, ...rest] = url.trim().split("/"); + const hasRegistry = + rest.length > 0 && (first.includes(".") || first.includes(":") || first === "localhost"); + const host = hasRegistry ? first : "docker.io"; + const path = hasRegistry ? rest : [first, ...rest]; + const repository = host === "docker.io" && path.length === 1 ? ["library", ...path] : path; + const [image, tag = "latest"] = repository.join("/").split(":"); return { host, image, tag }; } - private static registryAuth( - serveraddress: string, - username: string | null | undefined, - password: string | null | undefined, - ) { - if (!username || !password) { - return undefined; - } - - return { serveraddress, username, password }; - } - public static async create({ token }: CreateBot): Promise { await DiscordService.getGatewayBot(token); @@ -100,11 +92,7 @@ export default class BotService { }: ContainerImage): Promise> { const { host, image, tag } = this.parseContainerImage(url); - await DockerService.pullImage({ - fromImage: [host, image].join("/"), - tag, - auth: this.registryAuth(host, username, password), - }); + await DockerService.ensureImage({ serverName: host, image, tag, username, password }); const containerImage = await ContainerImageRepository.create({ image, @@ -150,11 +138,7 @@ export default class BotService { password: password ?? (keepsCredentials ? current.password : null), }; - await DockerService.pullImage({ - fromImage: [host, image].join("/"), - tag, - auth: this.registryAuth(host, credentials.username, credentials.password), - }); + await DockerService.ensureImage({ serverName: host, image, tag, ...credentials }); const containerImage = await ContainerImageRepository.update({ image, diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index acb1a5e..5c5f3fb 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -292,6 +292,7 @@ export default class ClusterService { if (containerImage === null) throw new NotFoundException({ message: "Container image not found" }); + await DockerService.ensureImage(containerImage); await this.stopAll(); await this.removeAll(); diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index 976af31..64b10e9 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -19,6 +19,14 @@ type MethodParameters< P extends keyof InstanceType, > = Parameters[P], (...args: any[]) => any>>; +type RegistryImage = { + serverName: string; + image: string; + tag: string; + username?: string | null; + password?: string | null; +}; + const socket = new DockerSocket(); await socket.init(); @@ -92,8 +100,46 @@ export default class DockerService { }); } - public static async pullImage(...options: MethodParameters) { - return this.call(this.images.create(...options)); + private static registryAuth({ serverName, username, password }: RegistryImage) { + if (!username || !password) { + return undefined; + } + + return { serveraddress: serverName, username, password }; + } + + private static familiarReference({ serverName, image, tag }: RegistryImage) { + if (serverName !== "docker.io") { + return `${serverName}/${image}:${tag}`; + } + + return `${image.replace(/^library\//, "")}:${tag}`; + } + + private static async pullImage(image: RegistryImage) { + return this.call( + this.images.create({ + fromImage: `${image.serverName}/${image.image}`, + tag: image.tag, + auth: this.registryAuth(image), + }), + ); + } + + private static async hasImage(image: RegistryImage) { + const images = await this.call( + this.images.list({ filters: { reference: [this.familiarReference(image)] } }), + ); + + return images.length > 0; + } + + public static async ensureImage(image: RegistryImage) { + if (await this.hasImage(image)) { + return; + } + + await this.pullImage(image); } public static async create(...options: MethodParameters) { From 787d29010972ae505db52aa3357181d5056ff165 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:10:06 +0200 Subject: [PATCH 24/34] refactor(prisma): store the desired cluster status --- .../migration.sql | 14 +++ packages/backend/prisma/schema.prisma | 17 ++- .../src/repositories/bot.repository.ts | 2 +- .../src/repositories/cluster.repository.ts | 29 +++-- .../backend/src/services/cluster.service.ts | 111 ++++++++---------- 5 files changed, 88 insertions(+), 85 deletions(-) create mode 100644 packages/backend/prisma/migrations/20261011210742_cluster_desired_status/migration.sql diff --git a/packages/backend/prisma/migrations/20261011210742_cluster_desired_status/migration.sql b/packages/backend/prisma/migrations/20261011210742_cluster_desired_status/migration.sql new file mode 100644 index 0000000..9ccbe68 --- /dev/null +++ b/packages/backend/prisma/migrations/20261011210742_cluster_desired_status/migration.sql @@ -0,0 +1,14 @@ +-- CreateEnum +CREATE TYPE "ClusterDesiredStatus" AS ENUM ('RUNNING', 'STOPPED'); + +-- AlterTable +ALTER TABLE "cluster" ADD COLUMN "desired_status" "ClusterDesiredStatus"; + +UPDATE "cluster" +SET "desired_status" = CASE WHEN "status" = 'STOPPED' THEN 'STOPPED' ELSE 'RUNNING' END::"ClusterDesiredStatus"; + +ALTER TABLE "cluster" ALTER COLUMN "desired_status" SET NOT NULL, +DROP COLUMN "status"; + +-- DropEnum +DROP TYPE "ClusterStatus"; diff --git a/packages/backend/prisma/schema.prisma b/packages/backend/prisma/schema.prisma index 1e47262..b28f8d1 100644 --- a/packages/backend/prisma/schema.prisma +++ b/packages/backend/prisma/schema.prisma @@ -35,21 +35,18 @@ model ContainerImage { @@map("container_image") } -enum ClusterStatus { - STARTING +enum ClusterDesiredStatus { RUNNING STOPPED - UPDATING - ERROR } model Cluster { - id Int - botId String @map("bot_id") - containerId String @map("container_id") - status ClusterStatus - shardIds Int[] @map("shard_ids") - bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) + id Int + botId String @map("bot_id") + containerId String @map("container_id") + desiredStatus ClusterDesiredStatus @map("desired_status") + shardIds Int[] @map("shard_ids") + bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade) @@id([botId, id]) @@map("cluster") diff --git a/packages/backend/src/repositories/bot.repository.ts b/packages/backend/src/repositories/bot.repository.ts index cc2af88..9ddda7a 100644 --- a/packages/backend/src/repositories/bot.repository.ts +++ b/packages/backend/src/repositories/bot.repository.ts @@ -59,7 +59,7 @@ export default class BotRepository { public static async findFirstClusters() { return prisma.bot.findFirst({ - select: { clusters: { select: { id: true, status: true, shardIds: true } } }, + select: { clusters: { select: { id: true, desiredStatus: true, shardIds: true } } }, }); } diff --git a/packages/backend/src/repositories/cluster.repository.ts b/packages/backend/src/repositories/cluster.repository.ts index 89e01af..52b89be 100644 --- a/packages/backend/src/repositories/cluster.repository.ts +++ b/packages/backend/src/repositories/cluster.repository.ts @@ -5,7 +5,7 @@ export default class ClusterRepository { private static readonly DEFAULT_SELECT = { id: true, shardIds: true, - status: true, + desiredStatus: true, } as const; public static async findFirstContainerIdById(id: Cluster["id"]) { @@ -18,7 +18,7 @@ export default class ClusterRepository { } public static async create( - data: Pick, + data: Pick, ) { return prisma.cluster.create({ data, @@ -46,13 +46,16 @@ export default class ClusterRepository { }); } - public static async updateStatusById(status: Cluster["status"], id: Cluster["id"]) { + public static async updateDesiredStatusById( + desiredStatus: Cluster["desiredStatus"], + id: Cluster["id"], + ) { const cluster = await this.findFirstBotIdById(id); if (cluster === null) return null; return prisma.cluster.update({ data: { - status, + desiredStatus, }, where: { botId_id: { botId: cluster.botId, id } }, select: this.DEFAULT_SELECT, @@ -72,13 +75,14 @@ export default class ClusterRepository { public static async findMany() { return prisma.cluster.findMany({ select: this.DEFAULT_SELECT, + orderBy: { id: "asc" }, }); } - public static async findFirstContainerIdAndStatusById(id: Cluster["id"]) { + public static async findFirstContainerIdAndDesiredStatusById(id: Cluster["id"]) { return prisma.cluster.findFirst({ where: { id }, - select: { containerId: true, status: true }, + select: { containerId: true, desiredStatus: true }, }); } @@ -86,9 +90,11 @@ export default class ClusterRepository { return prisma.cluster.findMany({ select: { id: true }, orderBy: { id: "asc" } }); } - public static async findManyIdAndContainerIdByStatus(status: Cluster["status"]) { + public static async findManyIdAndContainerIdByDesiredStatus( + desiredStatus: Cluster["desiredStatus"], + ) { return prisma.cluster.findMany({ - where: { status }, + where: { desiredStatus }, select: { id: true, containerId: true }, }); } @@ -97,9 +103,12 @@ export default class ClusterRepository { return prisma.cluster.findMany({ select: { id: true, containerId: true } }); } - public static async updateManyStatusById(status: Cluster["status"], ids: Cluster["id"][]) { + public static async updateManyDesiredStatusById( + desiredStatus: Cluster["desiredStatus"], + ids: Cluster["id"][], + ) { return prisma.cluster.updateMany({ - data: { status }, + data: { desiredStatus }, where: { id: { in: ids } }, }); } diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 5c5f3fb..8957408 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -1,5 +1,5 @@ import { BadRequestException, NotFoundException } from "#exceptions/http-exceptions"; -import type { Bot, ContainerImage } from "#prisma/client"; +import type { Bot, Cluster as ClusterRow, ContainerImage } from "#prisma/client"; import BotRepository from "#repositories/bot.repository"; import ClusterRepository from "#repositories/cluster.repository"; import ContainerImageRepository from "#repositories/container-image.repository"; @@ -15,8 +15,16 @@ import type { import DockerService from "./docker.service"; export default class ClusterService { + private static toCluster({ + id, + shardIds, + desiredStatus, + }: Pick): Cluster { + return { id, shardIds, status: desiredStatus }; + } + private static async create( - cluster: Pick, + cluster: Pick, bot: Pick, containerImage: Pick, ) { @@ -38,22 +46,24 @@ export default class ClusterService { `${bot.id}-${cluster.id}`, ); - return ClusterRepository.create({ + await ClusterRepository.create({ id: cluster.id, botId: bot.id, containerId: container.Id, shardIds: cluster.shardIds, - status: cluster.status, + desiredStatus: cluster.desiredStatus, }); + + return container.Id; } private static async findRunningContainerId(id: ClusterId) { - const cluster = await ClusterRepository.findFirstContainerIdAndStatusById(id); + const cluster = await ClusterRepository.findFirstContainerIdAndDesiredStatusById(id); if (cluster === null) { throw new NotFoundException({ message: `Cluster ${id} not found` }); } - if (cluster.status !== "RUNNING") { + if (cluster.desiredStatus !== "RUNNING") { throw new BadRequestException({ message: `Cluster ${id} is not running` }); } @@ -64,7 +74,7 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - return cluster; + return this.toCluster(cluster); } public static async logs(id: ClusterId, options: ClusterLogsQuery) { @@ -101,7 +111,7 @@ export default class ClusterService { } public static async getAllStats() { - const clusters = await ClusterRepository.findManyIdAndContainerIdByStatus("RUNNING"); + const clusters = await ClusterRepository.findManyIdAndContainerIdByDesiredStatus("RUNNING"); const results = await Promise.allSettled( clusters.map( async ({ id, containerId }) => @@ -124,57 +134,30 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - if ((await ClusterRepository.updateStatusById("STARTING", id)) === null) - throw new NotFoundException({ message: `Cluster ${id} not found` }); - - await DockerService.startContainer(cluster.containerId).catch(async (error) => { - if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) - throw new NotFoundException({ message: `Cluster ${id} not found` }); - - throw error; - }); + await ClusterRepository.updateDesiredStatusById("RUNNING", id); + await DockerService.startContainer(cluster.containerId); - const updated = await ClusterRepository.updateStatusById("RUNNING", id); - if (updated === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - - return updated; + return this.get(id); } public static async stop(id: ClusterId) { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await DockerService.stopContainer(cluster.containerId).catch(async (error) => { - if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) - throw new NotFoundException({ message: `Cluster ${id} not found` }); - - throw error; - }); - - const updated = await ClusterRepository.updateStatusById("STOPPED", id); - if (updated === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + await ClusterRepository.updateDesiredStatusById("STOPPED", id); + await DockerService.stopContainer(cluster.containerId); - return updated; + return this.get(id); } public static async restart(id: ClusterId) { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - if ((await ClusterRepository.updateStatusById("STARTING", id)) === null) - throw new NotFoundException({ message: `Cluster ${id} not found` }); + await ClusterRepository.updateDesiredStatusById("RUNNING", id); + await DockerService.restartContainer(cluster.containerId); - await DockerService.restartContainer(cluster.containerId).catch(async (error) => { - if ((await ClusterRepository.updateStatusById("ERROR", id)) === null) - throw new NotFoundException({ message: `Cluster ${id} not found` }); - - throw error; - }); - - const updated = await ClusterRepository.updateStatusById("RUNNING", id); - if (updated === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - - return updated; + return this.get(id); } public static async runAction(id: ClusterId, action: ClusterAction) { @@ -214,11 +197,16 @@ export default class ClusterService { await DockerService.stopContainer(cluster.containerId); await DockerService.removeContainer(cluster.containerId); - return ClusterRepository.deleteById(id); + const removed = await ClusterRepository.deleteById(id); + if (removed === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + + return this.toCluster(removed); } public static async getAll() { - return ClusterRepository.findMany(); + const clusters = await ClusterRepository.findMany(); + + return clusters.map((cluster) => this.toCluster(cluster)); } public static async stopAll() { @@ -229,7 +217,7 @@ export default class ClusterService { await Promise.all(clusters.map(({ containerId }) => DockerService.stopContainer(containerId))); - return ClusterRepository.updateManyStatusById( + return ClusterRepository.updateManyDesiredStatusById( "STOPPED", clusters.map(({ id }) => id), ); @@ -301,27 +289,22 @@ export default class ClusterService { throw new NotFoundException({ message: "Bot not found" }); } - const previousStatuses = new Map(previous.map(({ id, status }) => [id, status])); - const updated = await Promise.all( - clusters.map((cluster) => - this.create( - { - id: cluster.id!, - shardIds: cluster.shardIds, - status: previousStatuses.get(cluster.id!) === "STOPPED" ? "STOPPED" : "UPDATING", - }, + const desiredStatuses = new Map(previous.map(({ id, desiredStatus }) => [id, desiredStatus])); + await Promise.all( + clusters.map(async (cluster) => { + const desiredStatus = desiredStatuses.get(cluster.id!) ?? "RUNNING"; + const containerId = await this.create( + { id: cluster.id!, shardIds: cluster.shardIds, desiredStatus }, { ...bot, totalShards: updatedBot.totalShards }, containerImage, - ), - ), - ); - - return Promise.all( - updated.map((cluster) => { - if (cluster.status === "STOPPED") return Promise.resolve(cluster); + ); - return this.start(cluster.id); + if (desiredStatus === "RUNNING") { + await DockerService.startContainer(containerId); + } }), ); + + return this.getAll(); } } From b877afbc2c1d91e2587bb4ba9889e0ad01e036a7 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:12:49 +0200 Subject: [PATCH 25/34] feat(clusters): report the cluster status observed in docker --- .../src/repositories/cluster.repository.ts | 21 +-- .../backend/src/schemas/cluster.schema.ts | 9 +- .../backend/src/services/cluster.service.ts | 171 ++++++++++++------ .../backend/src/services/docker.service.ts | 12 ++ 4 files changed, 139 insertions(+), 74 deletions(-) diff --git a/packages/backend/src/repositories/cluster.repository.ts b/packages/backend/src/repositories/cluster.repository.ts index 52b89be..a31c3d5 100644 --- a/packages/backend/src/repositories/cluster.repository.ts +++ b/packages/backend/src/repositories/cluster.repository.ts @@ -26,10 +26,6 @@ export default class ClusterRepository { }); } - public static async findFirstById(id: Cluster["id"]) { - return prisma.cluster.findFirst({ where: { id }, select: this.DEFAULT_SELECT }); - } - public static async findFirstBotIdById(id: Cluster["id"]) { return prisma.cluster.findFirst({ where: { @@ -79,10 +75,10 @@ export default class ClusterRepository { }); } - public static async findFirstContainerIdAndDesiredStatusById(id: Cluster["id"]) { - return prisma.cluster.findFirst({ - where: { id }, - select: { containerId: true, desiredStatus: true }, + public static async findManyWithContainerId() { + return prisma.cluster.findMany({ + select: { ...this.DEFAULT_SELECT, containerId: true }, + orderBy: { id: "asc" }, }); } @@ -90,15 +86,6 @@ export default class ClusterRepository { return prisma.cluster.findMany({ select: { id: true }, orderBy: { id: "asc" } }); } - public static async findManyIdAndContainerIdByDesiredStatus( - desiredStatus: Cluster["desiredStatus"], - ) { - return prisma.cluster.findMany({ - where: { desiredStatus }, - select: { id: true, containerId: true }, - }); - } - public static async findManyIdAndContainerId() { return prisma.cluster.findMany({ select: { id: true, containerId: true } }); } diff --git a/packages/backend/src/schemas/cluster.schema.ts b/packages/backend/src/schemas/cluster.schema.ts index 2841ea0..740626a 100644 --- a/packages/backend/src/schemas/cluster.schema.ts +++ b/packages/backend/src/schemas/cluster.schema.ts @@ -5,14 +5,17 @@ export const ClusterIdSchema = z.coerce.number().int().nonnegative().meta({ }); export type ClusterId = z.infer; +export const ClusterStatusSchema = z + .enum(["STARTING", "RUNNING", "STOPPED", "ERROR", "UPDATING"]) + .meta({ description: "Status of the cluster" }); +export type ClusterStatus = z.infer; + export const ClusterSchema = z.object({ id: ClusterIdSchema, shardIds: z.array(z.number().nonnegative()).meta({ description: "Shard IDs associated to that cluster", }), - status: z.enum(["STARTING", "RUNNING", "STOPPED", "ERROR", "UPDATING"]).meta({ - description: "Status of the cluster", - }), + status: ClusterStatusSchema, }); export type Cluster = z.infer; diff --git a/packages/backend/src/services/cluster.service.ts b/packages/backend/src/services/cluster.service.ts index 8957408..70490ea 100644 --- a/packages/backend/src/services/cluster.service.ts +++ b/packages/backend/src/services/cluster.service.ts @@ -9,18 +9,81 @@ import type { ClusterId, ClusterLogsQuery, ClustersBulkActionResult, + ClusterStatus, UpdateClusters, } from "#schemas/cluster.schema"; import DockerService from "./docker.service"; +type PendingStatus = Extract; + export default class ClusterService { - private static toCluster({ - id, - shardIds, - desiredStatus, - }: Pick): Cluster { - return { id, shardIds, status: desiredStatus }; + private static readonly pendingStatuses = new Map(); + + private static async track( + ids: ClusterId[], + status: PendingStatus, + operation: () => Promise, + ) { + for (const id of ids) { + this.pendingStatuses.set(id, status); + } + + try { + return await operation(); + } finally { + for (const id of ids) { + this.pendingStatuses.delete(id); + } + } + } + + private static status( + desiredStatus: ClusterRow["desiredStatus"], + containerState: string | undefined, + pendingStatus: PendingStatus | undefined, + ): ClusterStatus { + if (pendingStatus !== undefined) { + return pendingStatus; + } + + switch (containerState) { + case undefined: + return "ERROR"; + case "running": + return "RUNNING"; + case "restarting": + return "STARTING"; + default: + return desiredStatus === "RUNNING" ? "ERROR" : "STOPPED"; + } + } + + private static async findManyObserved() { + const clusters = await ClusterRepository.findManyWithContainerId(); + const containerStates = await DockerService.containerStates( + clusters.map(({ containerId }) => containerId), + ); + + return clusters.map(({ id, shardIds, desiredStatus, containerId }) => ({ + id, + shardIds, + containerId, + status: this.status( + desiredStatus, + containerStates.get(containerId), + this.pendingStatuses.get(id), + ), + })); + } + + private static async findObserved(id: ClusterId) { + const cluster = (await this.findManyObserved()).find((cluster) => cluster.id === id); + if (cluster === undefined) { + throw new NotFoundException({ message: `Cluster ${id} not found` }); + } + + return cluster; } private static async create( @@ -58,23 +121,18 @@ export default class ClusterService { } private static async findRunningContainerId(id: ClusterId) { - const cluster = await ClusterRepository.findFirstContainerIdAndDesiredStatusById(id); - if (cluster === null) { - throw new NotFoundException({ message: `Cluster ${id} not found` }); - } - - if (cluster.desiredStatus !== "RUNNING") { + const cluster = await this.findObserved(id); + if (cluster.status !== "RUNNING") { throw new BadRequestException({ message: `Cluster ${id} is not running` }); } return cluster.containerId; } - public static async get(id: ClusterId) { - const cluster = await ClusterRepository.findFirstById(id); - if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + public static async get(id: ClusterId): Promise { + const { shardIds, status } = await this.findObserved(id); - return this.toCluster(cluster); + return { id, shardIds, status }; } public static async logs(id: ClusterId, options: ClusterLogsQuery) { @@ -111,7 +169,7 @@ export default class ClusterService { } public static async getAllStats() { - const clusters = await ClusterRepository.findManyIdAndContainerIdByDesiredStatus("RUNNING"); + const clusters = (await this.findManyObserved()).filter(({ status }) => status === "RUNNING"); const results = await Promise.allSettled( clusters.map( async ({ id, containerId }) => @@ -134,8 +192,10 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await ClusterRepository.updateDesiredStatusById("RUNNING", id); - await DockerService.startContainer(cluster.containerId); + await this.track([id], "STARTING", async () => { + await ClusterRepository.updateDesiredStatusById("RUNNING", id); + await DockerService.startContainer(cluster.containerId); + }); return this.get(id); } @@ -154,8 +214,10 @@ export default class ClusterService { const cluster = await ClusterRepository.findFirstContainerIdById(id); if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - await ClusterRepository.updateDesiredStatusById("RUNNING", id); - await DockerService.restartContainer(cluster.containerId); + await this.track([id], "STARTING", async () => { + await ClusterRepository.updateDesiredStatusById("RUNNING", id); + await DockerService.restartContainer(cluster.containerId); + }); return this.get(id); } @@ -190,23 +252,20 @@ export default class ClusterService { }; } - public static async remove(id: ClusterId) { - const cluster = await ClusterRepository.findFirstContainerIdById(id); - if (cluster === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); - - await DockerService.stopContainer(cluster.containerId); - await DockerService.removeContainer(cluster.containerId); + public static async remove(id: ClusterId): Promise { + const { shardIds, status, containerId } = await this.findObserved(id); - const removed = await ClusterRepository.deleteById(id); - if (removed === null) throw new NotFoundException({ message: `Cluster ${id} not found` }); + await DockerService.stopContainer(containerId); + await DockerService.removeContainer(containerId); + await ClusterRepository.deleteById(id); - return this.toCluster(removed); + return { id, shardIds, status }; } - public static async getAll() { - const clusters = await ClusterRepository.findMany(); + public static async getAll(): Promise { + const clusters = await this.findManyObserved(); - return clusters.map((cluster) => this.toCluster(cluster)); + return clusters.map(({ id, shardIds, status }) => ({ id, shardIds, status })); } public static async stopAll() { @@ -281,29 +340,33 @@ export default class ClusterService { throw new NotFoundException({ message: "Container image not found" }); await DockerService.ensureImage(containerImage); - await this.stopAll(); - await this.removeAll(); - const updatedBot = await BotRepository.updateTotalShards({ totalShards: shards.length }); - if (updatedBot === null) { - throw new NotFoundException({ message: "Bot not found" }); - } + const affectedIds = [...new Set([...previousIds, ...clusters.map(({ id }) => id!)])]; + await this.track(affectedIds, "UPDATING", async () => { + await this.stopAll(); + await this.removeAll(); - const desiredStatuses = new Map(previous.map(({ id, desiredStatus }) => [id, desiredStatus])); - await Promise.all( - clusters.map(async (cluster) => { - const desiredStatus = desiredStatuses.get(cluster.id!) ?? "RUNNING"; - const containerId = await this.create( - { id: cluster.id!, shardIds: cluster.shardIds, desiredStatus }, - { ...bot, totalShards: updatedBot.totalShards }, - containerImage, - ); - - if (desiredStatus === "RUNNING") { - await DockerService.startContainer(containerId); - } - }), - ); + const updatedBot = await BotRepository.updateTotalShards({ totalShards: shards.length }); + if (updatedBot === null) { + throw new NotFoundException({ message: "Bot not found" }); + } + + const desiredStatuses = new Map(previous.map(({ id, desiredStatus }) => [id, desiredStatus])); + await Promise.all( + clusters.map(async (cluster) => { + const desiredStatus = desiredStatuses.get(cluster.id!) ?? "RUNNING"; + const containerId = await this.create( + { id: cluster.id!, shardIds: cluster.shardIds, desiredStatus }, + { ...bot, totalShards: updatedBot.totalShards }, + containerImage, + ); + + if (desiredStatus === "RUNNING") { + await DockerService.startContainer(containerId); + } + }), + ); + }); return this.getAll(); } diff --git a/packages/backend/src/services/docker.service.ts b/packages/backend/src/services/docker.service.ts index 64b10e9..d0b22ee 100644 --- a/packages/backend/src/services/docker.service.ts +++ b/packages/backend/src/services/docker.service.ts @@ -142,6 +142,18 @@ export default class DockerService { await this.pullImage(image); } + public static async containerStates(containerIds: string[]) { + if (containerIds.length === 0) { + return new Map(); + } + + const containers = await this.call( + this.containers.list({ all: true, filters: { id: containerIds } }), + ); + + return new Map(containers.map(({ Id, State }) => [Id, State])); + } + public static async create(...options: MethodParameters) { return this.call(this.containers.create(...options)); } From cf4724b526b5ee11268dda8f4e6a620e433fb59f Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:27:29 +0200 Subject: [PATCH 26/34] build(backend): bundle the hono server with tsdown for production --- docker/backend.Dockerfile | 22 +- packages/backend/package.json | 9 +- packages/backend/tsdown.config.ts | 7 + pnpm-lock.yaml | 545 +++++++++++++++++++++++- secrets/backend/.env.production.example | 14 +- 5 files changed, 565 insertions(+), 32 deletions(-) create mode 100644 packages/backend/tsdown.config.ts diff --git a/docker/backend.Dockerfile b/docker/backend.Dockerfile index 7fae537..103047c 100644 --- a/docker/backend.Dockerfile +++ b/docker/backend.Dockerfile @@ -6,7 +6,7 @@ WORKDIR /app ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable +RUN npm install --global pnpm@12.6.0 COPY package.json \ pnpm-lock.yaml \ @@ -16,15 +16,6 @@ COPY package.json \ # Required by pnpm-workspace.yaml `patchedDependencies` COPY patches/ patches/ -# Install packages for Prisma -COPY packages/prisma-client/package.json \ - packages/prisma-client/ -RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ - pnpm install \ - --filter=@hallmaster/prisma-client \ - --prefer-offline \ - --frozen-lockfile - # Install packages for the backend COPY packages/backend/package.json packages/backend/ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \ @@ -38,10 +29,9 @@ COPY packages/backend/prisma/ packages/backend/prisma/ COPY packages/backend/prisma.config.ts packages/backend/ RUN pnpm --filter=@hallmaster/backend prisma:generate -# Build NestJS application -COPY packages/backend/tsconfig.build.json \ - packages/backend/tsconfig.json \ - packages/backend/nest-cli.json \ +# Build Hono application +COPY packages/backend/tsconfig.json \ + packages/backend/tsdown.config.ts \ packages/backend/ COPY packages/backend/src/ packages/backend/src/ RUN pnpm --filter=@hallmaster/backend build @@ -49,8 +39,8 @@ RUN pnpm --filter=@hallmaster/backend build # Cleanup the application RUN rm -rf packages/backend/src \ packages/backend/tsconfig.json \ - packages/backend/tsconfig.build.json \ - packages/backend/nest-cli.json + packages/backend/tsdown.config.ts \ + packages/backend/prisma/generated # Create the application package RUN pnpm deploy --filter=@hallmaster/backend --prod --legacy /prod-app diff --git a/packages/backend/package.json b/packages/backend/package.json index 1f860ab..0c3f2f7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -3,14 +3,16 @@ "type": "module", "scripts": { "dev": "tsx watch src/index.ts", - "build": "tsc", - "start": "node dist/index.js", + "build": "tsc && tsdown", + "start": "node dist/index.mjs", + "start:prod": "prisma migrate deploy && node dist/index.mjs", "lint": "oxlint", "lint:fix": "oxlint --fix", "format": "oxfmt", "format:check": "oxfmt --check", "prisma:dev": "prisma dev", "prisma:migrate": "prisma migrate dev", + "prisma:deploy": "prisma migrate deploy", "prisma:reset": "prisma migrate reset", "prisma:generate": "prisma generate" }, @@ -26,6 +28,7 @@ "hono": "^4.13.9", "hono-openapi": "^1.3.3", "pg": "^8.23.0", + "prisma": "^7.10.0", "zod": "^4.6.5" }, "devDependencies": { @@ -34,7 +37,7 @@ "oxfmt": "^0.70.0", "oxlint": "^1.85.0", "oxlint-tsgolint": "^7.0.2003", - "prisma": "^7.10.0", + "tsdown": "^0.23.1", "tsx": "^4.23.0", "typescript": "^7.0.2" } diff --git a/packages/backend/tsdown.config.ts b/packages/backend/tsdown.config.ts new file mode 100644 index 0000000..1b1b3eb --- /dev/null +++ b/packages/backend/tsdown.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: "src/index.ts", + platform: "node", + target: "node24", +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e263bf3..4ba6f79 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,6 +217,9 @@ importers: pg: specifier: ^8.23.0 version: 8.23.0 + prisma: + specifier: ^7.10.0 + version: 7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) zod: specifier: ^4.6.5 version: 4.6.5 @@ -236,9 +239,9 @@ importers: oxlint-tsgolint: specifier: ^7.0.2003 version: 7.0.2003 - prisma: - specifier: ^7.10.0 - version: 7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) + tsdown: + specifier: ^0.23.1 + version: 0.23.1(tsx@4.23.15)(typescript@7.0.2) tsx: specifier: ^4.23.0 version: 4.23.15 @@ -884,6 +887,9 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oxc-project/types@0.153.0': + resolution: {integrity: sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==} + '@oxfmt/binding-android-arm-eabi@0.57.0': resolution: {integrity: sha512-qVBsEO+KugOsCmUHcO8iqNnqc65p7PCKpCs8M66mPZ+Ri+CWbcpoQOEJBg2OTu03+0qu++NK1jj6IzvQVs0Sig==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1502,6 +1508,9 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + '@quansync/fs@1.1.0': + resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} + '@radix-ui/primitive@1.1.3': resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} @@ -1576,6 +1585,105 @@ packages: '@types/react': optional: true + '@rolldown/binding-android-arm-eabi@1.2.13': + resolution: {integrity: sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.13': + resolution: {integrity: sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.13': + resolution: {integrity: sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.13': + resolution: {integrity: sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.13': + resolution: {integrity: sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.13': + resolution: {integrity: sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.13': + resolution: {integrity: sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.13': + resolution: {integrity: sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.13': + resolution: {integrity: sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.13': + resolution: {integrity: sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.13': + resolution: {integrity: sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.13': + resolution: {integrity: sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.13': + resolution: {integrity: sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.13': + resolution: {integrity: sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.13': + resolution: {integrity: sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@rollup/plugin-commonjs@29.0.2': resolution: {integrity: sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -2236,6 +2344,75 @@ packages: '@visx/vendor@4.0.0-alpha.0': resolution: {integrity: sha512-6I+MuqXBcv9jnlcVowHoHKSdk9gXTWkHLKyqBwRWg7LY6A3Ei8SHfubpqGV5rBUSppxMq2RszPJUS6w+H0YgmQ==} + '@yuku-core/binding-android-arm64@0.18.1': + resolution: {integrity: sha512-1XTUWTmnpwXNyPwRLSQDjvHu7WPgykzCniYtTk+5ppnriznVONWd9TRPcFxCR8dlZ7HFGtv8v1wzbQmtmVlHNQ==} + cpu: [arm64] + os: [android] + + '@yuku-core/binding-darwin-arm64@0.18.1': + resolution: {integrity: sha512-7A7IPB/kEzIlm9Lwj6qVQ3ZORUH96mpqDWgGxkjmaAWGAfV4roTZWN+o7bW22BzcW55xbUKcZRslSZ8WRyqdQw==} + cpu: [arm64] + os: [darwin] + + '@yuku-core/binding-darwin-x64@0.18.1': + resolution: {integrity: sha512-aBZgmsAf1u/ps042xE9pgH9zOY57Rn0K8BXOWjQ2dAIg4W5nyQ/Oo3U3+VAcaEeBt6Q/XCwnCUpshFpWQaOCrw==} + cpu: [x64] + os: [darwin] + + '@yuku-core/binding-freebsd-x64@0.18.1': + resolution: {integrity: sha512-yOEEjmEW2XQWpoa+4J3+eVMhXd/4kMTXoLJ9WkhXMMeRpKGBtFGjX2uxV6qKuoti3talOnmMdi2/kvqkX+O4wQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-core/binding-linux-arm-gnu@0.18.1': + resolution: {integrity: sha512-klpvuNubx19bsox50+ioEYfvwUj/+O6ML9YBDjzsz9PULffyOYxkR+i63BYvg//jAcEE9oHIQphm2Wz3vmk1TA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-core/binding-linux-arm-musl@0.18.1': + resolution: {integrity: sha512-eUG20Eh3Ycrm2D5WvRIn7BJp1+iPiFL0zLDdavVM5B6ymYEEmgrs6mime/h2y4Is1GA9uGyBBJQHaqKTcRiEng==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-core/binding-linux-arm64-gnu@0.18.1': + resolution: {integrity: sha512-og2/svnJ2Awjz4INj2lQh0KQMwy4FrVCtD5SgfoZNxdv5knG6XdIyIvZycRCeyk15AkHdhwJqCsHJFcB3tjTxg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-core/binding-linux-arm64-musl@0.18.1': + resolution: {integrity: sha512-dmkidtxAY3iSUOFAOwnp7w8nVx1s9QdSdyiJPVw0TkBlLhQySuQer3Pir3tmhIhHC+Oc2Bn0f0QQpFNAMvzTPQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-core/binding-linux-x64-gnu@0.18.1': + resolution: {integrity: sha512-eXAZacTOzJbzv6sT/4h03MBE4DNGObfuTg3L6+FGADFt8Sam7peyhaFAekQIwvLETX3WYXhMwTehKPB69sdmcA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-core/binding-linux-x64-musl@0.18.1': + resolution: {integrity: sha512-mdeItxbo5No5ht5XdHh0mCHDBne/STUuzphzGQQxVX+XSsBPbUHsZkmUAahycR6RoEP2UF/YBhg1M5f0phBO9A==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-core/binding-win32-arm64@0.18.1': + resolution: {integrity: sha512-0CyeB03Lr/QV9XHGyuChJ3V99X9M/6Q+3JB3ZRJWjqs13G1qIeIiHwsaCfqIJsqei9wxBtaKWZ1vBU7ApQfMEA==} + cpu: [arm64] + os: [win32] + + '@yuku-core/binding-win32-x64@0.18.1': + resolution: {integrity: sha512-p0zG4bkR4++W011YbcP7Thg0UunaX9Lfg6U3nHEEwLrvTNy4P0+I5GO762tdjLozZpZwWLV/HmYNeecyOS2y5g==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.18.1': + resolution: {integrity: sha512-rWtqVdjA8O34nV/4mBFIdUm5Zj+FIzZDR7P2PEpqXXp53VEHabTROu7j2RjzzJbLMVYjW6gDKwOgPgiKRA+x4w==} + '@zag-js/accordion@1.39.1': resolution: {integrity: sha512-GA3m7gRTm3weSe1eMlHIsTNztcjZ6joIaRgxxKil7q/UX0xIVVGDy0aCr6oo7FAuoMiOOBVurYXILpFZ30nOXA==} @@ -2454,6 +2631,10 @@ packages: magicast: optional: true + cac@7.0.1: + resolution: {integrity: sha512-86diuUyeeWM8CrqhpGCaDafrzjS1s++7sWy2AbAPoFNz/xKv7ID57CR4pZsMuGCh60PtsnEx4z68b+Vgt07VPw==} + engines: {node: '>=20.19.0'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -2737,6 +2918,15 @@ packages: engines: {node: '>=12'} hasBin: true + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + effect@3.20.0: resolution: {integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==} @@ -2750,6 +2940,10 @@ packages: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} + empathic@2.1.0: + resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} + engines: {node: '>=14'} + enhanced-resolve@5.21.6: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} @@ -2889,6 +3083,10 @@ packages: get-port-please@3.2.0: resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + get-tsconfig@5.0.0-beta.6: + resolution: {integrity: sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==} + engines: {node: '>=20.20.0'} + giget@3.3.1: resolution: {integrity: sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==} hasBin: true @@ -2936,6 +3134,9 @@ packages: resolution: {integrity: sha512-7dMkQmZoC4E6F7AtaQSPhlWAdnBti+j7rreMZl8QB4jFiEhP9TWbGWUMi8WYzBCgmgulxuvLQupKqo+Co6Omyg==} engines: {node: '>=16.9.0'} + hookable@6.1.2: + resolution: {integrity: sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==} + human-id@4.1.3: resolution: {integrity: sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==} hasBin: true @@ -2968,6 +3169,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + import-without-cache@0.4.1: + resolution: {integrity: sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==} + engines: {node: ^22.18.0 || >=24.0.0} + ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3253,6 +3458,10 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@3.0.0: + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + engines: {node: '>=12.20.0'} + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -3422,6 +3631,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -3487,6 +3700,9 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -3533,6 +3749,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} @@ -3553,6 +3772,33 @@ packages: robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + rolldown-plugin-dts@0.28.7: + resolution: {integrity: sha512-o0lA6/NPzBAhMFgnIVJgW4eMBbMBQ8sXEbq4GqEHloerWMLqLNatpFy5AlWpEKskFShWKNghMMI8naul9Lgo5Q==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} + peerDependencies: + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + '@vue/language-core': ~3.2.0 || ~3.3.0 + rolldown: ^1.2.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + '@typescript/native-preview': + optional: true + '@volar/typescript': + optional: true + '@vue/language-core': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.2.13: + resolution: {integrity: sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.60.0: resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -3706,10 +3952,18 @@ packages: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + tinypool@2.1.0: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} @@ -3726,6 +3980,44 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tsdown@0.23.1: + resolution: {integrity: sha512-MSC2WSOts3in95J334/VIS7KPfor3eCSvndJ6KdoVj9Aak9Kh1aNfz9ws5owhdukuTNSXRdfkPEzA/ihpDD6SA==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.23.1 + '@tsdown/exe': 0.23.1 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: '>=0.5.0' + unrun: '*' + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -3748,6 +4040,9 @@ packages: engines: {node: '>=16.20.0'} hasBin: true + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -3766,6 +4061,10 @@ packages: typescript: optional: true + verkit@0.5.0: + resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==} + engines: {node: '>=18.12.0'} + virtua@0.49.2: resolution: {integrity: sha512-aEp3+6cmIRjHUlQnWdgGXYMYtrIG26QnN9jJDZEE5LRhvo1Z9HzoJwLDgyVULUPWcSdCnZAroQm7raXJyTG0AA==} peerDependencies: @@ -3864,6 +4163,22 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yuku-ast@0.18.1: + resolution: {integrity: sha512-Y9YgmYeExF+13IOm4r9pTgmQiCVJtrhq014st78eAckWlCkbjzNd2AVrQHng5uiLz9j31O4sGYhuP3znSKJv9w==} + engines: {node: '>=14.18.0'} + + yuku-codegen@0.18.1: + resolution: {integrity: sha512-0tNgBfNHW6+tEBGEf1N20C6tDsFR9Vj344Ey01wgAMeP+j0zR5WBF8dyzOu5oA/n6Yz6YqV1ZcASdi0cin+q8A==} + engines: {node: '>=14.18.0'} + + yuku-core@0.18.1: + resolution: {integrity: sha512-8PEOCKEi7r3nPY5cF0ZtJJSQj0ftwd0GC4En8fDQIP3nxw5ysSDwM0RcEuyHBblowRb82ad9AsExpqnKXAtC1Q==} + engines: {node: '>=14.18.0'} + + yuku-parser@0.18.1: + resolution: {integrity: sha512-t5pANCF6k9rgJre7jk8DMAU4lEoI1oWWZgXk0fcvmCYQquFVWv0rf6Awwt56nnPiHb0E24dhtrpfgf9ChTCbrQ==} + engines: {node: '>=14.18.0'} + zeptomatch@2.1.0: resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} @@ -4428,6 +4743,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oxc-project/types@0.153.0': {} + '@oxfmt/binding-android-arm-eabi@0.57.0': optional: true @@ -4802,6 +5119,10 @@ snapshots: transitivePeerDependencies: - '@types/react-dom' + '@quansync/fs@1.1.0': + dependencies: + quansync: 1.0.0 + '@radix-ui/primitive@1.1.3': {} '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.11)(react@19.2.4)': @@ -4856,6 +5177,53 @@ snapshots: optionalDependencies: '@types/react': 19.2.11 + '@rolldown/binding-android-arm-eabi@1.2.13': + optional: true + + '@rolldown/binding-android-arm64@1.2.13': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.13': + optional: true + + '@rolldown/binding-darwin-x64@1.2.13': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.13': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.13': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.13': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.13': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.13': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.13': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.13': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.13': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.13': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.13': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.13': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + '@rollup/plugin-commonjs@29.0.2(rollup@4.60.0)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.60.0) @@ -5398,6 +5766,44 @@ snapshots: d3-time-format: 4.1.0 internmap: 2.0.3 + '@yuku-core/binding-android-arm64@0.18.1': + optional: true + + '@yuku-core/binding-darwin-arm64@0.18.1': + optional: true + + '@yuku-core/binding-darwin-x64@0.18.1': + optional: true + + '@yuku-core/binding-freebsd-x64@0.18.1': + optional: true + + '@yuku-core/binding-linux-arm-gnu@0.18.1': + optional: true + + '@yuku-core/binding-linux-arm-musl@0.18.1': + optional: true + + '@yuku-core/binding-linux-arm64-gnu@0.18.1': + optional: true + + '@yuku-core/binding-linux-arm64-musl@0.18.1': + optional: true + + '@yuku-core/binding-linux-x64-gnu@0.18.1': + optional: true + + '@yuku-core/binding-linux-x64-musl@0.18.1': + optional: true + + '@yuku-core/binding-win32-arm64@0.18.1': + optional: true + + '@yuku-core/binding-win32-x64@0.18.1': + optional: true + + '@yuku-toolchain/types@0.18.1': {} + '@zag-js/accordion@1.39.1': dependencies: '@zag-js/anatomy': 1.39.1 @@ -5795,6 +6201,8 @@ snapshots: pkg-types: 2.3.0 rc9: 3.1.0 + cac@7.0.1: {} + callsites@3.1.0: {} chardet@2.1.1: {} @@ -6040,6 +6448,8 @@ snapshots: dotenv@18.0.4: {} + dts-resolver@3.0.0: {} + effect@3.20.0: dependencies: '@standard-schema/spec': 1.1.0 @@ -6051,6 +6461,8 @@ snapshots: empathic@2.0.0: {} + empathic@2.1.0: {} + enhanced-resolve@5.21.6: dependencies: graceful-fs: 4.2.11 @@ -6179,6 +6591,10 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -6224,6 +6640,10 @@ snapshots: get-port-please@3.2.0: {} + get-tsconfig@5.0.0-beta.6: + dependencies: + resolve-pkg-maps: 1.0.0 + giget@3.3.1: {} git-raw-commits@4.0.0: @@ -6283,6 +6703,8 @@ snapshots: hono@4.13.9: {} + hookable@6.1.2: {} + human-id@4.1.3: {} husky@9.1.7: {} @@ -6308,6 +6730,8 @@ snapshots: import-meta-resolve@4.2.0: {} + import-without-cache@0.4.1: {} + ini@4.1.1: {} internmap@1.0.1: {} @@ -6545,6 +6969,8 @@ snapshots: obug@2.1.1: {} + obug@3.0.0: {} + ohash@2.0.11: {} openapi-types@12.1.3: {} @@ -6751,6 +7177,8 @@ snapshots: picomatch@4.0.3: {} + picomatch@4.0.7: {} + pify@4.0.1: {} pkg-types@2.3.0: @@ -6810,6 +7238,8 @@ snapshots: quansync@0.2.11: {} + quansync@1.0.0: {} + queue-microtask@1.2.3: {} rc9@3.1.0: @@ -6845,6 +7275,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -6859,6 +7291,41 @@ snapshots: robust-predicates@3.0.3: {} + rolldown-plugin-dts@0.28.7(rolldown@1.2.13)(typescript@7.0.2): + dependencies: + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.6 + obug: 3.0.0 + rolldown: 1.2.13 + yuku-ast: 0.18.1 + yuku-codegen: 0.18.1 + yuku-parser: 0.18.1 + optionalDependencies: + typescript: 7.0.2 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.2.13: + dependencies: + '@oxc-project/types': 0.153.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.13 + '@rolldown/binding-android-arm64': 1.2.13 + '@rolldown/binding-darwin-arm64': 1.2.13 + '@rolldown/binding-darwin-x64': 1.2.13 + '@rolldown/binding-freebsd-x64': 1.2.13 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.13 + '@rolldown/binding-linux-arm64-gnu': 1.2.13 + '@rolldown/binding-linux-arm64-musl': 1.2.13 + '@rolldown/binding-linux-ppc64-gnu': 1.2.13 + '@rolldown/binding-linux-s390x-gnu': 1.2.13 + '@rolldown/binding-linux-x64-gnu': 1.2.13 + '@rolldown/binding-linux-x64-musl': 1.2.13 + '@rolldown/binding-openharmony-arm64': 1.2.13 + '@rolldown/binding-win32-arm64-msvc': 1.2.13 + '@rolldown/binding-win32-x64-msvc': 1.2.13 + rollup@4.60.0: dependencies: '@types/estree': 1.0.8 @@ -7034,11 +7501,18 @@ snapshots: tinyexec@1.0.2: {} + tinyexec@1.3.1: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + tinypool@2.1.0: {} tinypool@2.1.2: {} @@ -7049,6 +7523,34 @@ snapshots: totalist@3.0.1: {} + tree-kill@1.2.2: {} + + tsdown@0.23.1(tsx@4.23.15)(typescript@7.0.2): + dependencies: + cac: 7.0.1 + defu: 6.1.7 + empathic: 2.1.0 + hookable: 6.1.2 + import-without-cache: 0.4.1 + obug: 3.0.0 + picomatch: 4.0.7 + rolldown: 1.2.13 + rolldown-plugin-dts: 0.28.7(rolldown@1.2.13)(typescript@7.0.2) + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + verkit: 0.5.0 + optionalDependencies: + tsx: 4.23.15 + typescript: 7.0.2 + transitivePeerDependencies: + - '@typescript/native-preview' + - '@volar/typescript' + - '@vue/language-core' + - oxc-resolver + - vue-tsc + tslib@2.8.1: {} tsx@4.23.15: @@ -7086,6 +7588,11 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.1.0 + quansync: 1.0.0 + undici-types@6.21.0: {} undici-types@8.9.0: {} @@ -7096,6 +7603,8 @@ snapshots: optionalDependencies: typescript: 7.0.2 + verkit@0.5.0: {} + virtua@0.49.2(patch_hash=2c0d482dc06ad381cab34a6d5581eefb4327fc24a8dd80dad7562bd8922f879c)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(svelte@5.56.4(@typescript-eslint/types@8.56.0)): optionalDependencies: react: 19.2.4 @@ -7152,6 +7661,36 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yuku-ast@0.18.1: + dependencies: + '@yuku-toolchain/types': 0.18.1 + + yuku-codegen@0.18.1: + dependencies: + '@yuku-toolchain/types': 0.18.1 + + yuku-core@0.18.1: + dependencies: + '@yuku-toolchain/types': 0.18.1 + optionalDependencies: + '@yuku-core/binding-android-arm64': 0.18.1 + '@yuku-core/binding-darwin-arm64': 0.18.1 + '@yuku-core/binding-darwin-x64': 0.18.1 + '@yuku-core/binding-freebsd-x64': 0.18.1 + '@yuku-core/binding-linux-arm-gnu': 0.18.1 + '@yuku-core/binding-linux-arm-musl': 0.18.1 + '@yuku-core/binding-linux-arm64-gnu': 0.18.1 + '@yuku-core/binding-linux-arm64-musl': 0.18.1 + '@yuku-core/binding-linux-x64-gnu': 0.18.1 + '@yuku-core/binding-linux-x64-musl': 0.18.1 + '@yuku-core/binding-win32-arm64': 0.18.1 + '@yuku-core/binding-win32-x64': 0.18.1 + + yuku-parser@0.18.1: + dependencies: + '@yuku-toolchain/types': 0.18.1 + yuku-core: 0.18.1 + zeptomatch@2.1.0: dependencies: grammex: 3.1.12 diff --git a/secrets/backend/.env.production.example b/secrets/backend/.env.production.example index 862319d..79f2a83 100644 --- a/secrets/backend/.env.production.example +++ b/secrets/backend/.env.production.example @@ -1,12 +1,6 @@ -DISCORD_BOT_TOKEN_ENV_NAME=DISCORD_BOT_TOKEN -TOTAL_SHARDS_ENV_NAME=TOTAL_SHARDS -SHARD_ID_LIST_ENV_NAME=SHARD_ID_LIST +DATABASE_URL=postgresql://pguser:pgpassword@hallmaster-database:5432/hallmaster -DATABASE_URL=postgresql://pguser:pgpassword@hallmaster-postgres:5432/hallmaster +# openssl rand -base64 32 +SECRET=XXX -# python3 -c "import random,string;print(''.join([random.choice(string.hexdigits[:16]) for _ in range(32)]))" -JWT_SECRET=XXX -JWT_EXPIRES_IN=12h # 15m, 1d, ... - -# This enables logging of all incoming requests -# LOG_REQUESTS=true +PORT=3000 From be337ff70fa927cbcdaca75c4501aca3d4408334 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:30:44 +0200 Subject: [PATCH 27/34] test(backend): add unit tests for the services --- packages/backend/.gitignore | 1 + packages/backend/.oxlintrc.json | 10 +- packages/backend/package.json | 9 +- .../backend/tests/unit/bot.service.test.ts | 105 ++ .../tests/unit/cluster.service.test.ts | 250 +++ .../backend/tests/unit/docker.service.test.ts | 171 +++ packages/backend/vitest.config.ts | 20 + pnpm-lock.yaml | 1356 ++++++++++++++++- pnpm-workspace.yaml | 3 + 9 files changed, 1905 insertions(+), 20 deletions(-) create mode 100644 packages/backend/tests/unit/bot.service.test.ts create mode 100644 packages/backend/tests/unit/cluster.service.test.ts create mode 100644 packages/backend/tests/unit/docker.service.test.ts create mode 100644 packages/backend/vitest.config.ts diff --git a/packages/backend/.gitignore b/packages/backend/.gitignore index 87f64d8..f5caa46 100644 --- a/packages/backend/.gitignore +++ b/packages/backend/.gitignore @@ -27,6 +27,7 @@ lerna-debug.log* # misc .DS_Store dist/ +coverage/ /.alchemy /.prisma-composer diff --git a/packages/backend/.oxlintrc.json b/packages/backend/.oxlintrc.json index 833bbad..ac776a7 100644 --- a/packages/backend/.oxlintrc.json +++ b/packages/backend/.oxlintrc.json @@ -17,5 +17,13 @@ "options": { "typeAware": true, "typeCheck": true - } + }, + "overrides": [ + { + "files": ["tests/**/*.ts"], + "rules": { + "typescript/unbound-method": "off" + } + } + ] } diff --git a/packages/backend/package.json b/packages/backend/package.json index 0c3f2f7..394fd61 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -10,6 +10,9 @@ "lint:fix": "oxlint --fix", "format": "oxfmt", "format:check": "oxfmt --check", + "test": "vitest run", + "test:unit": "vitest run --project unit", + "test:cov": "vitest run --coverage", "prisma:dev": "prisma dev", "prisma:migrate": "prisma migrate dev", "prisma:deploy": "prisma migrate deploy", @@ -32,13 +35,17 @@ "zod": "^4.6.5" }, "devDependencies": { + "@testcontainers/postgresql": "^12.2.0", "@types/node": "^26.1.1", "@types/pg": "^8.23.1", + "@vitest/coverage-v8": "^5.0.3", "oxfmt": "^0.70.0", "oxlint": "^1.85.0", "oxlint-tsgolint": "^7.0.2003", "tsdown": "^0.23.1", "tsx": "^4.23.0", - "typescript": "^7.0.2" + "typescript": "^7.0.2", + "vite-tsconfig-paths": "^6.1.1", + "vitest": "^5.0.3" } } diff --git a/packages/backend/tests/unit/bot.service.test.ts b/packages/backend/tests/unit/bot.service.test.ts new file mode 100644 index 0000000..138022d --- /dev/null +++ b/packages/backend/tests/unit/bot.service.test.ts @@ -0,0 +1,105 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import ContainerImageRepository from "#repositories/container-image.repository"; +import BotService from "#services/bot.service"; +import DockerService from "#services/docker.service"; + +vi.mock("#repositories/bot.repository", () => ({ default: {} })); +vi.mock("#repositories/cluster.repository", () => ({ default: {} })); + +vi.mock("#repositories/container-image.repository", () => ({ + default: { + create: vi.fn(), + findFirst: vi.fn(), + update: vi.fn(), + }, +})); + +vi.mock("#services/docker.service", () => ({ + default: { + ensureImage: vi.fn(), + }, +})); + +beforeEach(() => { + vi.resetAllMocks(); + vi.mocked(ContainerImageRepository.create).mockImplementation((data) => Promise.resolve(data)); + vi.mocked(ContainerImageRepository.update).mockImplementation((data) => Promise.resolve(data)); +}); + +describe("container image registration", () => { + it.each([ + ["alpine", "docker.io", "library/alpine", "latest"], + ["hallmaster-bot:dev", "docker.io", "library/hallmaster-bot", "dev"], + ["user/app:1.0", "docker.io", "user/app", "1.0"], + ["ghcr.io/org/app:2", "ghcr.io", "org/app", "2"], + ["localhost:5000/app", "localhost:5000", "app", "latest"], + ["localhost/app:edge", "localhost", "app", "edge"], + ])("normalizes %s", async (url, serverName, image, tag) => { + const registered = await BotService.createContainerImage({ url }); + + expect(registered.url).toBe(`${serverName}/${image}:${tag}`); + expect(DockerService.ensureImage).toHaveBeenCalledWith( + expect.objectContaining({ serverName, image, tag }), + ); + }); +}); + +describe("container image update", () => { + beforeEach(() => { + vi.mocked(ContainerImageRepository.findFirst).mockResolvedValue({ + serverName: "registry.example.com", + image: "bot", + tag: "1.0", + username: "deployer", + password: "secret", + }); + }); + + it("keeps the password on the same registry and user", async () => { + await BotService.updateContainerImage({ + url: "registry.example.com/bot:2.0", + username: "deployer", + }); + + expect(DockerService.ensureImage).toHaveBeenCalledWith( + expect.objectContaining({ tag: "2.0", username: "deployer", password: "secret" }), + ); + expect(ContainerImageRepository.update).toHaveBeenCalledWith( + expect.objectContaining({ username: "deployer", password: "secret" }), + ); + }); + + it("uses the new password when one is provided", async () => { + await BotService.updateContainerImage({ + url: "registry.example.com/bot:2.0", + username: "deployer", + password: "rotated", + }); + + expect(ContainerImageRepository.update).toHaveBeenCalledWith( + expect.objectContaining({ password: "rotated" }), + ); + }); + + it.each([ + ["another user", { url: "registry.example.com/bot:2.0", username: "someone" }, "someone"], + ["another registry", { url: "ghcr.io/org/bot:2.0", username: "deployer" }, "deployer"], + ["no user", { url: "registry.example.com/bot:2.0" }, null], + ])("drops the password for %s", async (_case, image, username) => { + await BotService.updateContainerImage(image); + + expect(ContainerImageRepository.update).toHaveBeenCalledWith( + expect.objectContaining({ username, password: null }), + ); + }); + + it("rejects the update when no image is registered", async () => { + vi.mocked(ContainerImageRepository.findFirst).mockResolvedValue(null); + + await expect(BotService.updateContainerImage({ url: "alpine" })).rejects.toMatchObject({ + status: 404, + }); + expect(DockerService.ensureImage).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/tests/unit/cluster.service.test.ts b/packages/backend/tests/unit/cluster.service.test.ts new file mode 100644 index 0000000..7135e18 --- /dev/null +++ b/packages/backend/tests/unit/cluster.service.test.ts @@ -0,0 +1,250 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import BotRepository from "#repositories/bot.repository"; +import ClusterRepository from "#repositories/cluster.repository"; +import ContainerImageRepository from "#repositories/container-image.repository"; +import ClusterService from "#services/cluster.service"; +import DockerService from "#services/docker.service"; + +vi.mock("#repositories/bot.repository", () => ({ + default: { + findFirstIdAndToken: vi.fn(), + updateTotalShards: vi.fn(), + }, +})); + +vi.mock("#repositories/cluster.repository", () => ({ + default: { + create: vi.fn(), + deleteManyById: vi.fn(), + findFirstContainerIdById: vi.fn(), + findMany: vi.fn(), + findManyId: vi.fn(), + findManyIdAndContainerId: vi.fn(), + findManyWithContainerId: vi.fn(), + updateDesiredStatusById: vi.fn(), + updateManyDesiredStatusById: vi.fn(), + }, +})); + +vi.mock("#repositories/container-image.repository", () => ({ + default: { + findFirst: vi.fn(), + }, +})); + +vi.mock("#services/docker.service", () => ({ + default: { + containerLogs: vi.fn(), + containerStates: vi.fn(), + create: vi.fn(), + ensureImage: vi.fn(), + removeContainer: vi.fn(), + startContainer: vi.fn(), + stopContainer: vi.fn(), + }, +})); + +type ObservedCluster = { + id: number; + desiredStatus: "RUNNING" | "STOPPED"; + containerState?: string; +}; + +function givenClusters(clusters: ObservedCluster[]) { + vi.mocked(ClusterRepository.findManyWithContainerId).mockResolvedValue( + clusters.map(({ id, desiredStatus }) => ({ + id, + shardIds: [id], + desiredStatus, + containerId: `container-${id}`, + })), + ); + vi.mocked(DockerService.containerStates).mockResolvedValue( + new Map( + clusters.flatMap(({ id, containerState }) => + containerState === undefined ? [] : [[`container-${id}`, containerState] as const], + ), + ), + ); + vi.mocked(ClusterRepository.findFirstContainerIdById).mockImplementation((id) => + Promise.resolve( + clusters.some((cluster) => cluster.id === id) ? { containerId: `container-${id}` } : null, + ), + ); +} + +beforeEach(() => { + vi.resetAllMocks(); +}); + +describe("cluster status", () => { + it.each([ + ["RUNNING", "running", "RUNNING"], + ["STOPPED", "running", "RUNNING"], + ["RUNNING", "restarting", "STARTING"], + ["RUNNING", "exited", "ERROR"], + ["STOPPED", "exited", "STOPPED"], + ["STOPPED", "created", "STOPPED"], + ["RUNNING", undefined, "ERROR"], + ["STOPPED", undefined, "ERROR"], + ] as const)( + "reports a cluster desired %s with a %s container as %s", + async (desiredStatus, containerState, expected) => { + givenClusters([{ id: 0, desiredStatus, containerState }]); + + const [cluster] = await ClusterService.getAll(); + + expect(cluster.status).toBe(expected); + }, + ); + + it("reports STARTING while a start is in progress", async () => { + givenClusters([{ id: 0, desiredStatus: "STOPPED", containerState: "exited" }]); + let finishStart = () => {}; + vi.mocked(DockerService.startContainer).mockReturnValue( + new Promise((resolve) => { + finishStart = () => resolve(undefined); + }), + ); + + const start = ClusterService.start(0); + await vi.waitFor(() => expect(DockerService.startContainer).toHaveBeenCalled()); + + expect((await ClusterService.get(0)).status).toBe("STARTING"); + + givenClusters([{ id: 0, desiredStatus: "RUNNING", containerState: "running" }]); + finishStart(); + + expect((await start).status).toBe("RUNNING"); + expect(ClusterRepository.updateDesiredStatusById).toHaveBeenCalledWith("RUNNING", 0); + }); + + it("rejects stats of a cluster that is not running", async () => { + givenClusters([{ id: 0, desiredStatus: "RUNNING", containerState: "exited" }]); + + await expect(ClusterService.stats(0)).rejects.toMatchObject({ status: 400 }); + }); + + it("rejects an unknown cluster", async () => { + givenClusters([]); + + await expect(ClusterService.get(42)).rejects.toMatchObject({ status: 404 }); + }); +}); + +describe("cluster actions on every cluster", () => { + it("reports which clusters succeeded and which failed", async () => { + givenClusters([ + { id: 0, desiredStatus: "RUNNING", containerState: "running" }, + { id: 1, desiredStatus: "RUNNING", containerState: "running" }, + ]); + vi.mocked(ClusterRepository.findManyId).mockResolvedValue([{ id: 0 }, { id: 1 }]); + vi.mocked(DockerService.startContainer).mockImplementation((containerId) => + containerId === "container-1" + ? Promise.reject(new Error("No such container")) + : Promise.resolve(), + ); + + const result = await ClusterService.runActionOnAll("start"); + + expect(result).toEqual({ + succeeded: [0], + failed: [{ id: 1, reason: "No such container" }], + }); + }); +}); + +describe("cluster logs", () => { + it("sends time bounds to docker in seconds", async () => { + givenClusters([{ id: 0, desiredStatus: "RUNNING", containerState: "running" }]); + + await ClusterService.logs(0, { + since: new Date("2026-01-01T00:00:00.400Z"), + until: new Date("2026-01-01T00:01:00.400Z"), + tail: "all", + }); + + expect(DockerService.containerLogs).toHaveBeenCalledWith( + "container-0", + expect.objectContaining({ since: 1767225600, until: 1767225661 }), + ); + }); +}); + +describe("cluster layout update", () => { + beforeEach(() => { + vi.mocked(BotRepository.findFirstIdAndToken).mockResolvedValue({ id: "bot", token: "token" }); + vi.mocked(BotRepository.updateTotalShards).mockImplementation(({ totalShards }) => + Promise.resolve({ id: "bot", totalShards }), + ); + vi.mocked(ContainerImageRepository.findFirst).mockResolvedValue({ + serverName: "docker.io", + image: "library/alpine", + tag: "latest", + username: null, + password: null, + }); + vi.mocked(DockerService.create).mockImplementation((_body, name) => + Promise.resolve({ Id: `container-${name}`, Warnings: [] }), + ); + vi.mocked(ClusterRepository.findMany).mockResolvedValue([ + { id: 0, shardIds: [0], desiredStatus: "STOPPED" }, + { id: 1, shardIds: [1], desiredStatus: "RUNNING" }, + ]); + vi.mocked(ClusterRepository.findManyIdAndContainerId).mockResolvedValue([ + { id: 0, containerId: "old-0" }, + { id: 1, containerId: "old-1" }, + ]); + givenClusters([]); + }); + + it.each([ + ["an empty cluster", [{ shardIds: [] }]], + [ + "a duplicated cluster", + [ + { id: 0, shardIds: [0] }, + { id: 0, shardIds: [1] }, + ], + ], + ["an unknown cluster", [{ id: 7, shardIds: [0] }]], + ["non-contiguous shards", [{ shardIds: [0] }, { shardIds: [2] }]], + ])("rejects %s", async (_case, layout) => { + await expect(ClusterService.updateAll(layout)).rejects.toMatchObject({ status: 400 }); + expect(DockerService.stopContainer).not.toHaveBeenCalled(); + }); + + it("keeps stopped clusters stopped and starts the others", async () => { + await ClusterService.updateAll([{ id: 0, shardIds: [0] }, { shardIds: [1, 2] }]); + + expect(ClusterRepository.create).toHaveBeenCalledWith( + expect.objectContaining({ id: 0, desiredStatus: "STOPPED", shardIds: [0] }), + ); + expect(ClusterRepository.create).toHaveBeenCalledWith( + expect.objectContaining({ id: 1, desiredStatus: "RUNNING", shardIds: [1, 2] }), + ); + expect(DockerService.startContainer).toHaveBeenCalledTimes(1); + expect(DockerService.startContainer).toHaveBeenCalledWith("container-bot-1"); + }); + + it("stores the total number of shards", async () => { + await ClusterService.updateAll([{ shardIds: [0, 1] }, { shardIds: [2] }]); + + expect(BotRepository.updateTotalShards).toHaveBeenCalledWith({ totalShards: 3 }); + expect(DockerService.create).toHaveBeenCalledWith( + expect.objectContaining({ Env: expect.arrayContaining(["TOTAL_SHARDS=3"]) }), + "bot-0", + ); + }); + + it("leaves the current clusters untouched when the image is unavailable", async () => { + vi.mocked(DockerService.ensureImage).mockRejectedValue(new Error("pull access denied")); + + await expect(ClusterService.updateAll([{ shardIds: [0] }])).rejects.toThrow( + "pull access denied", + ); + expect(DockerService.stopContainer).not.toHaveBeenCalled(); + expect(DockerService.removeContainer).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/backend/tests/unit/docker.service.test.ts b/packages/backend/tests/unit/docker.service.test.ts new file mode 100644 index 0000000..907e9dc --- /dev/null +++ b/packages/backend/tests/unit/docker.service.test.ts @@ -0,0 +1,171 @@ +import { Readable } from "node:stream"; + +import { DockerAPIHttpError } from "@hallmaster/docker.js"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import DockerService from "#services/docker.service"; + +const { containers, images } = vi.hoisted(() => ({ + containers: { + list: vi.fn(), + remove: vi.fn(), + stats: vi.fn(), + stop: vi.fn(), + }, + images: { + create: vi.fn(), + list: vi.fn(), + }, +})); + +vi.mock("@hallmaster/docker.js", () => ({ + DockerAPIHttpError: class extends Error { + constructor( + public readonly status: number, + public readonly reason?: string, + ) { + super(reason); + } + }, + DockerSocket: class { + async init() {} + }, + DockerContainersAPI: class { + constructor() { + return containers; + } + }, + DockerImagesAPI: class { + constructor() { + return images; + } + }, +})); + +function statsSample(overrides: { precpuSystem?: number } = {}) { + return { + cpu_stats: { + cpu_usage: { total_usage: 400 }, + system_cpu_usage: 2000, + online_cpus: 2, + }, + precpu_stats: { + cpu_usage: { total_usage: 200 }, + system_cpu_usage: overrides.precpuSystem ?? 1000, + online_cpus: 2, + }, + memory_stats: { usage: 1000, limit: 1600, stats: { inactive_file: 200 } }, + networks: { eth0: { rx_bytes: 10, tx_bytes: 20 } }, + }; +} + +beforeEach(() => { + vi.resetAllMocks(); +}); + +describe("container stats", () => { + it("computes cpu, memory and network usage", async () => { + containers.stats.mockResolvedValue(statsSample()); + + expect(await DockerService.containerStats("container")).toEqual({ + cpuPercentage: 40, + memory: { usage: 800, percentage: 50 }, + network: [{ interface: "eth0", transmitted: 20, received: 10 }], + }); + }); + + it("reports no cpu usage without a previous sample", async () => { + containers.stats.mockResolvedValue(statsSample({ precpuSystem: 0 })); + + expect((await DockerService.containerStats("container")).cpuPercentage).toBe(0); + }); + + it("accepts the empty stats of a stopped container", async () => { + containers.stats.mockResolvedValue({ memory_stats: {}, cpu_stats: {}, precpu_stats: {} }); + + expect(await DockerService.containerStats("container")).toEqual({ + cpuPercentage: 0, + memory: { usage: 0, percentage: 0 }, + network: [], + }); + }); + + it("rejects malformed stats", async () => { + containers.stats.mockResolvedValue({ memory_stats: { usage: "a lot" } }); + + await expect(DockerService.containerStats("container")).rejects.toMatchObject({ + status: 424, + }); + }); + + it("skips the first streamed sample, which has no previous cpu sample", async () => { + containers.stats.mockResolvedValue( + Readable.from([statsSample({ precpuSystem: 0 }), statsSample()]), + ); + + const stats = await (await DockerService.streamContainerStats("container")).toArray(); + + expect(stats).toHaveLength(1); + expect(stats[0]).toMatchObject({ cpuPercentage: 40 }); + }); +}); + +describe("docker engine errors", () => { + it.each([304, 404])("treats a %i on stop as already stopped", async (status) => { + containers.stop.mockRejectedValue(new DockerAPIHttpError(status)); + + await expect(DockerService.stopContainer("container")).resolves.toBeUndefined(); + }); + + it("reports other docker errors as a failed dependency", async () => { + containers.stop.mockRejectedValue(new DockerAPIHttpError(500, "boom")); + + await expect(DockerService.stopContainer("container")).rejects.toMatchObject({ + status: 424, + message: "Docker Engine API returned a 500 boom", + }); + }); + + it("reports unexpected failures as a failed dependency", async () => { + containers.remove.mockRejectedValue(new Error("socket hang up")); + + await expect(DockerService.removeContainer("container")).rejects.toMatchObject({ + status: 424, + }); + }); +}); + +describe("image availability", () => { + const image = { serverName: "docker.io", image: "library/alpine", tag: "3.20" }; + + it("does not pull an image that is already present", async () => { + images.list.mockResolvedValue([{ Id: "sha256:abc" }]); + + await DockerService.ensureImage(image); + + expect(images.list).toHaveBeenCalledWith({ filters: { reference: ["alpine:3.20"] } }); + expect(images.create).not.toHaveBeenCalled(); + }); + + it("pulls a missing image with the registry credentials", async () => { + images.list.mockResolvedValue([]); + images.create.mockResolvedValue(undefined); + + await DockerService.ensureImage({ + serverName: "registry.example.com", + image: "bot", + tag: "1.0", + username: "deployer", + password: "secret", + }); + + expect(images.list).toHaveBeenCalledWith({ + filters: { reference: ["registry.example.com/bot:1.0"] }, + }); + expect(images.create).toHaveBeenCalledWith({ + fromImage: "registry.example.com/bot", + tag: "1.0", + auth: { serveraddress: "registry.example.com", username: "deployer", password: "secret" }, + }); + }); +}); diff --git a/packages/backend/vitest.config.ts b/packages/backend/vitest.config.ts new file mode 100644 index 0000000..db52372 --- /dev/null +++ b/packages/backend/vitest.config.ts @@ -0,0 +1,20 @@ +import tsconfigPaths from "vite-tsconfig-paths"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + coverage: { + include: ["src/**/*.ts"], + }, + projects: [ + { + extends: true, + test: { + name: "unit", + include: ["tests/unit/**/*.test.ts"], + }, + }, + ], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ba6f79..6331648 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -198,7 +198,7 @@ importers: version: 7.10.0 '@prisma/client': specifier: ^7.4.1 - version: 7.4.1(prisma@7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2))(typescript@7.0.2) + version: 7.4.1(prisma@7.10.0(@types/react@19.2.11)(magicast@0.5.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2))(typescript@7.0.2) '@scalar/hono-api-reference': specifier: ^0.12.6 version: 0.12.6(hono@4.13.9) @@ -219,17 +219,23 @@ importers: version: 8.23.0 prisma: specifier: ^7.10.0 - version: 7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) + version: 7.10.0(@types/react@19.2.11)(magicast@0.5.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) zod: specifier: ^4.6.5 version: 4.6.5 devDependencies: + '@testcontainers/postgresql': + specifier: ^12.2.0 + version: 12.2.0 '@types/node': specifier: ^26.1.1 version: 26.6.3 '@types/pg': specifier: ^8.23.1 version: 8.23.1 + '@vitest/coverage-v8': + specifier: ^5.0.3 + version: 5.0.3(vitest@5.0.3) oxfmt: specifier: ^0.70.0 version: 0.70.0(svelte@5.56.4(@typescript-eslint/types@8.56.0)) @@ -248,6 +254,12 @@ importers: typescript: specifier: ^7.0.2 version: 7.0.2 + vite-tsconfig-paths: + specifier: ^6.1.1 + version: 6.1.1(typescript@7.0.2)(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) + vitest: + specifier: ^5.0.3 + version: 5.0.3(@types/node@26.6.3)(@vitest/coverage-v8@5.0.3)(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) packages/frontend: devDependencies: @@ -330,14 +342,38 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.28.6': resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@balena/dockerignore@1.0.2': + resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@changesets/apply-release-plan@7.0.14': resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} @@ -806,6 +842,20 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@grpc/grpc-js@1.14.6': + resolution: {integrity: sha512-SgC/B3/VZ8lTyOZ4Cwk6f1skWhmkNAaVfiX90qd5u1rRXGKk1//8FTUEN9GvOUq7rfWIqAYkA3CoyumYVpOS6g==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} + engines: {node: '>=6'} + hasBin: true + '@hallmaster/docker.js@0.0.28': resolution: {integrity: sha512-Nu3dALGsn3o6k4vcX7IRgFU/BXtZ+JZlBxEYuu2lbdaEoKL6Pu/Hq8BT3urSIVbl3usXNleXbiaHyCLCGxtJUA==} @@ -833,6 +883,10 @@ packages: '@internationalized/date@3.12.0': resolution: {integrity: sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -849,9 +903,18 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@kwsites/file-exists@1.1.1': + resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} + '@layerstack/svelte-actions@1.0.1-next.18': resolution: {integrity: sha512-gxPzCnJ1c9LTfWtRqLUzefCx+k59ZpxDUQ2XB+LokveZQPe7IDSOwHaBOEMlaGoGrtwc3Ft8dSZq+2WT2o9u/g==} @@ -1442,6 +1505,10 @@ packages: resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} engines: {node: '>=10'} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -1508,6 +1575,33 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + '@quansync/fs@1.1.0': resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} @@ -2107,6 +2201,12 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 + '@testcontainers/postgresql@12.2.0': + resolution: {integrity: sha512-14oNBbKdqIkWTiw8XwP6YqWnuBebiij8m4fF03WTSHvNt5FfvQpGTLwIV3p07UpDnqWRFvY50YFzbiYdB+SWuA==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -2152,6 +2252,15 @@ packages: '@types/d3-time@3.0.0': resolution: {integrity: sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/docker-modem@3.0.6': + resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==} + + '@types/dockerode@4.0.2': + resolution: {integrity: sha512-hA4myZ0XzwHG/p/eCLElYsQS3y1XZfvi6AfLFKVp1SNpPkO6J3xLatacAFVkReTxXRoIa3eHJmhJi9w6I3+zNA==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2167,6 +2276,9 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + '@types/node@22.19.11': resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} @@ -2182,6 +2294,15 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/ssh2-streams@0.1.13': + resolution: {integrity: sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==} + + '@types/ssh2@0.5.52': + resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==} + + '@types/ssh2@1.15.6': + resolution: {integrity: sha512-oGdxhBqcRTwSTKFm+9EiKzkNVYRLEFkcW44lhguvBalGJbWfGnDt/ezwSUZc+SF9m9bMc3VyklNAtp7zICjS5w==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -2344,6 +2465,37 @@ packages: '@visx/vendor@4.0.0-alpha.0': resolution: {integrity: sha512-6I+MuqXBcv9jnlcVowHoHKSdk9gXTWkHLKyqBwRWg7LY6A3Ei8SHfubpqGV5rBUSppxMq2RszPJUS6w+H0YgmQ==} + '@vitest/coverage-v8@5.0.3': + resolution: {integrity: sha512-+klsyz7BvT1vCU28Zkfzms1Ia78XD0V41U3FUtRaa3S+vOr/EXvK1O6BvVD7l1RzEjm6SONR2aybOvDDf9u0mQ==} + peerDependencies: + '@vitest/browser': 5.0.3 + vitest: 5.0.3 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/istanbul-lib-coverage@1.0.2': + resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==} + engines: {node: '>=22'} + + '@vitest/istanbul-lib-report@1.0.2': + resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} + engines: {node: '>=22'} + + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} + '@yuku-core/binding-android-arm64@0.18.1': resolution: {integrity: sha512-1XTUWTmnpwXNyPwRLSQDjvHu7WPgykzCniYtTk+5ppnriznVONWd9TRPcFxCR8dlZ7HFGtv8v1wzbQmtmVlHNQ==} cpu: [arm64] @@ -2560,6 +2712,10 @@ packages: '@zag-js/utils@1.39.1': resolution: {integrity: sha512-9k741cH7L655Ua3tedTkuMblcXVXVgCLTB9svp9oTjA7oatpOpYF4z43kgAQVjyThNXMJ7AvtO4C80ajQLTScg==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + acorn@8.16.0: resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} @@ -2576,10 +2732,26 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.4.0: + resolution: {integrity: sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + argon2@0.45.1: resolution: {integrity: sha512-skm+/WCjkGqCQxF7FG1LuZXM5yvbFjgbfiCGsud2oLgaDhh6b6dbH0b1EkghbM+xx4Bj8Ape+KKgixoIlWZicQ==} engines: {node: '>=16.17.0'} @@ -2601,6 +2773,22 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + asn1@0.2.6: + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.7: + resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} + + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + aws-ssl-profiles@1.1.2: resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} engines: {node: '>= 6.0.0'} @@ -2609,6 +2797,60 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} + b4a@1.9.0: + resolution: {integrity: sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.9.2: + resolution: {integrity: sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + bare-fs@4.8.4: + resolution: {integrity: sha512-MPC2eyrpbk3A873sK4y6rk+Iv4o2enxTjHZ8zH3HO0mjrpm5XjnguY2XmfidIsDV6vnjpbMBKUAJpV0ISD2SsQ==} + engines: {bare: '>=1.28.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-path@3.1.2: + resolution: {integrity: sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==} + + bare-stream@2.13.4: + resolution: {integrity: sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==} + peerDependencies: + bare-abort-controller: '*' + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + bare-buffer: + optional: true + bare-events: + optional: true + + bare-url@2.5.4: + resolution: {integrity: sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bcrypt-pbkdf@1.0.2: + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} + better-path-resolve@1.0.0: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} @@ -2616,13 +2858,37 @@ packages: better-result@2.10.0: resolution: {integrity: sha512-oQhh0y1qo2/ZKdAAEvHZAqKKiHOFU5k/bW96fE2ScgQOVkJRiHwB+nOS1SgFsYqRlxMDWvefXi9Q3px7QvgNDw==} + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + brace-expansion@2.1.7: + resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + buildcheck@0.0.7: + resolution: {integrity: sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==} + engines: {node: '>=10.0.0'} + + byline@5.0.0: + resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==} + engines: {node: '>=0.10.0'} + c12@3.3.4: resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} peerDependencies: @@ -2639,6 +2905,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} + engines: {node: '>=18'} + chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} @@ -2650,6 +2920,9 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -2685,6 +2958,10 @@ packages: compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + confbox@0.2.4: resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} @@ -2705,6 +2982,9 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig-typescript-loader@6.2.0: resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} engines: {node: '>=v18'} @@ -2722,6 +3002,19 @@ packages: typescript: optional: true + cpu-features@0.0.10: + resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==} + engines: {node: '>=10.0.0'} + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + cross-env@10.1.0: resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} engines: {node: '>=20'} @@ -2865,6 +3158,15 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + deepmerge-ts@7.1.5: resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} engines: {node: '>=16.0.0'} @@ -2905,6 +3207,18 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + docker-compose@1.5.0: + resolution: {integrity: sha512-O+eD6c63+BJORUWddXA7uAlI6H1KLDPd/aS14k73nXYpFliM48C3xrrKB3sXsR1Fp/rHUZQe7+fJ+QA7+jepyw==} + engines: {node: '>= 6.0.0'} + + docker-modem@5.0.7: + resolution: {integrity: sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==} + engines: {node: '>= 8.0'} + + dockerode@5.0.1: + resolution: {integrity: sha512-avsq/xk4YPIrn0CgleX5bjT9Y8IT1p9PxrNQ++RBQ2WEyFfHCTDsT9kmyxz+H/axnjAwg8wJWEIuPGOUuNupiA==} + engines: {node: '>= 14.17'} + dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -2927,6 +3241,9 @@ packages: oxc-resolver: optional: true + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + effect@3.20.0: resolution: {integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==} @@ -2936,6 +3253,9 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + empathic@2.0.0: resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} engines: {node: '>=14'} @@ -2944,6 +3264,9 @@ packages: resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} engines: {node: '>=14'} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + enhanced-resolve@5.21.6: resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} engines: {node: '>=10.13.0'} @@ -2963,6 +3286,9 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + es-toolkit@1.49.0: resolution: {integrity: sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==} @@ -2999,10 +3325,28 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + eventsource-parser@3.0.8: resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} engines: {node: '>=18.0.0'} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} @@ -3019,6 +3363,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -3057,6 +3404,9 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -3083,6 +3433,10 @@ packages: get-port-please@3.2.0: resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + get-tsconfig@5.0.0-beta.6: resolution: {integrity: sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==} engines: {node: '>=20.20.0'} @@ -3100,6 +3454,11 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} engines: {node: '>=18'} @@ -3112,6 +3471,9 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -3158,6 +3520,9 @@ packages: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -3173,6 +3538,9 @@ packages: resolution: {integrity: sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==} engines: {node: ^22.18.0 || >=24.0.0} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3227,6 +3595,10 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -3235,9 +3607,15 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -3246,6 +3624,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3275,6 +3656,10 @@ packages: peerDependencies: svelte: ^5.0.0 + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -3383,6 +3768,9 @@ packages: long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru.min@1.1.4: resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} @@ -3394,6 +3782,12 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.3: + resolution: {integrity: sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==} + + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} + memoize@10.2.0: resolution: {integrity: sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==} engines: {node: '>=18'} @@ -3418,9 +3812,29 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -3429,6 +3843,9 @@ packages: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mysql2@3.15.3: resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} engines: {node: '>= 8.0'} @@ -3437,6 +3854,9 @@ packages: resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} engines: {node: '>=8.0.0'} + nan@2.29.0: + resolution: {integrity: sha512-GlGk3HIvitbvs+LT3g6XUP1kpirKNvmDFwF/bmo6XNWSb/eYEs/O4bfgIEIXCZ+lIOTS5xNwDvSGMw6FJdAhtA==} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3455,9 +3875,17 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + obug@3.0.0: resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} engines: {node: '>=12.20.0'} @@ -3465,6 +3893,9 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} @@ -3551,6 +3982,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + package-manager-detector@0.2.11: resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} @@ -3573,6 +4007,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3688,12 +4126,30 @@ packages: typescript: optional: true + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + properties-reader@3.0.1: + resolution: {integrity: sha512-WPn+h9RGEExOKdu4bsF4HksG/uzd3cFq3MFtq8PsFeExPse5Ha/VOjQNyHhjboBFwGXGev6muJYTSPAOkROq2g==} + engines: {node: '>=18'} + + protobufjs@7.6.6: + resolution: {integrity: sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==} + engines: {node: '>=12.0.0'} + proxy-compare@3.0.1: resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} @@ -3722,6 +4178,20 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.8.0: + resolution: {integrity: sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -3826,6 +4296,12 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-regex2@5.0.0: resolution: {integrity: sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==} @@ -3883,6 +4359,9 @@ packages: spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + split-ca@1.0.1: + resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -3894,17 +4373,44 @@ packages: resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} engines: {node: '>= 0.6'} + ssh-remote-port-forward@1.0.4: + resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==} + + ssh2@1.17.0: + resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==} + engines: {node: '>=10.16.0'} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} + + streamx@2.28.1: + resolution: {integrity: sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_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==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -3939,6 +4445,22 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} + tar-fs@2.1.5: + resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==} + + tar-fs@3.1.3: + resolution: {integrity: sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.2.2: + resolution: {integrity: sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==} + + teex@1.0.1: + resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -3948,6 +4470,17 @@ packages: engines: {node: '>=10'} hasBin: true + testcontainers@12.2.0: + resolution: {integrity: sha512-Pd0c2eU0DdIuK+Pb3c+vmBUdKjDLgxZmPecQGESFAKyn5LTqSaSw8l7htfMlLwabb/QsUILe6XkxMJw8Ly/ThA==} + engines: {node: '>= 22.22'} + + text-decoder@1.2.7: + resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==} + + tinybench@6.2.2: + resolution: {integrity: sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==} + engines: {node: '>=20.0.0'} + tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -3972,6 +4505,14 @@ packages: resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} + engines: {node: '>=14.0.0'} + + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + engines: {node: '>=14.14'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3984,6 +4525,17 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + deprecated: unmaintained + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tsdown@0.23.1: resolution: {integrity: sha512-MSC2WSOts3in95J334/VIS7KPfor3eCSvndJ6KdoVj9Aak9Kh1aNfz9ws5owhdukuTNSXRdfkPEzA/ihpDD6SA==} engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} @@ -4026,6 +4578,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tweetnacl@0.14.5: + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} + type-fest@5.10.0: resolution: {integrity: sha512-NoSdpq/WEiAg5sjmBkmV/hfxv6HJH4NqPNrqjtSO5CwRmpsDfaf4begxW34KdJykH/l1yHtwBWQkCRdoXO8mPA==} engines: {node: '>=20'} @@ -4043,16 +4598,26 @@ packages: unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + undici@8.11.2: + resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} + engines: {node: '>=22.19.0'} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + valibot@1.4.2: resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} peerDependencies: @@ -4085,6 +4650,11 @@ packages: vue: optional: true + vite-tsconfig-paths@6.1.1: + resolution: {integrity: sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==} + peerDependencies: + vite: '*' + vite@7.3.5: resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4133,15 +4703,68 @@ packages: vite: optional: true + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 + happy-dom: '*' + jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} + hasBin: true + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -4185,6 +4808,10 @@ packages: zimmerframe@1.1.4: resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -4199,10 +4826,27 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.9': + dependencies: + '@babel/types': 7.29.8 + '@babel/runtime@7.28.6': {} + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@balena/dockerignore@1.0.2': {} + + '@bcoe/v8-coverage@1.0.2': {} + '@changesets/apply-release-plan@7.0.14': dependencies: '@changesets/config': 3.1.2 @@ -4641,16 +5285,35 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@hallmaster/docker.js@0.0.28': {} - - '@hono/node-server@2.1.1(hono@4.13.9)': + '@grpc/grpc-js@1.14.6': dependencies: - hono: 4.13.9 + '@grpc/proto-loader': 0.8.1 + '@js-sdsl/ordered-map': 4.4.2 - '@hono/standard-validator@0.4.0(@standard-schema/spec@1.1.0)(hono@4.13.9)': + '@grpc/proto-loader@0.7.15': dependencies: - '@standard-schema/spec': 1.1.0 - hono: 4.13.9 + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.6 + yargs: 17.7.2 + + '@grpc/proto-loader@0.8.1': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.6 + yargs: 17.7.2 + + '@hallmaster/docker.js@0.0.28': {} + + '@hono/node-server@2.1.1(hono@4.13.9)': + dependencies: + hono: 4.13.9 + + '@hono/standard-validator@0.4.0(@standard-schema/spec@1.1.0)(hono@4.13.9)': + dependencies: + '@standard-schema/spec': 1.1.0 + hono: 4.13.9 '@inquirer/external-editor@1.0.3(@types/node@26.6.3)': dependencies: @@ -4663,6 +5326,15 @@ snapshots: dependencies: '@swc/helpers': 0.5.18 + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.2.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4683,11 +5355,21 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@js-sdsl/ordered-map@4.4.2': {} + + '@kwsites/file-exists@1.1.1': + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@layerstack/svelte-actions@1.0.1-next.18': dependencies: '@floating-ui/dom': 1.7.6 @@ -5011,6 +5693,9 @@ snapshots: '@phc/format@1.0.0': {} + '@pkgjs/parseargs@0.11.0': + optional: true + '@polka/url@1.0.0-next.29': {} '@prisma/adapter-pg@7.10.0': @@ -5024,16 +5709,16 @@ snapshots: '@prisma/client-runtime-utils@7.4.1': {} - '@prisma/client@7.4.1(prisma@7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2))(typescript@7.0.2)': + '@prisma/client@7.4.1(prisma@7.10.0(@types/react@19.2.11)(magicast@0.5.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2))(typescript@7.0.2)': dependencies: '@prisma/client-runtime-utils': 7.4.1 optionalDependencies: - prisma: 7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) + prisma: 7.10.0(@types/react@19.2.11)(magicast@0.5.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2) typescript: 7.0.2 - '@prisma/config@7.10.0': + '@prisma/config@7.10.0(magicast@0.5.5)': dependencies: - c12: 3.3.4 + c12: 3.3.4(magicast@0.5.5) deepmerge-ts: 7.1.5 effect: 3.20.0 empathic: 2.0.0 @@ -5119,6 +5804,26 @@ snapshots: transitivePeerDependencies: - '@types/react-dom' + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.1': {} + + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.2': {} + '@quansync/fs@1.1.0': dependencies: quansync: 1.0.0 @@ -5551,6 +6256,20 @@ snapshots: tailwindcss: 4.3.1 vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) + '@testcontainers/postgresql@12.2.0': + dependencies: + testcontainers: 12.2.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/cookie@0.6.0': {} '@types/d3-array@3.0.3': {} @@ -5594,6 +6313,19 @@ snapshots: '@types/d3-time@3.0.0': {} + '@types/deep-eql@4.0.2': {} + + '@types/docker-modem@3.0.6': + dependencies: + '@types/node': 26.6.3 + '@types/ssh2': 1.15.6 + + '@types/dockerode@4.0.2': + dependencies: + '@types/docker-modem': 3.0.6 + '@types/node': 26.6.3 + '@types/ssh2': 1.15.6 + '@types/estree@1.0.8': {} '@types/geojson@7946.0.16': {} @@ -5604,6 +6336,10 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + '@types/node@22.19.11': dependencies: undici-types: 6.21.0 @@ -5624,6 +6360,19 @@ snapshots: '@types/resolve@1.20.2': {} + '@types/ssh2-streams@0.1.13': + dependencies: + '@types/node': 26.6.3 + + '@types/ssh2@0.5.52': + dependencies: + '@types/node': 26.6.3 + '@types/ssh2-streams': 0.1.13 + + '@types/ssh2@1.15.6': + dependencies: + '@types/node': 18.19.130 + '@types/trusted-types@2.0.7': {} '@typescript-eslint/types@8.56.0': @@ -5766,6 +6515,35 @@ snapshots: d3-time-format: 4.1.0 internmap: 2.0.3 + '@vitest/coverage-v8@5.0.3(vitest@5.0.3)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/istanbul-lib-coverage': 1.0.2 + '@vitest/istanbul-lib-report': 1.0.2 + ast-v8-to-istanbul: 1.0.7 + magicast: 0.5.5 + obug: 2.2.1 + std-env: 4.3.0 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@types/node@26.6.3)(@vitest/coverage-v8@5.0.3)(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) + + '@vitest/istanbul-lib-coverage@1.0.2': {} + + '@vitest/istanbul-lib-report@1.0.2': + dependencies: + '@vitest/istanbul-lib-coverage': 1.0.2 + + '@vitest/mocker@5.0.3(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0))': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 + estree-walker: 3.0.3 + magic-string: 1.4.3 + optionalDependencies: + vite: 7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) + + '@vitest/spy@5.0.3': {} + '@yuku-core/binding-android-arm64@0.18.1': optional: true @@ -6133,6 +6911,10 @@ snapshots: '@zag-js/utils@1.39.1': {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + acorn@8.16.0: {} ajv@8.18.0: @@ -6146,10 +6928,38 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.4.0: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansi-styles@6.2.3: {} + + archiver-utils@5.0.2: + dependencies: + glob: 10.5.0 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.23 + normalize-path: 3.0.0 + readable-stream: 4.8.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.8.0 + readdir-glob: 1.1.3 + tar-stream: 3.2.2 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + argon2@0.45.1: dependencies: '@phc/format': 1.0.0 @@ -6169,24 +6979,106 @@ snapshots: array-union@2.1.0: {} + asn1@0.2.6: + dependencies: + safer-buffer: 2.1.2 + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.7: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + async-lock@1.4.1: {} + + async@3.2.6: {} + aws-ssl-profiles@1.1.2: {} axobject-query@4.1.0: {} + b4a@1.9.0: {} + + balanced-match@1.0.2: {} + + bare-events@2.9.2: {} + + bare-fs@4.8.4: + dependencies: + bare-events: 2.9.2 + bare-path: 3.1.2 + bare-stream: 2.13.4(bare-events@2.9.2) + bare-url: 2.5.4 + fast-fifo: 1.3.2 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + bare-path@3.1.2: {} + + bare-stream@2.13.4(bare-events@2.9.2): + dependencies: + b4a: 1.9.0 + streamx: 2.28.1 + teex: 1.0.1 + optionalDependencies: + bare-events: 2.9.2 + transitivePeerDependencies: + - react-native-b4a + + bare-url@2.5.4: + dependencies: + bare-path: 3.1.2 + + base64-js@1.5.1: {} + + bcrypt-pbkdf@1.0.2: + dependencies: + tweetnacl: 0.14.5 + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 better-result@2.10.0: {} + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + brace-expansion@2.1.7: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 + buffer-crc32@1.0.0: {} + buffer-from@1.1.2: optional: true - c12@3.3.4: + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buildcheck@0.0.7: + optional: true + + byline@5.0.0: {} + + c12@3.3.4(magicast@0.5.5): dependencies: chokidar: 5.0.0 confbox: 0.2.4 @@ -6200,11 +7092,15 @@ snapshots: perfect-debounce: 2.1.0 pkg-types: 2.3.0 rc9: 3.1.0 + optionalDependencies: + magicast: 0.5.5 cac@7.0.1: {} callsites@3.1.0: {} + chai@6.3.0: {} + chardet@2.1.1: {} chokidar@4.0.3: @@ -6215,6 +7111,8 @@ snapshots: dependencies: readdirp: 5.1.1 + chownr@1.1.4: {} + ci-info@3.9.0: {} classnames@2.5.1: {} @@ -6245,6 +7143,14 @@ snapshots: array-ify: 1.0.0 dot-prop: 5.3.0 + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.8.0 + confbox@0.2.4: {} conventional-changelog-angular@8.1.0: @@ -6261,6 +7167,8 @@ snapshots: cookie@0.6.0: {} + core-util-is@1.0.3: {} + cosmiconfig-typescript-loader@6.2.0(@types/node@26.6.3)(cosmiconfig@9.0.0(typescript@7.0.2))(typescript@7.0.2): dependencies: '@types/node': 26.6.3 @@ -6277,6 +7185,19 @@ snapshots: optionalDependencies: typescript: 7.0.2 + cpu-features@0.0.10: + dependencies: + buildcheck: 0.0.7 + nan: 2.29.0 + optional: true + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.8.0 + cross-env@10.1.0: dependencies: '@epic-web/invariant': 1.0.0 @@ -6414,6 +7335,10 @@ snapshots: dargs@8.1.0: {} + debug@4.4.3: + dependencies: + ms: 2.1.3 + deepmerge-ts@7.1.5: {} deepmerge@4.3.1: {} @@ -6440,6 +7365,30 @@ snapshots: dependencies: path-type: 4.0.0 + docker-compose@1.5.0: + dependencies: + yaml: 2.9.0 + + docker-modem@5.0.7: + dependencies: + debug: 4.4.3 + readable-stream: 3.6.2 + split-ca: 1.0.1 + ssh2: 1.17.0 + transitivePeerDependencies: + - supports-color + + dockerode@5.0.1: + dependencies: + '@balena/dockerignore': 1.0.2 + '@grpc/grpc-js': 1.14.6 + '@grpc/proto-loader': 0.7.15 + docker-modem: 5.0.7 + protobufjs: 7.6.6 + tar-fs: 2.1.5 + transitivePeerDependencies: + - supports-color + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -6450,6 +7399,8 @@ snapshots: dts-resolver@3.0.0: {} + eastasianwidth@0.2.0: {} + effect@3.20.0: dependencies: '@standard-schema/spec': 1.1.0 @@ -6459,10 +7410,16 @@ snapshots: emoji-regex@8.0.0: {} + emoji-regex@9.2.2: {} + empathic@2.0.0: {} empathic@2.1.0: {} + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + enhanced-resolve@5.21.6: dependencies: graceful-fs: 4.2.11 @@ -6481,6 +7438,8 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-module-lexer@2.3.2: {} + es-toolkit@1.49.0: {} esbuild@0.27.3: @@ -6555,8 +7514,24 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + event-target-shim@5.0.1: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.9.2 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + eventsource-parser@3.0.8: {} + expect-type@1.4.0: {} + exsolve@1.0.8: {} extendable-error@0.1.7: {} @@ -6569,6 +7544,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-fifo@1.3.2: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6615,6 +7592,8 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + fs-constants@1.0.0: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -6640,6 +7619,8 @@ snapshots: get-port-please@3.2.0: {} + get-port@5.1.1: {} + get-tsconfig@5.0.0-beta.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -6656,6 +7637,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.9 + minipass: 7.1.3 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + global-directory@4.0.1: dependencies: ini: 4.1.1 @@ -6671,6 +7661,8 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + globrex@0.1.2: {} + graceful-fs@4.2.11: {} grammex@3.1.12: {} @@ -6721,6 +7713,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ieee754@1.2.1: {} + ignore@5.3.2: {} import-fresh@3.3.1: @@ -6732,6 +7726,8 @@ snapshots: import-without-cache@0.4.1: {} + inherits@2.0.4: {} + ini@4.1.1: {} internmap@1.0.1: {} @@ -6770,18 +7766,30 @@ snapshots: dependencies: '@types/estree': 1.0.8 + is-stream@2.0.1: {} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 is-windows@1.0.2: {} + isarray@1.0.0: {} + isexe@2.0.0: {} + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jiti@2.6.1: {} jiti@2.7.0: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -6839,6 +7847,10 @@ snapshots: - '@sveltejs/kit' - zod + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + lightningcss-android-arm64@1.32.0: optional: true @@ -6912,6 +7924,8 @@ snapshots: long@5.3.2: {} + lru-cache@10.4.3: {} + lru.min@1.1.4: {} lz-string@1.5.0: {} @@ -6920,6 +7934,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.4.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.5: + dependencies: + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + source-map-js: 1.2.1 + memoize@10.2.0: dependencies: mimic-function: 5.0.1 @@ -6937,12 +7961,28 @@ snapshots: mimic-function@5.0.1: {} + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.7 + + minimatch@9.0.9: + dependencies: + brace-expansion: 2.1.7 + minimist@1.2.8: {} + minipass@7.1.3: {} + + mkdirp-classic@0.5.3: {} + + mkdirp@3.0.1: {} + mri@1.2.0: {} mrmime@2.0.1: {} + ms@2.1.3: {} + mysql2@3.15.3: dependencies: aws-ssl-profiles: 1.1.2 @@ -6959,6 +7999,9 @@ snapshots: dependencies: lru.min: 1.1.4 + nan@2.29.0: + optional: true + nanoid@3.3.11: {} nanoid@5.1.16: {} @@ -6967,12 +8010,20 @@ snapshots: node-gyp-build@4.8.4: {} + normalize-path@3.0.0: {} + obug@2.1.1: {} + obug@2.2.1: {} + obug@3.0.0: {} ohash@2.0.11: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + openapi-types@12.1.3: {} outdent@0.5.0: {} @@ -7107,6 +8158,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} + package-manager-detector@0.2.11: dependencies: quansync: 0.2.11 @@ -7128,6 +8181,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + path-type@4.0.0: {} pathe@2.0.3: {} @@ -7209,9 +8267,9 @@ snapshots: prettier@2.8.8: {} - prisma@7.10.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2): + prisma@7.10.0(@types/react@19.2.11)(magicast@0.5.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@7.0.2): dependencies: - '@prisma/config': 7.10.0 + '@prisma/config': 7.10.0(magicast@0.5.5) '@prisma/dev': 0.24.17(typescript@7.0.2) '@prisma/engines': 7.10.0 '@prisma/studio-core': 0.33.0(@types/react@19.2.11)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -7226,14 +8284,44 @@ snapshots: - react - react-dom + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 retry: 0.12.0 signal-exit: 3.0.7 + properties-reader@3.0.1: + dependencies: + '@kwsites/file-exists': 1.1.1 + mkdirp: 3.0.1 + transitivePeerDependencies: + - supports-color + + protobufjs@7.6.6: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 26.6.3 + long: 5.3.2 + proxy-compare@3.0.1: {} + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + pure-rand@6.1.0: {} quansync@0.2.11: {} @@ -7261,6 +8349,34 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + 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@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.8.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + readdirp@4.1.2: {} readdirp@5.1.1: {} @@ -7377,6 +8493,10 @@ snapshots: dependencies: mri: 1.2.0 + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + safe-regex2@5.0.0: dependencies: ret: 0.5.0 @@ -7425,24 +8545,68 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + split-ca@1.0.1: {} + split2@4.2.0: {} sprintf-js@1.0.3: {} sqlstring@2.3.3: {} + ssh-remote-port-forward@1.0.4: + dependencies: + '@types/ssh2': 0.5.52 + ssh2: 1.17.0 + + ssh2@1.17.0: + dependencies: + asn1: 0.2.6 + bcrypt-pbkdf: 1.0.2 + optionalDependencies: + cpu-features: 0.0.10 + nan: 2.29.0 + std-env@3.10.0: {} + std-env@4.3.0: {} + + streamx@2.28.1: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.2.0 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.4.0 + strip-bom@3.0.0: {} supports-preserve-symlinks-flag@1.0.0: {} @@ -7489,6 +8653,51 @@ snapshots: tapable@2.3.3: {} + tar-fs@2.1.5: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.4 + tar-stream: 2.2.0 + + tar-fs@3.1.3: + dependencies: + pump: 3.0.4 + tar-stream: 3.2.2 + optionalDependencies: + bare-fs: 4.8.4 + bare-path: 3.1.2 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar-stream@3.2.2: + dependencies: + b4a: 1.9.0 + bare-fs: 4.8.4 + fast-fifo: 1.3.2 + streamx: 2.28.1 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + + teex@1.0.1: + dependencies: + streamx: 2.28.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + term-size@2.2.1: {} terser@5.46.0: @@ -7499,6 +8708,37 @@ snapshots: source-map-support: 0.5.21 optional: true + testcontainers@12.2.0: + dependencies: + '@balena/dockerignore': 1.0.2 + '@types/dockerode': 4.0.2 + archiver: 7.0.1 + async-lock: 1.4.1 + byline: 5.0.0 + debug: 4.4.3 + docker-compose: 1.5.0 + dockerode: 5.0.1 + get-port: 5.1.1 + proper-lockfile: 4.1.2 + properties-reader: 3.0.1 + ssh-remote-port-forward: 1.0.4 + tar-fs: 3.1.3 + tmp: 0.2.7 + undici: 8.11.2 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - react-native-b4a + - supports-color + + text-decoder@1.2.7: + dependencies: + b4a: 1.9.0 + transitivePeerDependencies: + - react-native-b4a + + tinybench@6.2.2: {} + tinyexec@1.0.2: {} tinyexec@1.3.1: {} @@ -7517,6 +8757,10 @@ snapshots: tinypool@2.1.2: {} + tinyrainbow@3.2.0: {} + + tmp@0.2.7: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -7525,6 +8769,10 @@ snapshots: tree-kill@1.2.2: {} + tsconfck@3.1.6(typescript@7.0.2): + optionalDependencies: + typescript: 7.0.2 + tsdown@0.23.1(tsx@4.23.15)(typescript@7.0.2): dependencies: cac: 7.0.1 @@ -7559,6 +8807,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tweetnacl@0.14.5: {} + type-fest@5.10.0: dependencies: tagged-tag: 1.0.0 @@ -7593,12 +8843,18 @@ snapshots: '@quansync/fs': 1.1.0 quansync: 1.0.0 + undici-types@5.26.5: {} + undici-types@6.21.0: {} undici-types@8.9.0: {} + undici@8.11.2: {} + universalify@0.1.2: {} + util-deprecate@1.0.2: {} + valibot@1.4.2(typescript@7.0.2): optionalDependencies: typescript: 7.0.2 @@ -7611,6 +8867,16 @@ snapshots: react-dom: 19.2.4(react@19.2.4) svelte: 5.56.4(@typescript-eslint/types@8.56.0) + vite-tsconfig-paths@6.1.1(typescript@7.0.2)(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@7.0.2) + vite: 7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + - typescript + vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0): dependencies: esbuild: 0.27.3 @@ -7628,26 +8894,74 @@ snapshots: tsx: 4.23.15 yaml: 2.9.0 + vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.60.0 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 26.6.3 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + terser: 5.46.0 + tsx: 4.23.15 + yaml: 2.9.0 + vitefu@1.1.1(vite@7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)): optionalDependencies: vite: 7.3.5(@types/node@22.19.11)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) + vitest@5.0.3(@types/node@26.6.3)(@vitest/coverage-v8@5.0.3)(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0)) + chai: 6.3.0 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.3 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.2.2 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + vite: 7.3.5(@types/node@26.6.3)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)(tsx@4.23.15)(yaml@2.9.0) + why-is-node-running: 3.2.1 + optionalDependencies: + '@types/node': 26.6.3 + '@vitest/coverage-v8': 5.0.3(vitest@5.0.3) + transitivePeerDependencies: + - msw + which@2.0.2: dependencies: isexe: 2.0.0 + why-is-node-running@3.2.1: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.2.0 + + wrappy@1.0.2: {} + xtend@4.0.2: {} y18n@5.0.8: {} - yaml@2.9.0: - optional: true + yaml@2.9.0: {} yargs-parser@21.1.1: {} @@ -7698,6 +9012,12 @@ snapshots: zimmerframe@1.1.4: {} + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.8.0 + zod@4.3.6: optional: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5aa3abe..6ed682a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,8 +7,11 @@ allowBuilds: '@scarf/scarf': true '@swc/core': true argon2: true + cpu-features: false esbuild: true prisma: true + protobufjs: false + ssh2: false unrs-resolver: false # virtua patch is frontend-only; unused when deploying the backend subset From 3d652a959860654efe1d8924da0e146ba2fa0fef Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:33:04 +0200 Subject: [PATCH 28/34] test(backend): add integration tests against postgres and docker --- packages/backend/package.json | 1 + .../backend/tests/integration/api.test.ts | 83 +++++++++ .../tests/integration/clusters.test.ts | 176 ++++++++++++++++++ .../backend/tests/integration/global-setup.ts | 26 +++ packages/backend/tests/integration/helpers.ts | 77 ++++++++ packages/backend/tests/integration/setup.ts | 4 + packages/backend/vitest.config.ts | 12 ++ 7 files changed, 379 insertions(+) create mode 100644 packages/backend/tests/integration/api.test.ts create mode 100644 packages/backend/tests/integration/clusters.test.ts create mode 100644 packages/backend/tests/integration/global-setup.ts create mode 100644 packages/backend/tests/integration/helpers.ts create mode 100644 packages/backend/tests/integration/setup.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 394fd61..3275f26 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -12,6 +12,7 @@ "format:check": "oxfmt --check", "test": "vitest run", "test:unit": "vitest run --project unit", + "test:integration": "vitest run --project integration", "test:cov": "vitest run --coverage", "prisma:dev": "prisma dev", "prisma:migrate": "prisma migrate dev", diff --git a/packages/backend/tests/integration/api.test.ts b/packages/backend/tests/integration/api.test.ts new file mode 100644 index 0000000..e4320f6 --- /dev/null +++ b/packages/backend/tests/integration/api.test.ts @@ -0,0 +1,83 @@ +import { beforeEach, describe, expect, it } from "vitest"; + +import { register, request, resetState } from "./helpers"; + +beforeEach(async () => { + await resetState(); +}); + +describe("authentication", () => { + it("registers a single user", async () => { + expect(await register()).toEqual(expect.any(String)); + + const second = await request("/auth/register", { + method: "POST", + body: { username: "other", password: "password123" }, + }); + + expect(second.status).toBe(409); + expect(await second.json()).toEqual({ message: "A user is already registered" }); + }); + + it("rejects a wrong password", async () => { + await register(); + + const response = await request("/auth/login", { + method: "POST", + body: { username: "admin", password: "wrong-password" }, + }); + + expect(response.status).toBe(401); + expect(await response.json()).toEqual({ message: "Invalid username or password" }); + }); + + it("rejects a request without a token", async () => { + const response = await request("/clusters"); + + expect(response.status).toBe(401); + expect(response.headers.get("WWW-Authenticate")).toContain("Bearer"); + expect(await response.json()).toEqual({ message: expect.any(String) }); + }); +}); + +describe("error responses", () => { + it("lists invalid fields without echoing the submitted values", async () => { + const response = await request("/auth/register", { + method: "POST", + body: { username: "admin", password: "short" }, + }); + const body = await response.text(); + + expect(response.status).toBe(400); + expect(JSON.parse(body)).toEqual({ + message: "Validation failed", + issues: [{ path: "password", message: expect.any(String) }], + }); + expect(body).not.toContain("short"); + }); + + it("reports invalid path parameters", async () => { + const token = await register(); + + const response = await request("/clusters/abc", { token }); + + expect(response.status).toBe(400); + expect(await response.json()).toMatchObject({ issues: [{ path: "id" }] }); + }); + + it("reports unknown routes as json", async () => { + const response = await request("/nope"); + + expect(response.status).toBe(404); + expect(await response.json()).toEqual({ message: "Route not found" }); + }); + + it("reports unknown clusters as json", async () => { + const token = await register(); + + const response = await request("/clusters/42", { token }); + + expect(response.status).toBe(404); + expect(await response.json()).toEqual({ message: "Cluster 42 not found" }); + }); +}); diff --git a/packages/backend/tests/integration/clusters.test.ts b/packages/backend/tests/integration/clusters.test.ts new file mode 100644 index 0000000..fcc499c --- /dev/null +++ b/packages/backend/tests/integration/clusters.test.ts @@ -0,0 +1,176 @@ +import { afterAll, beforeEach, describe, expect, it } from "vitest"; + +import { + containerState, + createBot, + register, + removeContainerOutsideHallMaster, + request, + resetState, + stopContainerOutsideHallMaster, +} from "./helpers"; + +type Cluster = { id: number; shardIds: number[]; status: string }; + +let token: string; + +async function call(path: string, init: { method?: string; body?: unknown } = {}) { + const response = await request(path, { ...init, token }); + + return { status: response.status, body: (await response.json()) as unknown }; +} + +async function applyLayout(layout: { id?: number; shardIds: number[] }[]) { + const { status, body } = await call("/clusters", { method: "PUT", body: layout }); + expect(status).toBe(200); + + return body as Cluster[]; +} + +afterAll(async () => { + await resetState(); +}); + +beforeEach(async () => { + await resetState(); + token = await register(); + await createBot(); + + const { status } = await call("/bot/container-image", { + method: "POST", + body: { url: "nginx:alpine" }, + }); + expect(status).toBe(201); +}); + +describe("cluster layout", () => { + it("starts every cluster of a new layout", async () => { + const clusters = await applyLayout([{ shardIds: [0] }, { shardIds: [1, 2] }]); + + expect(clusters).toEqual([ + { id: 0, shardIds: [0], status: "RUNNING" }, + { id: 1, shardIds: [1, 2], status: "RUNNING" }, + ]); + expect(await containerState(0)).toBe("running"); + expect(await containerState(1)).toBe("running"); + expect((await call("/bot")).body).toMatchObject({ shards: 3 }); + }); + + it("keeps a stopped cluster stopped when the layout changes", async () => { + await applyLayout([{ shardIds: [0] }]); + await call("/clusters/0/stop", { method: "POST" }); + + const clusters = await applyLayout([{ id: 0, shardIds: [0] }, { shardIds: [1] }]); + + expect(clusters).toEqual([ + { id: 0, shardIds: [0], status: "STOPPED" }, + { id: 1, shardIds: [1], status: "RUNNING" }, + ]); + }); + + it("recreates a cluster whose container was removed outside HallMaster", async () => { + await applyLayout([{ shardIds: [0] }]); + await removeContainerOutsideHallMaster(0); + + const clusters = await applyLayout([{ id: 0, shardIds: [0] }]); + + expect(clusters).toEqual([{ id: 0, shardIds: [0], status: "RUNNING" }]); + expect(await containerState(0)).toBe("running"); + }); +}); + +describe("cluster actions", () => { + beforeEach(async () => { + await applyLayout([{ shardIds: [0] }, { shardIds: [1] }]); + }); + + it("stops and starts a single cluster", async () => { + expect((await call("/clusters/0/stop", { method: "POST" })).body).toMatchObject({ + status: "STOPPED", + }); + expect(await containerState(0)).toBe("exited"); + expect(await containerState(1)).toBe("running"); + + expect((await call("/clusters/0/start", { method: "POST" })).body).toMatchObject({ + status: "RUNNING", + }); + expect(await containerState(0)).toBe("running"); + }); + + it("reports the failures of an action applied to every cluster", async () => { + await removeContainerOutsideHallMaster(1); + + const { body } = await call("/clusters/restart", { method: "POST" }); + + expect(body).toEqual({ + succeeded: [0], + failed: [{ id: 1, reason: expect.stringContaining("No such container") }], + }); + }); + + it("removes a running cluster", async () => { + const { status } = await call("/clusters/0", { method: "DELETE" }); + + expect(status).toBe(200); + expect(await containerState(0)).toBeUndefined(); + expect((await call("/clusters")).body).toEqual([{ id: 1, shardIds: [1], status: "RUNNING" }]); + }); + + it("removes the bot and its containers", async () => { + const { status } = await call("/bot", { method: "DELETE" }); + + expect(status).toBe(200); + expect(await containerState(0)).toBeUndefined(); + expect(await containerState(1)).toBeUndefined(); + }); +}); + +describe("observed cluster status", () => { + beforeEach(async () => { + await applyLayout([{ shardIds: [0] }]); + }); + + it("reports a container stopped outside HallMaster as an error", async () => { + await stopContainerOutsideHallMaster(0); + + expect((await call("/clusters/0")).body).toMatchObject({ status: "ERROR" }); + }); + + it("reports a removed container as an error", async () => { + await removeContainerOutsideHallMaster(0); + + expect((await call("/clusters/0")).body).toMatchObject({ status: "ERROR" }); + }); +}); + +describe("cluster monitoring", () => { + beforeEach(async () => { + await applyLayout([{ shardIds: [0] }]); + }); + + it("returns the stats of a running cluster", async () => { + const { status, body } = await call("/clusters/0/stats"); + + expect(status).toBe(200); + expect(body).toMatchObject({ + cpuPercentage: expect.any(Number), + memory: { usage: expect.any(Number), percentage: expect.any(Number) }, + }); + }); + + it("refuses the stats of a stopped cluster", async () => { + await call("/clusters/0/stop", { method: "POST" }); + + const { status, body } = await call("/clusters/0/stats"); + + expect(status).toBe(400); + expect(body).toEqual({ message: "Cluster 0 is not running" }); + }); + + it("returns the logs of a cluster", async () => { + const { status, body } = await call("/clusters/0/logs"); + + expect(status).toBe(200); + expect(body).toEqual(expect.any(Array)); + }); +}); diff --git a/packages/backend/tests/integration/global-setup.ts b/packages/backend/tests/integration/global-setup.ts new file mode 100644 index 0000000..c7edb31 --- /dev/null +++ b/packages/backend/tests/integration/global-setup.ts @@ -0,0 +1,26 @@ +import { execFileSync } from "node:child_process"; + +import { PostgreSqlContainer } from "@testcontainers/postgresql"; +import type { TestProject } from "vitest/node"; + +declare module "vitest" { + export interface ProvidedContext { + databaseUrl: string; + } +} + +export default async function setup(project: TestProject) { + const database = await new PostgreSqlContainer("postgres:17-alpine").start(); + const databaseUrl = database.getConnectionUri(); + + execFileSync("pnpm", ["exec", "prisma", "migrate", "deploy"], { + env: { ...process.env, DATABASE_URL: databaseUrl }, + stdio: "ignore", + }); + + project.provide("databaseUrl", databaseUrl); + + return async () => { + await database.stop(); + }; +} diff --git a/packages/backend/tests/integration/helpers.ts b/packages/backend/tests/integration/helpers.ts new file mode 100644 index 0000000..517aa53 --- /dev/null +++ b/packages/backend/tests/integration/helpers.ts @@ -0,0 +1,77 @@ +import { DockerContainersAPI, DockerSocket } from "@hallmaster/docker.js"; + +import prisma from "#lib/database"; + +import app from "../../src/app"; + +export const BOT_ID = "424242424242424242"; + +const socket = new DockerSocket(); +await socket.init(); +const containers = new DockerContainersAPI(socket); + +async function findContainer(clusterId: number) { + const name = `${BOT_ID}-${clusterId}`; + const matches = await containers.list({ all: true, filters: { name: [name] } }); + + return matches.find(({ Names }) => Names.includes(`/${name}`)); +} + +export async function containerState(clusterId: number) { + return (await findContainer(clusterId))?.State; +} + +export async function stopContainerOutsideHallMaster(clusterId: number) { + const container = await findContainer(clusterId); + if (container !== undefined) { + await containers.stop(container.Id); + } +} + +export async function removeContainerOutsideHallMaster(clusterId: number) { + const container = await findContainer(clusterId); + if (container !== undefined) { + await containers.stop(container.Id); + await containers.remove(container.Id); + } +} + +export async function resetState() { + await prisma.$executeRawUnsafe('TRUNCATE "User", "bot" CASCADE'); + + const leftovers = await containers.list({ all: true, filters: { name: [`${BOT_ID}-`] } }); + for (const container of leftovers) { + await containers.stop(container.Id); + await containers.remove(container.Id); + } +} + +export async function createBot() { + await prisma.bot.create({ data: { id: BOT_ID, token: "integration.token", totalShards: 0 } }); +} + +export function request( + path: string, + init: { method?: string; token?: string; body?: unknown } = {}, +) { + const headers = new Headers({ "Content-Type": "application/json" }); + if (init.token !== undefined) { + headers.set("Authorization", `Bearer ${init.token}`); + } + + return app.request(`/api${path}`, { + method: init.method ?? "GET", + headers, + body: init.body === undefined ? undefined : JSON.stringify(init.body), + }); +} + +export async function register() { + const response = await request("/auth/register", { + method: "POST", + body: { username: "admin", password: "password123" }, + }); + const { token } = (await response.json()) as { token: string }; + + return token; +} diff --git a/packages/backend/tests/integration/setup.ts b/packages/backend/tests/integration/setup.ts new file mode 100644 index 0000000..8b75fb5 --- /dev/null +++ b/packages/backend/tests/integration/setup.ts @@ -0,0 +1,4 @@ +import { inject } from "vitest"; + +process.env.DATABASE_URL = inject("databaseUrl"); +process.env.SECRET = "integration-secret"; diff --git a/packages/backend/vitest.config.ts b/packages/backend/vitest.config.ts index db52372..7118c7e 100644 --- a/packages/backend/vitest.config.ts +++ b/packages/backend/vitest.config.ts @@ -15,6 +15,18 @@ export default defineConfig({ include: ["tests/unit/**/*.test.ts"], }, }, + { + extends: true, + test: { + name: "integration", + include: ["tests/integration/**/*.test.ts"], + globalSetup: ["tests/integration/global-setup.ts"], + setupFiles: ["tests/integration/setup.ts"], + fileParallelism: false, + testTimeout: 60_000, + hookTimeout: 180_000, + }, + }, ], }, }); From 0aab207a7e18c01b9515f22cde010e37cd1a7451 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:33:16 +0200 Subject: [PATCH 29/34] ci(backend): check formatting and drop the removed prisma-client path --- .github/workflows/backend-ci.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/backend-ci.yaml b/.github/workflows/backend-ci.yaml index 0ea6005..04e029f 100644 --- a/.github/workflows/backend-ci.yaml +++ b/.github/workflows/backend-ci.yaml @@ -8,7 +8,6 @@ on: - "docker/backend.Dockerfile" - "secrets/backend/**" - "packages/backend/**" - - "packages/prisma-client/**" - ".github/workflows/backend-ci.yaml" pull_request: paths: *backend-paths @@ -43,7 +42,7 @@ jobs: run: pnpm --filter=@hallmaster/backend lint - name: Run format check - run: pnpm --filter=@hallmaster/backend format + run: pnpm --filter=@hallmaster/backend format:check - name: Test the build process run: pnpm --filter=@hallmaster/backend build From cec8dfdfa2591d6aeeadec73e2279e5097e70fb4 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:37:00 +0200 Subject: [PATCH 30/34] ci(backend): run unit and integration tests in separate steps --- .github/workflows/backend-ci.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/backend-ci.yaml b/.github/workflows/backend-ci.yaml index 04e029f..c15c10a 100644 --- a/.github/workflows/backend-ci.yaml +++ b/.github/workflows/backend-ci.yaml @@ -47,8 +47,11 @@ jobs: - name: Test the build process run: pnpm --filter=@hallmaster/backend build - - name: Run the tests - run: pnpm --filter=@hallmaster/backend test:cov + - name: Run the unit tests + run: pnpm --filter=@hallmaster/backend test:unit + + - name: Run the integration tests + run: pnpm --filter=@hallmaster/backend test:integration build-and-push: name: Build and Push Images From cf1aeb32214c68b286455091378df8febbd10ca2 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:40:03 +0200 Subject: [PATCH 31/34] build(pnpm): pin the exact pnpm version --- docker/backend.Dockerfile | 2 +- package.json | 2 +- pnpm-lock.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/backend.Dockerfile b/docker/backend.Dockerfile index 103047c..d8798b5 100644 --- a/docker/backend.Dockerfile +++ b/docker/backend.Dockerfile @@ -6,7 +6,7 @@ WORKDIR /app ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" -RUN npm install --global pnpm@12.6.0 +RUN corepack enable COPY package.json \ pnpm-lock.yaml \ diff --git a/package.json b/package.json index a5b5f55..1297c83 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ }, "packageManager": { "name": "pnpm", - "version": "^12.6.0" + "version": "12.6.0" } }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6331648..c72284f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,7 @@ importers: configDependencies: {} packageManagerDependencies: pnpm: - specifier: ^12.6.0 + specifier: 12.6.0 version: 12.6.0 packages: From 4bca8cd6767d71300acb36281b3961d5cfb362e9 Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:45:17 +0200 Subject: [PATCH 32/34] feat(backend): expose the api schemas and the recommended shards --- packages/backend/package.json | 3 + .../backend/src/controllers/bot.controller.ts | 57 ++++++++++++++++++- packages/backend/src/schemas/bot.schema.ts | 5 ++ .../backend/src/schemas/cluster.schema.ts | 4 ++ packages/backend/src/schemas/index.ts | 4 ++ packages/backend/src/services/bot.service.ts | 11 ++++ .../backend/tests/unit/bot.service.test.ts | 30 +++++++++- 7 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 packages/backend/src/schemas/index.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 3275f26..9644006 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,9 @@ { "name": "@hallmaster/backend", "type": "module", + "exports": { + "./schemas": "./src/schemas/index.ts" + }, "scripts": { "dev": "tsx watch src/index.ts", "build": "tsc && tsdown", diff --git a/packages/backend/src/controllers/bot.controller.ts b/packages/backend/src/controllers/bot.controller.ts index 7540f3b..887bddc 100644 --- a/packages/backend/src/controllers/bot.controller.ts +++ b/packages/backend/src/controllers/bot.controller.ts @@ -3,8 +3,13 @@ import { describeRoute, resolver } from "hono-openapi"; import authMiddlewares from "#middlewares/auth.middlware"; import validator from "#middlewares/validator.middleware"; -import { BotSchema, ContainerImageSchema, CreateBotSchema } from "#schemas/bot.schema"; -import { DiscordUserSchema } from "#schemas/discord.schema"; +import { + BotDiscordProfileSchema, + BotSchema, + ContainerImageSchema, + CreateBotSchema, + RecommendedShardsSchema, +} from "#schemas/bot.schema"; import { ApiErrorSchema } from "#schemas/error.schema"; import BotService from "#services/bot.service"; @@ -167,7 +172,7 @@ const botController = new Hono() 200: { description: "OK", content: { - "application/json": { schema: resolver(DiscordUserSchema) }, + "application/json": { schema: resolver(BotDiscordProfileSchema) }, }, }, 404: { @@ -204,6 +209,52 @@ const botController = new Hono() return c.json(profile, 200); }, ) + .get( + "/recommended-shards", + describeRoute({ + tags, + description: "Get the number of shards recommended by Discord", + responses: { + 200: { + description: "OK", + content: { + "application/json": { schema: resolver(RecommendedShardsSchema) }, + }, + }, + 404: { + description: "Not Found", + content: { + "application/json": { + schema: resolver(ApiErrorSchema), + example: { message: "Bot not found" }, + }, + }, + }, + 424: { + description: "Failed Dependency", + content: { + "application/json": { + schema: resolver(ApiErrorSchema), + examples: { + "Unreachable or timed out": { + value: { message: "Discord API is unreachable or timed out" }, + }, + "Invalid token": { value: { message: "Invalid Discord bot token" } }, + Unexpected: { + value: { message: "Discord API returned an unexpected response body" }, + }, + }, + }, + }, + }, + }, + }), + async (c) => { + const recommendedShards = await BotService.recommendedShards(); + + return c.json(recommendedShards, 200); + }, + ) .get( "/container-image", describeRoute({ diff --git a/packages/backend/src/schemas/bot.schema.ts b/packages/backend/src/schemas/bot.schema.ts index b9f321c..2143931 100644 --- a/packages/backend/src/schemas/bot.schema.ts +++ b/packages/backend/src/schemas/bot.schema.ts @@ -68,6 +68,11 @@ export const BotSchema = z.object({ }); export type Bot = z.infer; +export const RecommendedShardsSchema = BotSchema.pick({ shards: true }).meta({ + description: "Number of shards recommended by Discord", +}); +export type RecommendedShards = z.infer; + // export const UpdateBotSchema = CreateBotSchema.extend({ // layout: BotClusterLayoutSchema.meta({ description: "Cluster layout" }), // }); diff --git a/packages/backend/src/schemas/cluster.schema.ts b/packages/backend/src/schemas/cluster.schema.ts index 740626a..762c920 100644 --- a/packages/backend/src/schemas/cluster.schema.ts +++ b/packages/backend/src/schemas/cluster.schema.ts @@ -64,11 +64,13 @@ export const ClusterStatsSchema = z.object({ }), ), }); +export type ClusterStats = z.infer; export const ClustersStatsSchema = z.record(ClusterIdSchema, ClusterStatsSchema).meta({ description: "Stats of every running clusters keyed by ID (clusters whose stats failed to fetch are omitted)", }); +export type ClustersStats = z.infer; export const ClustersBulkActionResultSchema = z.object({ succeeded: z.array(ClusterIdSchema).meta({ @@ -123,7 +125,9 @@ export const ClusterLogSchema = z.object({ description: "RFC3339Nano timestamp emitted by the Docker daemon for that log line", }), }); +export type ClusterLog = z.infer; export const ClusterLogsSchema = z.array(ClusterLogSchema).meta({ description: "Logs (line by line) of a container", }); +export type ClusterLogs = z.infer; diff --git a/packages/backend/src/schemas/index.ts b/packages/backend/src/schemas/index.ts new file mode 100644 index 0000000..fe54832 --- /dev/null +++ b/packages/backend/src/schemas/index.ts @@ -0,0 +1,4 @@ +export * from "./auth.schema"; +export * from "./bot.schema"; +export * from "./cluster.schema"; +export * from "./error.schema"; diff --git a/packages/backend/src/services/bot.service.ts b/packages/backend/src/services/bot.service.ts index 4e317db..58a96e0 100644 --- a/packages/backend/src/services/bot.service.ts +++ b/packages/backend/src/services/bot.service.ts @@ -78,6 +78,17 @@ export default class BotService { }; } + public static async recommendedShards() { + const bot = await BotRepository.findFirstToken(); + if (bot === null) { + throw new NotFoundException({ message: "Bot not found" }); + } + + const { shards } = await DiscordService.getGatewayBot(bot.token); + + return { shards }; + } + public static async discordProfile() { const bot = await BotRepository.findFirstToken(); if (bot === null) throw new NotFoundException({ message: "Bot not found" }); diff --git a/packages/backend/tests/unit/bot.service.test.ts b/packages/backend/tests/unit/bot.service.test.ts index 138022d..342ec2c 100644 --- a/packages/backend/tests/unit/bot.service.test.ts +++ b/packages/backend/tests/unit/bot.service.test.ts @@ -1,10 +1,16 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; +import BotRepository from "#repositories/bot.repository"; import ContainerImageRepository from "#repositories/container-image.repository"; import BotService from "#services/bot.service"; +import DiscordService from "#services/discord.service"; import DockerService from "#services/docker.service"; -vi.mock("#repositories/bot.repository", () => ({ default: {} })); +vi.mock("#repositories/bot.repository", () => ({ + default: { + findFirstToken: vi.fn(), + }, +})); vi.mock("#repositories/cluster.repository", () => ({ default: {} })); vi.mock("#repositories/container-image.repository", () => ({ @@ -15,6 +21,12 @@ vi.mock("#repositories/container-image.repository", () => ({ }, })); +vi.mock("#services/discord.service", () => ({ + default: { + getGatewayBot: vi.fn(), + }, +})); + vi.mock("#services/docker.service", () => ({ default: { ensureImage: vi.fn(), @@ -103,3 +115,19 @@ describe("container image update", () => { expect(DockerService.ensureImage).not.toHaveBeenCalled(); }); }); + +describe("recommended shards", () => { + it("returns the number of shards recommended by Discord", async () => { + vi.mocked(BotRepository.findFirstToken).mockResolvedValue({ token: "token" }); + vi.mocked(DiscordService.getGatewayBot).mockResolvedValue({ shards: 4 }); + + expect(await BotService.recommendedShards()).toEqual({ shards: 4 }); + expect(DiscordService.getGatewayBot).toHaveBeenCalledWith("token"); + }); + + it("rejects the request when no bot is registered", async () => { + vi.mocked(BotRepository.findFirstToken).mockResolvedValue(null); + + await expect(BotService.recommendedShards()).rejects.toMatchObject({ status: 404 }); + }); +}); From 89d7d23e5e30225e417f6f472dda8c78d3bec26c Mon Sep 17 00:00:00 2001 From: A1ex Date: Sun, 11 Oct 2026 23:49:18 +0200 Subject: [PATCH 33/34] refactor(frontend): align the remote functions with the hono api --- .../src/lib/components/ClusterLogs.svelte | 6 +- .../frontend/src/lib/remotes/auth.remote.ts | 14 +- .../frontend/src/lib/remotes/bot.remote.ts | 149 ++++++++++++++---- .../src/lib/remotes/clusters.remote.ts | 87 +++++----- .../src/lib/utils/LayoutManager.svelte.ts | 10 +- .../src/routes/(app)/clusters/+page.svelte | 4 +- .../clusters/components/ActionRow.svelte | 4 +- .../components/CollapsibleContent.svelte | 4 +- .../components/CollapsibleTrigger.svelte | 7 +- .../components/cluster/QuickActions.svelte | 14 +- .../components/cluster/widgets/CPU.svelte | 6 +- .../components/cluster/widgets/Logs.svelte | 4 +- .../components/cluster/widgets/Memory.svelte | 6 +- .../components/cluster/widgets/Shards.svelte | 4 +- .../(app)/components/navigation/Header.svelte | 10 +- .../(app)/layout/components/Actions.svelte | 2 +- .../src/routes/(app)/settings/+page.svelte | 16 +- .../routes/(app)/stats/components/CPU.svelte | 6 +- .../(app)/stats/components/Memory.svelte | 6 +- .../routes/(form)/setup/components/Bot.svelte | 14 +- 20 files changed, 227 insertions(+), 146 deletions(-) diff --git a/packages/frontend/src/lib/components/ClusterLogs.svelte b/packages/frontend/src/lib/components/ClusterLogs.svelte index 31d671b..551ad16 100644 --- a/packages/frontend/src/lib/components/ClusterLogs.svelte +++ b/packages/frontend/src/lib/components/ClusterLogs.svelte @@ -1,6 +1,6 @@
diff --git a/packages/frontend/src/routes/(app)/clusters/components/ActionRow.svelte b/packages/frontend/src/routes/(app)/clusters/components/ActionRow.svelte index b17a980..002a3f3 100644 --- a/packages/frontend/src/routes/(app)/clusters/components/ActionRow.svelte +++ b/packages/frontend/src/routes/(app)/clusters/components/ActionRow.svelte @@ -6,12 +6,12 @@ stopClusters, } from "$lib/remotes/clusters.remote"; import toaster from "$lib/utils/toaster"; - import type { GetClusterDto } from "@hallmaster/backend/dto"; + import type { Cluster } from "@hallmaster/backend/schemas"; import { PlayIcon, RotateCcwIcon, SquareIcon, XIcon } from "@lucide/svelte"; import { fade } from "svelte/transition"; interface Props { - group: GetClusterDto["id"][]; + group: Cluster["id"][]; } let { group = $bindable() }: Props = $props(); diff --git a/packages/frontend/src/routes/(app)/clusters/components/CollapsibleContent.svelte b/packages/frontend/src/routes/(app)/clusters/components/CollapsibleContent.svelte index 4a4a7c7..a5d6ebf 100644 --- a/packages/frontend/src/routes/(app)/clusters/components/CollapsibleContent.svelte +++ b/packages/frontend/src/routes/(app)/clusters/components/CollapsibleContent.svelte @@ -1,5 +1,5 @@ -{#snippet component(data?: GetBotDto["discord"])} +{#snippet component(data?: BotDiscordProfile)}
@@ -27,9 +27,9 @@ {/snippet} - {const { discord } = await getBot()} + {const profile = await getBotProfile()} - {@render component(discord)} + {@render component(profile)} {#snippet pending()} {@render component()} diff --git a/packages/frontend/src/routes/(app)/layout/components/Actions.svelte b/packages/frontend/src/routes/(app)/layout/components/Actions.svelte index 53a7311..b1f9737 100644 --- a/packages/frontend/src/routes/(app)/layout/components/Actions.svelte +++ b/packages/frontend/src/routes/(app)/layout/components/Actions.svelte @@ -21,7 +21,7 @@ class="preset-filled-primary-500 btn-sm" disabled={!changed || !!updateBotLayout.pending} onclick={() => { - toaster.promise(updateBotLayout({ layout: layout!.export() }), { + toaster.promise(updateBotLayout(layout!.export()), { loading: { title: "Updating layout.." }, success: { title: "Successfully applied layout" }, error: (error) => ({ diff --git a/packages/frontend/src/routes/(app)/settings/+page.svelte b/packages/frontend/src/routes/(app)/settings/+page.svelte index b717381..5b2d4d3 100644 --- a/packages/frontend/src/routes/(app)/settings/+page.svelte +++ b/packages/frontend/src/routes/(app)/settings/+page.svelte @@ -1,7 +1,7 @@ @@ -36,8 +36,8 @@ icon={ContainerIcon} placeholder="ghcr.io/image:tag" required - {...dockerImage.image.as("text")} - issues={dockerImage.image.issues()} + {...url.as("text")} + issues={url.issues()} />
{/if} From ad3bc7b386f9ef9d9b4aba3203a4f6f43a79f8fe Mon Sep 17 00:00:00 2001 From: A1ex Date: Mon, 12 Oct 2026 00:00:16 +0200 Subject: [PATCH 34/34] test(backend): cover the discord client, the bot routes and the streams --- .../backend/tests/integration/bot.test.ts | 137 ++++++++++++++++++ .../tests/integration/clusters.test.ts | 58 ++++++++ packages/backend/tests/integration/helpers.ts | 37 +++++ .../tests/unit/discord.service.test.ts | 103 +++++++++++++ 4 files changed, 335 insertions(+) create mode 100644 packages/backend/tests/integration/bot.test.ts create mode 100644 packages/backend/tests/unit/discord.service.test.ts diff --git a/packages/backend/tests/integration/bot.test.ts b/packages/backend/tests/integration/bot.test.ts new file mode 100644 index 0000000..aa60bf2 --- /dev/null +++ b/packages/backend/tests/integration/bot.test.ts @@ -0,0 +1,137 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { FailedDependancyException } from "#exceptions/http-exceptions"; +import DiscordService from "#services/discord.service"; + +import { BOT_ID, register, request, resetState } from "./helpers"; + +vi.mock("#services/discord.service", () => ({ + default: { + getBotProfile: vi.fn(), + getGatewayBot: vi.fn(), + }, +})); + +const DISCORD_TOKEN = `${Buffer.from(BOT_ID).toString("base64")}.timestamp.signature`; + +let token: string; + +async function call(path: string, init: { method?: string; body?: unknown } = {}) { + const response = await request(path, { ...init, token }); + + return { status: response.status, body: (await response.json()) as unknown }; +} + +beforeEach(async () => { + vi.resetAllMocks(); + vi.mocked(DiscordService.getGatewayBot).mockResolvedValue({ shards: 2 }); + vi.mocked(DiscordService.getBotProfile).mockResolvedValue({ + name: "hallmaster", + displayName: "HallMaster", + discriminator: "0", + avatarUrl: null, + bannerUrl: null, + accentColor: null, + }); + + await resetState(); + token = await register(); +}); + +describe("bot registration", () => { + it("registers a single bot from its Discord token", async () => { + const created = await call("/bot", { method: "POST", body: { token: DISCORD_TOKEN } }); + + expect(created).toEqual({ status: 201, body: { id: BOT_ID, shards: 0 } }); + expect(await call("/bot")).toEqual({ status: 200, body: { id: BOT_ID, shards: 0 } }); + expect(await call("/bot", { method: "POST", body: { token: DISCORD_TOKEN } })).toEqual({ + status: 409, + body: { message: "A bot is already registered" }, + }); + }); + + it("rejects an invalid Discord token", async () => { + vi.mocked(DiscordService.getGatewayBot).mockRejectedValue( + new FailedDependancyException({ message: "Invalid Discord bot token" }), + ); + + expect(await call("/bot", { method: "POST", body: { token: "invalid" } })).toEqual({ + status: 424, + body: { message: "Invalid Discord bot token" }, + }); + }); + + it("updates the bot token", async () => { + await call("/bot", { method: "POST", body: { token: DISCORD_TOKEN } }); + + const updated = await call("/bot", { method: "PUT", body: { token: `${DISCORD_TOKEN}2` } }); + + expect(updated).toEqual({ status: 200, body: { id: BOT_ID, shards: 0 } }); + expect(DiscordService.getGatewayBot).toHaveBeenLastCalledWith(`${DISCORD_TOKEN}2`); + }); + + it("returns the Discord profile and the recommended shards", async () => { + await call("/bot", { method: "POST", body: { token: DISCORD_TOKEN } }); + + expect(await call("/bot/profile")).toMatchObject({ + status: 200, + body: { name: "hallmaster", displayName: "HallMaster" }, + }); + expect(await call("/bot/recommended-shards")).toEqual({ status: 200, body: { shards: 2 } }); + }); + + it("reports that no bot is registered", async () => { + expect(await call("/bot")).toEqual({ status: 404, body: { message: "Bot not found" } }); + }); +}); + +describe("container image", () => { + beforeEach(async () => { + await call("/bot", { method: "POST", body: { token: DISCORD_TOKEN } }); + }); + + it("registers a single container image", async () => { + expect(await call("/bot/container-image")).toEqual({ + status: 404, + body: { message: "Container image not found" }, + }); + + const created = await call("/bot/container-image", { + method: "POST", + body: { url: "nginx:alpine" }, + }); + + expect(created).toEqual({ status: 201, body: { url: "docker.io/library/nginx:alpine" } }); + expect((await call("/bot/container-image")).body).toEqual({ + url: "docker.io/library/nginx:alpine", + }); + expect( + (await call("/bot/container-image", { method: "POST", body: { url: "nginx:alpine" } })) + .status, + ).toBe(409); + }); + + it("updates the container image without exposing the password", async () => { + await call("/bot/container-image", { method: "POST", body: { url: "nginx:alpine" } }); + + const updated = await call("/bot/container-image", { + method: "PUT", + body: { url: "docker.io/library/nginx:alpine", username: "deployer", password: "secret" }, + }); + + expect(updated).toEqual({ + status: 200, + body: { url: "docker.io/library/nginx:alpine", username: "deployer" }, + }); + expect(JSON.stringify((await call("/bot/container-image")).body)).not.toContain("secret"); + }); + + it("refuses to update an image that was never registered", async () => { + const { status } = await call("/bot/container-image", { + method: "PUT", + body: { url: "nginx:alpine" }, + }); + + expect(status).toBe(404); + }); +}); diff --git a/packages/backend/tests/integration/clusters.test.ts b/packages/backend/tests/integration/clusters.test.ts index fcc499c..07c7d02 100644 --- a/packages/backend/tests/integration/clusters.test.ts +++ b/packages/backend/tests/integration/clusters.test.ts @@ -3,6 +3,7 @@ import { afterAll, beforeEach, describe, expect, it } from "vitest"; import { containerState, createBot, + openEvents, register, removeContainerOutsideHallMaster, request, @@ -108,6 +109,22 @@ describe("cluster actions", () => { }); }); + it("stops and starts every cluster", async () => { + expect((await call("/clusters/stop", { method: "POST" })).body).toEqual({ + succeeded: [0, 1], + failed: [], + }); + expect(await containerState(0)).toBe("exited"); + expect(await containerState(1)).toBe("exited"); + + expect((await call("/clusters/start", { method: "POST" })).body).toEqual({ + succeeded: [0, 1], + failed: [], + }); + expect(await containerState(0)).toBe("running"); + expect(await containerState(1)).toBe("running"); + }); + it("removes a running cluster", async () => { const { status } = await call("/clusters/0", { method: "DELETE" }); @@ -174,3 +191,44 @@ describe("cluster monitoring", () => { expect(body).toEqual(expect.any(Array)); }); }); + +describe("cluster streams", () => { + beforeEach(async () => { + await applyLayout([{ shardIds: [0] }]); + }); + + it("streams the clusters", async () => { + const stream = await openEvents("/clusters/sse?interval=1", token); + + expect(stream.contentType).toContain("text/event-stream"); + expect(await stream.read(1)).toEqual([[{ id: 0, shardIds: [0], status: "RUNNING" }]]); + }); + + it("streams the stats of every running cluster", async () => { + const [stats] = await (await openEvents("/clusters/stats/sse?interval=1", token)).read(1); + + expect(stats).toMatchObject({ 0: { cpuPercentage: expect.any(Number) } }); + }); + + it("streams the stats of a cluster", async () => { + const events = await (await openEvents("/clusters/0/stats/sse", token)).read(2); + + expect(events).toHaveLength(2); + expect(events[0]).toMatchObject({ memory: { usage: expect.any(Number) } }); + }); + + it("streams the new log lines of a cluster", async () => { + const stream = await openEvents("/clusters/0/logs/sse", token); + await call("/clusters/0/restart", { method: "POST" }); + + const [log] = await stream.read(1); + + expect(log).toMatchObject({ content: expect.any(String), stream: expect.any(String) }); + }); + + it("refuses to stream an unknown cluster", async () => { + const response = await request("/clusters/42/stats/sse", { token }); + + expect(response.status).toBe(404); + }); +}); diff --git a/packages/backend/tests/integration/helpers.ts b/packages/backend/tests/integration/helpers.ts index 517aa53..2e357e0 100644 --- a/packages/backend/tests/integration/helpers.ts +++ b/packages/backend/tests/integration/helpers.ts @@ -75,3 +75,40 @@ export async function register() { return token; } + +export async function openEvents(path: string, token: string) { + const response = await request(path, { token }); + if (response.body === null) { + throw new Error(`No body for ${path}`); + } + + const reader = response.body.pipeThrough(new TextDecoderStream()).getReader(); + let buffer = ""; + + async function read(count: number) { + const events: unknown[] = []; + + while (events.length < count) { + const { value, done } = await reader.read(); + if (done) { + break; + } + + buffer += value; + const blocks = buffer.split("\n\n"); + buffer = blocks.pop() ?? ""; + for (const block of blocks) { + const data = block.split("\n").find((line) => line.startsWith("data: ")); + if (data !== undefined) { + events.push(JSON.parse(data.slice("data: ".length))); + } + } + } + + await reader.cancel(); + + return events; + } + + return { status: response.status, contentType: response.headers.get("Content-Type"), read }; +} diff --git a/packages/backend/tests/unit/discord.service.test.ts b/packages/backend/tests/unit/discord.service.test.ts new file mode 100644 index 0000000..4285e3e --- /dev/null +++ b/packages/backend/tests/unit/discord.service.test.ts @@ -0,0 +1,103 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import DiscordService from "#services/discord.service"; + +const fetchMock = vi.fn(); + +function respond(status: number, body: unknown = {}) { + fetchMock.mockResolvedValue(new Response(JSON.stringify(body), { status })); +} + +beforeEach(() => { + vi.stubGlobal("fetch", fetchMock); +}); + +afterEach(() => { + fetchMock.mockReset(); + vi.unstubAllGlobals(); +}); + +describe("gateway bot", () => { + it("returns the recommended number of shards", async () => { + respond(200, { url: "wss://gateway.discord.gg", shards: 3 }); + + expect(await DiscordService.getGatewayBot("token")).toEqual({ shards: 3 }); + expect(fetchMock).toHaveBeenCalledWith( + "https://discord.com/api/v10/gateway/bot", + expect.objectContaining({ headers: { Authorization: "Bot token" } }), + ); + }); + + it("reports an invalid token", async () => { + respond(401); + + await expect(DiscordService.getGatewayBot("token")).rejects.toMatchObject({ + status: 424, + message: "Invalid Discord bot token", + }); + }); + + it("reports an unavailable Discord API", async () => { + respond(503); + + await expect(DiscordService.getGatewayBot("token")).rejects.toMatchObject({ status: 424 }); + }); + + it("reports an unreachable Discord API", async () => { + fetchMock.mockRejectedValue(new TypeError("fetch failed")); + + await expect(DiscordService.getGatewayBot("token")).rejects.toMatchObject({ + status: 424, + message: "Discord API is unreachable or timed out", + }); + }); + + it("reports an unexpected response body", async () => { + respond(200, { shards: "many" }); + + await expect(DiscordService.getGatewayBot("token")).rejects.toMatchObject({ + status: 424, + message: "Discord API returned an unexpected response body", + }); + }); +}); + +describe("bot profile", () => { + it("builds the profile with cdn urls", async () => { + respond(200, { + id: "42", + username: "hallmaster", + global_name: "HallMaster", + discriminator: "0", + avatar: "a_animated", + banner: "static", + accent_color: 16711680, + }); + + expect(await DiscordService.getBotProfile("token")).toEqual({ + name: "hallmaster", + displayName: "HallMaster", + discriminator: "0", + avatarUrl: "https://cdn.discordapp.com/avatars/42/a_animated.gif", + bannerUrl: "https://cdn.discordapp.com/banners/42/static.png", + accentColor: 16711680, + }); + }); + + it("accepts a profile without avatar, banner or accent color", async () => { + respond(200, { + id: "42", + username: "hallmaster", + global_name: null, + discriminator: "0", + avatar: null, + }); + + expect(await DiscordService.getBotProfile("token")).toMatchObject({ + displayName: null, + avatarUrl: null, + bannerUrl: null, + accentColor: null, + }); + }); +});