diff --git a/biome.json b/biome.json new file mode 100644 index 0000000000..676d4ceccc --- /dev/null +++ b/biome.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.5.3/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": false + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "linter": { + "enabled": true, + "rules": { + "preset": "recommended" + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/eslint.config.mjs b/eslint.config.mjs index 88dec1a035..763a80187d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,62 +1,65 @@ import js from "@eslint/js"; -import tseslint from "typescript-eslint"; -import reactHooks from "eslint-plugin-react-hooks"; import nextPlugin from "@next/eslint-plugin-next"; -import security from "eslint-plugin-security"; -import jsxA11y from "eslint-plugin-jsx-a11y"; import prettier from "eslint-config-prettier"; +import jsxA11y from "eslint-plugin-jsx-a11y"; +import reactHooks from "eslint-plugin-react-hooks"; +import security from "eslint-plugin-security"; +import tseslint from "typescript-eslint"; export default tseslint.config( - js.configs.recommended, - ...tseslint.configs.recommended, - security.configs.recommended, - prettier, - { - plugins: { - "@next/next": nextPlugin, - "react-hooks": reactHooks, - "jsx-a11y": jsxA11y, - }, - rules: { - ...nextPlugin.configs.recommended.rules, + js.configs.recommended, + ...tseslint.configs.recommended, + security.configs.recommended, + prettier, + { + plugins: { + "@next/next": nextPlugin, + "react-hooks": reactHooks, + "jsx-a11y": jsxA11y, + }, + rules: { + ...nextPlugin.configs.recommended.rules, - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "warn", + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", - "no-console": "off", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }], - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/consistent-type-imports": "error", - "@typescript-eslint/no-non-null-assertion": "warn", - "prefer-const": "error", - "no-var": "error", - eqeqeq: ["error", "always", { null: "ignore" }], + "no-console": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + ], + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/no-non-null-assertion": "warn", + "prefer-const": "error", + "no-var": "error", + eqeqeq: ["error", "always", { null: "ignore" }], - "no-empty": ["warn", { allowEmptyCatch: true }], - "no-useless-assignment": "off", - "no-undef": "off", + "no-empty": ["warn", { allowEmptyCatch: true }], + "no-useless-assignment": "off", + "no-undef": "off", - "security/detect-object-injection": "warn", - "security/detect-non-literal-fs-filename": "warn", - }, - }, - { - files: ["**/*.test.ts", "**/*.test.tsx", "scripts/**"], - rules: { - "security/detect-object-injection": "off", - "security/detect-non-literal-fs-filename": "off", - "@typescript-eslint/no-explicit-any": "off", - }, - }, - { - ignores: [ - ".next/", - "node_modules/", - "src/generated/", - "public/", - "prisma/migrations/", - "db_backup_*.sql", - ], - }, + "security/detect-object-injection": "warn", + "security/detect-non-literal-fs-filename": "warn", + }, + }, + { + files: ["**/*.test.ts", "**/*.test.tsx", "scripts/**"], + rules: { + "security/detect-object-injection": "off", + "security/detect-non-literal-fs-filename": "off", + "@typescript-eslint/no-explicit-any": "off", + }, + }, + { + ignores: [ + ".next/", + "node_modules/", + "src/generated/", + "public/", + "prisma/migrations/", + "db_backup_*.sql", + ], + }, ); diff --git a/next.config.ts b/next.config.ts index 1d97330fb8..21c2c9b77e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,64 +2,77 @@ import type { NextConfig } from "next"; import createNextIntlPlugin from "next-intl/plugin"; const securityHeaders = [ - { key: "X-DNS-Prefetch-Control", value: "on" }, - { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" }, - { key: "X-Frame-Options", value: "DENY" }, - { key: "X-Content-Type-Options", value: "nosniff" }, - { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, - { - key: "Permissions-Policy", - value: "camera=(), microphone=(), geolocation=(), interest-cohort=()", - }, - { - key: "Content-Security-Policy", - value: [ - "default-src 'self'", - "script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://static.cloudflareinsights.com", - "style-src 'self' 'unsafe-inline'", - "img-src 'self' data: blob: https:", - "frame-src 'self' https://challenges.cloudflare.com https://www.google.com/recaptcha/", - "connect-src 'self' https: wss:", - "font-src 'self' data:", - "object-src 'none'", - "base-uri 'self'", - "form-action 'self'", - ].join("; "), - }, + { key: "X-DNS-Prefetch-Control", value: "on" }, + { + key: "Strict-Transport-Security", + value: "max-age=63072000; includeSubDomains; preload", + }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, + { + key: "Permissions-Policy", + value: "camera=(), microphone=(), geolocation=(), interest-cohort=()", + }, + { + key: "Content-Security-Policy", + value: [ + "default-src 'self'", + "script-src 'self' 'unsafe-inline' https://challenges.cloudflare.com https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://static.cloudflareinsights.com", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: blob: https:", + "frame-src 'self' https://challenges.cloudflare.com https://www.google.com/recaptcha/", + "connect-src 'self' https: wss:", + "font-src 'self' data:", + "object-src 'none'", + "base-uri 'self'", + "form-action 'self'", + ].join("; "), + }, ]; const nextConfig: NextConfig = { - turbopack: { root: import.meta.dirname }, - serverExternalPackages: ["@prisma/adapter-mariadb", "mariadb", "@prisma/client", "lzma"], + turbopack: { root: import.meta.dirname }, + serverExternalPackages: [ + "@prisma/adapter-mariadb", + "mariadb", + "@prisma/client", + "lzma", + ], - // Compress responses with gzip - compress: true, + // Compress responses with gzip + compress: true, - // Cache pages longer in the router cache for faster back/forward navigation - experimental: { - staleTimes: { - dynamic: 30, - static: 180, - }, - }, + // Cache pages longer in the router cache for faster back/forward navigation + experimental: { + staleTimes: { + dynamic: 30, + static: 180, + }, + }, - // Add caching headers for static assets - async headers() { - return [ - { - source: "/(.*)", - headers: securityHeaders, - }, - { - source: "/assets/(.*)", - headers: [{ key: "Cache-Control", value: "public, max-age=31536000, immutable" }], - }, - { - source: "/images/(.*)", - headers: [{ key: "Cache-Control", value: "public, max-age=86400" }], - }, - ]; - }, + // Add caching headers for static assets + async headers() { + return [ + { + source: "/(.*)", + headers: securityHeaders, + }, + { + source: "/assets/(.*)", + headers: [ + { + key: "Cache-Control", + value: "public, max-age=31536000, immutable", + }, + ], + }, + { + source: "/images/(.*)", + headers: [{ key: "Cache-Control", value: "public, max-age=86400" }], + }, + ]; + }, }; // next-intl WITHOUT i18n routing — locale comes from the NEXT_LOCALE cookie via diff --git a/package.json b/package.json index 7ddb1a13eb..9eb81a3d45 100644 --- a/package.json +++ b/package.json @@ -1,100 +1,96 @@ { - "name": "atomcms-next", - "private": true, - "type": "module", - "engines": { - "node": ">=22" - }, - "packageManager": "pnpm@10.33.4", - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "prisma:generate": "prisma generate", - "typecheck": "tsc --noEmit", - "lint": "eslint . --max-warnings 200", - "format": "prettier --write .", - "test": "vitest run", - "db:migrate": "tsx scripts/apply-migrations.ts", - "db:migrate:status": "tsx scripts/apply-migrations.ts --status", - "jobs:worker": "tsx scripts/jobs-worker.ts" - }, - "dependencies": { - "@dnd-kit/core": "^6.3.1", - "@dnd-kit/sortable": "^10.0.0", - "@dnd-kit/utilities": "^3.2.2", - "@hookform/resolvers": "^5.4.0", - "@prisma/adapter-mariadb": "^7.8.0", - "@prisma/client": "^7.8.0", - "@tanstack/react-virtual": "^3.14.5", - "bcryptjs": "^3.0.2", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.1", - "croner": "^10.0.1", - "hash-wasm": "^4.12.0", - "ioredis": "^5.11.1", - "jpeg-js": "^0.4.4", - "json5": "^2.2.3", - "jszip": "^3.10.1", - "lucide-react": "^1.23.0", - "lzma": "^2.3.2", - "music-metadata": "^11.13.0", - "mysql2": "^3.22.6", - "next": "^16.2.10", - "next-auth": "5.0.0-beta.31", - "next-intl": "^4.13.1", - "nodemailer": "^9.0.3", - "otplib": "^12.0.1", - "radix-ui": "^1.6.2", - "react": "^19.2.0", - "react-dom": "^19.2.0", - "react-hook-form": "^7.81.0", - "resend": "^6.17.1", - "sanitize-html": "^2.17.5", - "sonner": "^2.0.7", - "tailwind-merge": "^3.6.0", - "zod": "^3.24.0" - }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@next/eslint-plugin-next": "^16.2.10", - "@tailwindcss/forms": "^0.5.11", - "@tailwindcss/postcss": "^4.3.2", - "@tailwindcss/typography": "^0.5.20", - "@types/jpeg-js": "^0.3.7", - "@types/node": "^22.10.0", - "@types/nodemailer": "^6.4.0", - "@types/react": "^19.2.0", - "@types/react-dom": "^19.2.0", - "@types/sanitize-html": "^2.16.1", - "@vitalets/google-translate-api": "^9.2.1", - "dotenv": "^16.4.0", - "eslint": "^10.6.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-security": "^4.0.1", - "postcss": "^8.5.16", - "prettier": "^3.9.5", - "prisma": "^7.8.0", - "tailwindcss": "^4.3.2", - "tsx": "^4.22.5", - "typescript": "^5.7.0", - "typescript-eslint": "^8.63.0", - "vitest": "^2.1.0" - }, - "pnpm": { - "onlyBuiltDependencies": [ - "esbuild", - "prisma", - "@prisma/client", - "@prisma/engines" - ], - "overrides": { - "fast-uri": "^3.1.3", - "@hono/node-server": "^1.19.13", - "postcss": "^8.5.16" - } - } + "name": "atomcms-next", + "private": true, + "type": "module", + "engines": { + "node": ">=22" + }, + "packageManager": "pnpm@10.33.4", + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "prisma:generate": "prisma generate", + "typecheck": "tsc --noEmit", + "biome:check": "biome check --write .", + "biome:lint": "biome lint .", + "biome:format": "biome format --write .", + "test": "vitest run", + "db:migrate": "tsx scripts/apply-migrations.ts", + "db:migrate:status": "tsx scripts/apply-migrations.ts --status", + "jobs:worker": "tsx scripts/jobs-worker.ts" + }, + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@hookform/resolvers": "^5.4.0", + "@prisma/adapter-mariadb": "^7.8.0", + "@prisma/client": "^7.8.0", + "@tanstack/react-virtual": "^3.14.5", + "bcryptjs": "^3.0.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "croner": "^10.0.1", + "hash-wasm": "^4.12.0", + "ioredis": "^5.11.1", + "jpeg-js": "^0.4.4", + "json5": "^2.2.3", + "jszip": "^3.10.1", + "lucide-react": "^1.23.0", + "lzma": "^2.3.2", + "music-metadata": "^11.13.0", + "mysql2": "^3.22.6", + "next": "^16.2.10", + "next-auth": "5.0.0-beta.31", + "next-intl": "^4.13.1", + "nodemailer": "^9.0.3", + "otplib": "^12.0.1", + "radix-ui": "^1.6.2", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-hook-form": "^7.81.0", + "resend": "^6.17.1", + "sanitize-html": "^2.17.5", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "zod": "^3.24.0" + }, + "devDependencies": { + "@biomejs/biome": "2.5.3", + "@tailwindcss/forms": "^0.5.11", + "@tailwindcss/postcss": "^4.3.2", + "@tailwindcss/typography": "^0.5.20", + "@types/jpeg-js": "^0.3.7", + "@types/node": "^22.10.0", + "@types/nodemailer": "^6.4.0", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@types/sanitize-html": "^2.16.1", + "@vitalets/google-translate-api": "^9.2.1", + "dotenv": "^16.4.0", + "postcss": "^8.5.16", + "prisma": "^7.8.0", + "tailwindcss": "^4.3.2", + "tsx": "^4.22.5", + "typescript": "^5.7.0", + "vitest": "^2.1.0" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild", + "prisma", + "@prisma/client", + "@prisma/engines", + "sharp", + "@parcel/watcher", + "@swc/core" + ], + "overrides": { + "fast-uri": "^3.1.3", + "@hono/node-server": "^1.19.13", + "postcss": "^8.5.16" + } + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c26afbc92c..ff1f20fcd2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: "9.0" +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -6,31 +6,32 @@ settings: overrides: fast-uri: ^3.1.3 - "@hono/node-server": ^1.19.13 + '@hono/node-server': ^1.19.13 postcss: ^8.5.16 importers: + .: dependencies: - "@dnd-kit/core": + '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@dnd-kit/sortable": + '@dnd-kit/sortable': specifier: ^10.0.0 version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) - "@dnd-kit/utilities": + '@dnd-kit/utilities': specifier: ^3.2.2 version: 3.2.2(react@19.2.7) - "@hookform/resolvers": + '@hookform/resolvers': specifier: ^5.4.0 version: 5.4.0(react-hook-form@7.81.0(react@19.2.7)) - "@prisma/adapter-mariadb": + '@prisma/adapter-mariadb': specifier: ^7.8.0 version: 7.8.0 - "@prisma/client": + '@prisma/client': specifier: ^7.8.0 version: 7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3) - "@tanstack/react-virtual": + '@tanstack/react-virtual': specifier: ^3.14.5 version: 3.14.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) bcryptjs: @@ -77,13 +78,13 @@ importers: version: 3.22.6(@types/node@22.20.0) next: specifier: ^16.2.10 - version: 16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-auth: specifier: 5.0.0-beta.31 - version: 5.0.0-beta.31(next@16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(nodemailer@9.0.3)(react@19.2.7) + version: 5.0.0-beta.31(next@16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(nodemailer@9.0.3)(react@19.2.7) next-intl: specifier: ^4.13.1 - version: 4.13.1(next@16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(typescript@5.9.3) + version: 4.13.1(next@16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(typescript@5.9.3) nodemailer: specifier: ^9.0.3 version: 9.0.3 @@ -118,66 +119,45 @@ importers: specifier: ^3.24.0 version: 3.25.76 devDependencies: - "@eslint/js": - specifier: ^10.0.1 - version: 10.0.1(eslint@10.6.0(jiti@2.7.0)) - "@next/eslint-plugin-next": - specifier: ^16.2.10 - version: 16.2.10 - "@tailwindcss/forms": + '@biomejs/biome': + specifier: 2.5.3 + version: 2.5.3 + '@tailwindcss/forms': specifier: ^0.5.11 version: 0.5.11(tailwindcss@4.3.2) - "@tailwindcss/postcss": + '@tailwindcss/postcss': specifier: ^4.3.2 version: 4.3.2 - "@tailwindcss/typography": + '@tailwindcss/typography': specifier: ^0.5.20 version: 0.5.20(tailwindcss@4.3.2) - "@types/jpeg-js": + '@types/jpeg-js': specifier: ^0.3.7 version: 0.3.7 - "@types/node": + '@types/node': specifier: ^22.10.0 version: 22.20.0 - "@types/nodemailer": + '@types/nodemailer': specifier: ^6.4.0 version: 6.4.24 - "@types/react": + '@types/react': specifier: ^19.2.0 version: 19.2.17 - "@types/react-dom": + '@types/react-dom': specifier: ^19.2.0 version: 19.2.3(@types/react@19.2.17) - "@types/sanitize-html": + '@types/sanitize-html': specifier: ^2.16.1 version: 2.16.1 - "@vitalets/google-translate-api": + '@vitalets/google-translate-api': specifier: ^9.2.1 version: 9.2.1 dotenv: specifier: ^16.4.0 version: 16.6.1 - eslint: - specifier: ^10.6.0 - version: 10.6.0(jiti@2.7.0) - eslint-config-prettier: - specifier: ^10.1.8 - version: 10.1.8(eslint@10.6.0(jiti@2.7.0)) - eslint-plugin-jsx-a11y: - specifier: ^6.10.2 - version: 6.10.2(eslint@10.6.0(jiti@2.7.0)) - eslint-plugin-react-hooks: - specifier: ^7.1.1 - version: 7.1.1(eslint@10.6.0(jiti@2.7.0)) - eslint-plugin-security: - specifier: ^4.0.1 - version: 4.0.1 postcss: specifier: ^8.5.16 version: 8.5.16 - prettier: - specifier: ^3.9.5 - version: 3.9.5 prisma: specifier: ^7.8.0 version: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) @@ -190,16 +170,13 @@ importers: typescript: specifier: ^5.7.0 version: 5.9.3 - typescript-eslint: - specifier: ^8.63.0 - version: 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) vitest: specifier: ^2.1.0 version: 2.1.9(@types/node@22.20.0)(lightningcss@1.32.0) packages/db: dependencies: - "@atomcms/settings": + '@atomcms/settings': specifier: workspace:* version: link:../settings drizzle-orm: @@ -229,3624 +206,2312 @@ importers: version: 2.1.9(@types/node@22.20.0) packages: - "@alloc/quick-lru@5.2.0": - resolution: - { - integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==, - } - engines: { node: ">=10" } - "@auth/core@0.41.2": - resolution: - { - integrity: sha512-Hx5MNBxN2fJTbJKGUKAA0wca43D0Akl3TvufY54Gn8lop7F+34vU1zA1pn0vQfIoVuLIrpfc2nkyjwIaPJMW7w==, - } + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@auth/core@0.41.2': + resolution: {integrity: sha512-Hx5MNBxN2fJTbJKGUKAA0wca43D0Akl3TvufY54Gn8lop7F+34vU1zA1pn0vQfIoVuLIrpfc2nkyjwIaPJMW7w==} peerDependencies: - "@simplewebauthn/browser": ^9.0.1 - "@simplewebauthn/server": ^9.0.2 + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 nodemailer: ^7.0.7 peerDependenciesMeta: - "@simplewebauthn/browser": + '@simplewebauthn/browser': optional: true - "@simplewebauthn/server": + '@simplewebauthn/server': optional: true nodemailer: optional: true - "@babel/code-frame@7.29.7": - resolution: - { - integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, - } - engines: { node: ">=6.9.0" } - - "@babel/compat-data@7.29.7": - resolution: - { - integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==, - } - engines: { node: ">=6.9.0" } - - "@babel/core@7.29.7": - resolution: - { - integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==, - } - engines: { node: ">=6.9.0" } - - "@babel/generator@7.29.7": - resolution: - { - integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-compilation-targets@7.29.7": - resolution: - { - integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-globals@7.29.7": - resolution: - { - integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-module-imports@7.29.7": - resolution: - { - integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-module-transforms@7.29.7": - resolution: - { - integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==, - } - engines: { node: ">=6.9.0" } - peerDependencies: - "@babel/core": ^7.0.0 - - "@babel/helper-string-parser@7.29.7": - resolution: - { - integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==, - } - 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/helper-validator-option@7.29.7": - resolution: - { - integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helpers@7.29.7": - resolution: - { - integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==, - } - engines: { node: ">=6.9.0" } - - "@babel/parser@7.29.7": - resolution: - { - integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==, - } - engines: { node: ">=6.0.0" } + '@biomejs/biome@2.5.3': + resolution: {integrity: sha512-MrJswFdei9EfDwwUy2tQrPDpK0AO+RmMFvBoaaJ6ayBc3sUbHdCE+XG5N8vp+5So41ZupZJQm0roHFFhMGVD7A==} + engines: {node: '>=14.21.3'} hasBin: true - "@babel/template@7.29.7": - resolution: - { - integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==, - } - engines: { node: ">=6.9.0" } + '@biomejs/cli-darwin-arm64@2.5.3': + resolution: {integrity: sha512-QhYP9muVQ0nUO5zztFuPbEwi4+94sJWVjaZds9aMi1l/KNZBiUjdiSUrGHsTaMGDXrYl+r4AS2sUKfgH3w+V3g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] - "@babel/traverse@7.29.7": - resolution: - { - integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==, - } - engines: { node: ">=6.9.0" } + '@biomejs/cli-darwin-x64@2.5.3': + resolution: {integrity: sha512-NC1Ss13UaW7QZX+y8j44bF7AP0jSJdBl6iRhe0MAkvaSqZy+mWg3GaXsrb+eSoHoGDBtaXWEbMVV0iVN2cZ7cQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] - "@babel/types@7.29.7": - resolution: - { - integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==, - } - engines: { node: ">=6.9.0" } + '@biomejs/cli-linux-arm64-musl@2.5.3': + resolution: {integrity: sha512-fccix0w6xp6csCXgxeC0dU/3ecgRQal0y+cv2SP9ajNlhe7Yrk2Ug7UDe2j9AT9ZDYitkXpvUKgZjjuoYeP4Vg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] - "@borewit/text-codec@0.2.2": - resolution: - { - integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==, - } + '@biomejs/cli-linux-arm64@2.5.3': + resolution: {integrity: sha512-ksx1KWeyYW18ILL04msF/J4ZBtBDN33znYK8Z/aNv/vlBVxL9/g3mGP+omgHJKy4+KWbK87vcmmpmurfNjSgiA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] - "@dnd-kit/accessibility@3.1.1": - resolution: - { - integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==, - } + '@biomejs/cli-linux-x64-musl@2.5.3': + resolution: {integrity: sha512-O/yU9YKRUiHhmcjF2f38PSjseVk3G4VLWYc0G2HWpzdBVREV6G8IGWIVEFf7MFPfWIzNUIvPsEjeAZQIOgnLcQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.5.3': + resolution: {integrity: sha512-yMkJtilsgvILDcVkh187aVLTb64xYsrxYajx5kym+r1ULkO5HUOfu9AYKLGQbOVLwJtT2utNw7hhFNg+17mUYA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@2.5.3': + resolution: {integrity: sha512-cX5z+GYwRcqEok0AH3KSfQGgqYd0Nomfp6Fbe1uiTtELE38hdH2k842wQ9wLNaF/JJ7r4rjJQ4VR+ce+fRmQbw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.5.3': + resolution: {integrity: sha512-ExSaJWi4/u6+GXCszlSKpWSjKNbDseAYqqkCznsCsZ/4uidZ/BEqsCc5/3ctlq6dfIubdIIRSVLC/PG9xPl70Q==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@borewit/text-codec@0.2.2': + resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} + + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} peerDependencies: - react: ">=16.8.0" + react: '>=16.8.0' - "@dnd-kit/core@6.3.1": - resolution: - { - integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==, - } + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" + react: '>=16.8.0' + react-dom: '>=16.8.0' - "@dnd-kit/sortable@10.0.0": - resolution: - { - integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==, - } + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} peerDependencies: - "@dnd-kit/core": ^6.3.0 - react: ">=16.8.0" + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' - "@dnd-kit/utilities@3.2.2": - resolution: - { - integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==, - } + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} peerDependencies: - react: ">=16.8.0" + react: '>=16.8.0' - "@drizzle-team/brocli@0.10.2": - resolution: - { - integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==, - } + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - "@electric-sql/pglite-socket@0.1.1": - resolution: - { - integrity: sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw==, - } + '@electric-sql/pglite-socket@0.1.1': + resolution: {integrity: sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw==} hasBin: true peerDependencies: - "@electric-sql/pglite": 0.4.1 + '@electric-sql/pglite': 0.4.1 - "@electric-sql/pglite-tools@0.3.1": - resolution: - { - integrity: sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA==, - } + '@electric-sql/pglite-tools@0.3.1': + resolution: {integrity: sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA==} peerDependencies: - "@electric-sql/pglite": 0.4.1 + '@electric-sql/pglite': 0.4.1 - "@electric-sql/pglite@0.4.1": - resolution: - { - integrity: sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q==, - } + '@electric-sql/pglite@0.4.1': + resolution: {integrity: sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q==} - "@emnapi/runtime@1.11.1": - resolution: - { - integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==, - } + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} - "@esbuild-kit/core-utils@3.3.2": - resolution: - { - integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==, - } - deprecated: "Merged into tsx: https://tsx.is" + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' - "@esbuild-kit/esm-loader@2.6.5": - resolution: - { - integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==, - } - deprecated: "Merged into tsx: https://tsx.is" + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' - "@esbuild/aix-ppc64@0.19.12": - resolution: - { - integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==, - } - engines: { node: ">=12" } + '@esbuild/aix-ppc64@0.19.12': + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} cpu: [ppc64] os: [aix] - "@esbuild/aix-ppc64@0.21.5": - resolution: - { - integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==, - } - engines: { node: ">=12" } + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} cpu: [ppc64] os: [aix] - "@esbuild/aix-ppc64@0.28.1": - resolution: - { - integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==, - } - engines: { node: ">=18" } + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - "@esbuild/android-arm64@0.18.20": - resolution: - { - integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==, - } - engines: { node: ">=12" } + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} cpu: [arm64] os: [android] - "@esbuild/android-arm64@0.19.12": - resolution: - { - integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==, - } - engines: { node: ">=12" } + '@esbuild/android-arm64@0.19.12': + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} cpu: [arm64] os: [android] - "@esbuild/android-arm64@0.21.5": - resolution: - { - integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==, - } - engines: { node: ">=12" } + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} cpu: [arm64] os: [android] - "@esbuild/android-arm64@0.28.1": - resolution: - { - integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==, - } - engines: { node: ">=18" } + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - "@esbuild/android-arm@0.18.20": - resolution: - { - integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==, - } - engines: { node: ">=12" } + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} cpu: [arm] os: [android] - "@esbuild/android-arm@0.19.12": - resolution: - { - integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==, - } - engines: { node: ">=12" } + '@esbuild/android-arm@0.19.12': + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} cpu: [arm] os: [android] - "@esbuild/android-arm@0.21.5": - resolution: - { - integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==, - } - engines: { node: ">=12" } + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} cpu: [arm] os: [android] - "@esbuild/android-arm@0.28.1": - resolution: - { - integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==, - } - engines: { node: ">=18" } + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} cpu: [arm] os: [android] - "@esbuild/android-x64@0.18.20": - resolution: - { - integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==, - } - engines: { node: ">=12" } + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} cpu: [x64] os: [android] - "@esbuild/android-x64@0.19.12": - resolution: - { - integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==, - } - engines: { node: ">=12" } + '@esbuild/android-x64@0.19.12': + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} cpu: [x64] os: [android] - "@esbuild/android-x64@0.21.5": - resolution: - { - integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==, - } - engines: { node: ">=12" } + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} cpu: [x64] os: [android] - "@esbuild/android-x64@0.28.1": - resolution: - { - integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==, - } - engines: { node: ">=18" } + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} cpu: [x64] os: [android] - "@esbuild/darwin-arm64@0.18.20": - resolution: - { - integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==, - } - engines: { node: ">=12" } + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-arm64@0.19.12": - resolution: - { - integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==, - } - engines: { node: ">=12" } + '@esbuild/darwin-arm64@0.19.12': + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-arm64@0.21.5": - resolution: - { - integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==, - } - engines: { node: ">=12" } + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-arm64@0.28.1": - resolution: - { - integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==, - } - engines: { node: ">=18" } + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - "@esbuild/darwin-x64@0.18.20": - resolution: - { - integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==, - } - engines: { node: ">=12" } + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} cpu: [x64] os: [darwin] - "@esbuild/darwin-x64@0.19.12": - resolution: - { - integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==, - } - engines: { node: ">=12" } + '@esbuild/darwin-x64@0.19.12': + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} cpu: [x64] os: [darwin] - "@esbuild/darwin-x64@0.21.5": - resolution: - { - integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==, - } - engines: { node: ">=12" } + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} cpu: [x64] os: [darwin] - "@esbuild/darwin-x64@0.28.1": - resolution: - { - integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==, - } - engines: { node: ">=18" } + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - "@esbuild/freebsd-arm64@0.18.20": - resolution: - { - integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==, - } - engines: { node: ">=12" } + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-arm64@0.19.12": - resolution: - { - integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==, - } - engines: { node: ">=12" } + '@esbuild/freebsd-arm64@0.19.12': + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-arm64@0.21.5": - resolution: - { - integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==, - } - engines: { node: ">=12" } + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-arm64@0.28.1": - resolution: - { - integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - "@esbuild/freebsd-x64@0.18.20": - resolution: - { - integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==, - } - engines: { node: ">=12" } + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} cpu: [x64] os: [freebsd] - "@esbuild/freebsd-x64@0.19.12": - resolution: - { - integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==, - } - engines: { node: ">=12" } + '@esbuild/freebsd-x64@0.19.12': + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} cpu: [x64] os: [freebsd] - "@esbuild/freebsd-x64@0.21.5": - resolution: - { - integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==, - } - engines: { node: ">=12" } + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} cpu: [x64] os: [freebsd] - "@esbuild/freebsd-x64@0.28.1": - resolution: - { - integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==, - } - engines: { node: ">=18" } + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - "@esbuild/linux-arm64@0.18.20": - resolution: - { - integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==, - } - engines: { node: ">=12" } + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm64@0.19.12": - resolution: - { - integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==, - } - engines: { node: ">=12" } + '@esbuild/linux-arm64@0.19.12': + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm64@0.21.5": - resolution: - { - integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==, - } - engines: { node: ">=12" } + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm64@0.28.1": - resolution: - { - integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - "@esbuild/linux-arm@0.18.20": - resolution: - { - integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==, - } - engines: { node: ">=12" } + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} cpu: [arm] os: [linux] - "@esbuild/linux-arm@0.19.12": - resolution: - { - integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==, - } - engines: { node: ">=12" } + '@esbuild/linux-arm@0.19.12': + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} cpu: [arm] os: [linux] - "@esbuild/linux-arm@0.21.5": - resolution: - { - integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==, - } - engines: { node: ">=12" } + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} cpu: [arm] os: [linux] - "@esbuild/linux-arm@0.28.1": - resolution: - { - integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - "@esbuild/linux-ia32@0.18.20": - resolution: - { - integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==, - } - engines: { node: ">=12" } + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} cpu: [ia32] os: [linux] - "@esbuild/linux-ia32@0.19.12": - resolution: - { - integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==, - } - engines: { node: ">=12" } + '@esbuild/linux-ia32@0.19.12': + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} cpu: [ia32] os: [linux] - "@esbuild/linux-ia32@0.21.5": - resolution: - { - integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==, - } - engines: { node: ">=12" } + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} cpu: [ia32] os: [linux] - "@esbuild/linux-ia32@0.28.1": - resolution: - { - integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==, - } - engines: { node: ">=18" } + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - "@esbuild/linux-loong64@0.18.20": - resolution: - { - integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==, - } - engines: { node: ">=12" } + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} cpu: [loong64] os: [linux] - "@esbuild/linux-loong64@0.19.12": - resolution: - { - integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==, - } - engines: { node: ">=12" } + '@esbuild/linux-loong64@0.19.12': + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} cpu: [loong64] os: [linux] - "@esbuild/linux-loong64@0.21.5": - resolution: - { - integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==, - } - engines: { node: ">=12" } + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} cpu: [loong64] os: [linux] - "@esbuild/linux-loong64@0.28.1": - resolution: - { - integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==, - } - engines: { node: ">=18" } + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - "@esbuild/linux-mips64el@0.18.20": - resolution: - { - integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==, - } - engines: { node: ">=12" } + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} cpu: [mips64el] os: [linux] - "@esbuild/linux-mips64el@0.19.12": - resolution: - { - integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==, - } - engines: { node: ">=12" } + '@esbuild/linux-mips64el@0.19.12': + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} cpu: [mips64el] os: [linux] - "@esbuild/linux-mips64el@0.21.5": - resolution: - { - integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==, - } - engines: { node: ">=12" } + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} cpu: [mips64el] os: [linux] - "@esbuild/linux-mips64el@0.28.1": - resolution: - { - integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - "@esbuild/linux-ppc64@0.18.20": - resolution: - { - integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==, - } - engines: { node: ">=12" } + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} cpu: [ppc64] os: [linux] - "@esbuild/linux-ppc64@0.19.12": - resolution: - { - integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==, - } - engines: { node: ">=12" } + '@esbuild/linux-ppc64@0.19.12': + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} cpu: [ppc64] os: [linux] - "@esbuild/linux-ppc64@0.21.5": - resolution: - { - integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==, - } - engines: { node: ">=12" } + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} cpu: [ppc64] os: [linux] - "@esbuild/linux-ppc64@0.28.1": - resolution: - { - integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - "@esbuild/linux-riscv64@0.18.20": - resolution: - { - integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==, - } - engines: { node: ">=12" } + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} cpu: [riscv64] os: [linux] - "@esbuild/linux-riscv64@0.19.12": - resolution: - { - integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==, - } - engines: { node: ">=12" } + '@esbuild/linux-riscv64@0.19.12': + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} + engines: {node: '>=12'} cpu: [riscv64] os: [linux] - "@esbuild/linux-riscv64@0.21.5": - resolution: - { - integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==, - } - engines: { node: ">=12" } + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} cpu: [riscv64] os: [linux] - "@esbuild/linux-riscv64@0.28.1": - resolution: - { - integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==, - } - engines: { node: ">=18" } + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - "@esbuild/linux-s390x@0.18.20": - resolution: - { - integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==, - } - engines: { node: ">=12" } + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} cpu: [s390x] os: [linux] - "@esbuild/linux-s390x@0.19.12": - resolution: - { - integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==, - } - engines: { node: ">=12" } + '@esbuild/linux-s390x@0.19.12': + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} cpu: [s390x] os: [linux] - "@esbuild/linux-s390x@0.21.5": - resolution: - { - integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==, - } - engines: { node: ">=12" } + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} cpu: [s390x] os: [linux] - "@esbuild/linux-s390x@0.28.1": - resolution: - { - integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==, - } - engines: { node: ">=18" } + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - "@esbuild/linux-x64@0.18.20": - resolution: - { - integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==, - } - engines: { node: ">=12" } + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} cpu: [x64] os: [linux] - "@esbuild/linux-x64@0.19.12": - resolution: - { - integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==, - } - engines: { node: ">=12" } + '@esbuild/linux-x64@0.19.12': + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} cpu: [x64] os: [linux] - "@esbuild/linux-x64@0.21.5": - resolution: - { - integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==, - } - engines: { node: ">=12" } + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} cpu: [x64] os: [linux] - "@esbuild/linux-x64@0.28.1": - resolution: - { - integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==, - } - engines: { node: ">=18" } + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - "@esbuild/netbsd-arm64@0.28.1": - resolution: - { - integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - "@esbuild/netbsd-x64@0.18.20": - resolution: - { - integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==, - } - engines: { node: ">=12" } + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} cpu: [x64] os: [netbsd] - "@esbuild/netbsd-x64@0.19.12": - resolution: - { - integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==, - } - engines: { node: ">=12" } + '@esbuild/netbsd-x64@0.19.12': + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} cpu: [x64] os: [netbsd] - "@esbuild/netbsd-x64@0.21.5": - resolution: - { - integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==, - } - engines: { node: ">=12" } + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} cpu: [x64] os: [netbsd] - "@esbuild/netbsd-x64@0.28.1": - resolution: - { - integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==, - } - engines: { node: ">=18" } + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - "@esbuild/openbsd-arm64@0.28.1": - resolution: - { - integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - "@esbuild/openbsd-x64@0.18.20": - resolution: - { - integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==, - } - engines: { node: ">=12" } + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} cpu: [x64] os: [openbsd] - "@esbuild/openbsd-x64@0.19.12": - resolution: - { - integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==, - } - engines: { node: ">=12" } + '@esbuild/openbsd-x64@0.19.12': + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} cpu: [x64] os: [openbsd] - "@esbuild/openbsd-x64@0.21.5": - resolution: - { - integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==, - } - engines: { node: ">=12" } + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} cpu: [x64] os: [openbsd] - "@esbuild/openbsd-x64@0.28.1": - resolution: - { - integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==, - } - engines: { node: ">=18" } + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - "@esbuild/openharmony-arm64@0.28.1": - resolution: - { - integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==, - } - engines: { node: ">=18" } + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - "@esbuild/sunos-x64@0.18.20": - resolution: - { - integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==, - } - engines: { node: ">=12" } + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} cpu: [x64] os: [sunos] - "@esbuild/sunos-x64@0.19.12": - resolution: - { - integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==, - } - engines: { node: ">=12" } + '@esbuild/sunos-x64@0.19.12': + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} cpu: [x64] os: [sunos] - "@esbuild/sunos-x64@0.21.5": - resolution: - { - integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==, - } - engines: { node: ">=12" } + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} cpu: [x64] os: [sunos] - "@esbuild/sunos-x64@0.28.1": - resolution: - { - integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==, - } - engines: { node: ">=18" } + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - "@esbuild/win32-arm64@0.18.20": - resolution: - { - integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==, - } - engines: { node: ">=12" } + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} cpu: [arm64] os: [win32] - "@esbuild/win32-arm64@0.19.12": - resolution: - { - integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==, - } - engines: { node: ">=12" } + '@esbuild/win32-arm64@0.19.12': + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} cpu: [arm64] os: [win32] - "@esbuild/win32-arm64@0.21.5": - resolution: - { - integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==, - } - engines: { node: ">=12" } + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} cpu: [arm64] os: [win32] - "@esbuild/win32-arm64@0.28.1": - resolution: - { - integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==, - } - engines: { node: ">=18" } + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - "@esbuild/win32-ia32@0.18.20": - resolution: - { - integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==, - } - engines: { node: ">=12" } + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} cpu: [ia32] os: [win32] - "@esbuild/win32-ia32@0.19.12": - resolution: - { - integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==, - } - engines: { node: ">=12" } + '@esbuild/win32-ia32@0.19.12': + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} cpu: [ia32] os: [win32] - "@esbuild/win32-ia32@0.21.5": - resolution: - { - integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==, - } - engines: { node: ">=12" } + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} cpu: [ia32] os: [win32] - "@esbuild/win32-ia32@0.28.1": - resolution: - { - integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==, - } - engines: { node: ">=18" } + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - "@esbuild/win32-x64@0.18.20": - resolution: - { - integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==, - } - engines: { node: ">=12" } + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} cpu: [x64] os: [win32] - "@esbuild/win32-x64@0.19.12": - resolution: - { - integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==, - } - engines: { node: ">=12" } + '@esbuild/win32-x64@0.19.12': + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} cpu: [x64] os: [win32] - "@esbuild/win32-x64@0.21.5": - resolution: - { - integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==, - } - engines: { node: ">=12" } + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} cpu: [x64] os: [win32] - "@esbuild/win32-x64@0.28.1": - resolution: - { - integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==, - } - engines: { node: ">=18" } + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + 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 } + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + react: '>=16.8.0' + react-dom: '>=16.8.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 } + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} - "@eslint/config-array@0.23.5": - resolution: - { - integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + '@formatjs/fast-memoize@3.1.6': + resolution: {integrity: sha512-H5aexk1Le7T9TPmscacZ+1pR6CTa2n1wq+HDVGXhH8TzUlQQpeXzZs91dRtmFHrbeNbjPFPfQujUqm7MHgVoXQ==} - "@eslint/config-helpers@0.6.0": - resolution: - { - integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + '@formatjs/icu-messageformat-parser@3.5.12': + resolution: {integrity: sha512-YyzzxVgYJ8DELmmkhn0Yr0rUj0dTJFf9Jp628K3S0ysInBWxLVDOS8i3RP91cCp4DMK4WYb4cVMhWA9i4knSJg==} - "@eslint/core@1.2.1": - resolution: - { - integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + '@formatjs/icu-skeleton-parser@2.1.10': + resolution: {integrity: sha512-XuSva+8ZGawk8VnD5VD6UeH8KarQ/Z022zgjHDoHmlNiAewstXuuzXc0Hk5pGFSdG+nNw5bfJKXqj1ZXHn9yUA==} - "@eslint/js@10.0.1": - resolution: - { - integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - peerDependencies: - eslint: ^10.0.0 - peerDependenciesMeta: - eslint: - optional: true + '@formatjs/intl-localematcher@0.8.10': + resolution: {integrity: sha512-P/IC3qws3jH+1fEs+o0RIFgXKRaQlFehjS5W0FPAqdo6hgzawLl+eD0q0JjheQ3XtoOe5n8WSYfX06KQZI/QJA==} - "@eslint/object-schema@3.0.5": - resolution: - { - integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - - "@eslint/plugin-kit@0.7.2": - resolution: - { - integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - - "@floating-ui/core@1.8.0": - resolution: - { - integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==, - } - - "@floating-ui/dom@1.8.0": - resolution: - { - integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==, - } - - "@floating-ui/react-dom@2.1.9": - resolution: - { - integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==, - } - peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - - "@floating-ui/utils@0.2.12": - resolution: - { - integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==, - } - - "@formatjs/fast-memoize@3.1.6": - resolution: - { - integrity: sha512-H5aexk1Le7T9TPmscacZ+1pR6CTa2n1wq+HDVGXhH8TzUlQQpeXzZs91dRtmFHrbeNbjPFPfQujUqm7MHgVoXQ==, - } - - "@formatjs/icu-messageformat-parser@3.5.12": - resolution: - { - integrity: sha512-YyzzxVgYJ8DELmmkhn0Yr0rUj0dTJFf9Jp628K3S0ysInBWxLVDOS8i3RP91cCp4DMK4WYb4cVMhWA9i4knSJg==, - } - - "@formatjs/icu-skeleton-parser@2.1.10": - resolution: - { - integrity: sha512-XuSva+8ZGawk8VnD5VD6UeH8KarQ/Z022zgjHDoHmlNiAewstXuuzXc0Hk5pGFSdG+nNw5bfJKXqj1ZXHn9yUA==, - } - - "@formatjs/intl-localematcher@0.8.10": - resolution: - { - integrity: sha512-P/IC3qws3jH+1fEs+o0RIFgXKRaQlFehjS5W0FPAqdo6hgzawLl+eD0q0JjheQ3XtoOe5n8WSYfX06KQZI/QJA==, - } - - "@hono/node-server@1.19.14": - resolution: - { - integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==, - } - engines: { node: ">=18.14.1" } + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 - "@hookform/resolvers@5.4.0": - resolution: - { - integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==, - } + '@hookform/resolvers@5.4.0': + resolution: {integrity: sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==} peerDependencies: react-hook-form: ^7.55.0 - "@humanfs/core@0.19.2": - resolution: - { - integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==, - } - engines: { node: ">=18.18.0" } + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} - "@humanfs/node@0.16.8": - resolution: - { - integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==, - } - engines: { node: ">=18.18.0" } - - "@humanfs/types@0.15.0": - resolution: - { - integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==, - } - 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" } - - "@img/colour@1.1.0": - resolution: - { - integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==, - } - engines: { node: ">=18" } - - "@img/sharp-darwin-arm64@0.34.5": - resolution: - { - integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - "@img/sharp-darwin-x64@0.34.5": - resolution: - { - integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - "@img/sharp-libvips-darwin-arm64@1.2.4": - resolution: - { - integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==, - } + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - "@img/sharp-libvips-darwin-x64@1.2.4": - resolution: - { - integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==, - } + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - "@img/sharp-libvips-linux-arm64@1.2.4": - resolution: - { - integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==, - } + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-arm@1.2.4": - resolution: - { - integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==, - } + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-ppc64@1.2.4": - resolution: - { - integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==, - } + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-riscv64@1.2.4": - resolution: - { - integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==, - } + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-s390x@1.2.4": - resolution: - { - integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==, - } + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] libc: [glibc] - "@img/sharp-libvips-linux-x64@1.2.4": - resolution: - { - integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==, - } + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] libc: [glibc] - "@img/sharp-libvips-linuxmusl-arm64@1.2.4": - resolution: - { - integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==, - } + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] libc: [musl] - "@img/sharp-libvips-linuxmusl-x64@1.2.4": - resolution: - { - integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==, - } + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] libc: [musl] - "@img/sharp-linux-arm64@0.34.5": - resolution: - { - integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [glibc] - "@img/sharp-linux-arm@0.34.5": - resolution: - { - integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] libc: [glibc] - "@img/sharp-linux-ppc64@0.34.5": - resolution: - { - integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] libc: [glibc] - "@img/sharp-linux-riscv64@0.34.5": - resolution: - { - integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] libc: [glibc] - "@img/sharp-linux-s390x@0.34.5": - resolution: - { - integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] libc: [glibc] - "@img/sharp-linux-x64@0.34.5": - resolution: - { - integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [glibc] - "@img/sharp-linuxmusl-arm64@0.34.5": - resolution: - { - integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] libc: [musl] - "@img/sharp-linuxmusl-x64@0.34.5": - resolution: - { - integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] libc: [musl] - "@img/sharp-wasm32@0.34.5": - resolution: - { - integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - "@img/sharp-win32-arm64@0.34.5": - resolution: - { - integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - "@img/sharp-win32-ia32@0.34.5": - resolution: - { - integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - "@img/sharp-win32-x64@0.34.5": - resolution: - { - integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] - "@ioredis/commands@1.10.0": - resolution: - { - integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==, - } + '@ioredis/commands@1.10.0': + resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==} - "@jridgewell/gen-mapping@0.3.13": - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - "@jridgewell/remapping@2.3.5": - resolution: - { - integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, - } + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - "@jridgewell/resolve-uri@3.1.2": - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: ">=6.0.0" } + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} - "@jridgewell/sourcemap-codec@1.5.5": - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - "@jridgewell/trace-mapping@0.3.31": - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - "@kurkle/color@0.3.4": - resolution: - { - integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==, - } + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - "@next/env@16.2.10": - resolution: - { - integrity: sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==, - } + '@next/env@16.2.10': + resolution: {integrity: sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==} - "@next/eslint-plugin-next@16.2.10": - resolution: - { - integrity: sha512-Gs8D2m21VnJeFo9qvYIIqJH94frWerWYu41BprU1pLtRVF7PCQNLiFZZ3fG+iPuj3K83Cwv/rt+msLOy8Qgu3Q==, - } - - "@next/swc-darwin-arm64@16.2.10": - resolution: - { - integrity: sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==, - } - engines: { node: ">= 10" } + '@next/swc-darwin-arm64@16.2.10': + resolution: {integrity: sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==} + engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - "@next/swc-darwin-x64@16.2.10": - resolution: - { - integrity: sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==, - } - engines: { node: ">= 10" } + '@next/swc-darwin-x64@16.2.10': + resolution: {integrity: sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==} + engines: {node: '>= 10'} cpu: [x64] os: [darwin] - "@next/swc-linux-arm64-gnu@16.2.10": - resolution: - { - integrity: sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==, - } - engines: { node: ">= 10" } + '@next/swc-linux-arm64-gnu@16.2.10': + resolution: {integrity: sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - "@next/swc-linux-arm64-musl@16.2.10": - resolution: - { - integrity: sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==, - } - engines: { node: ">= 10" } + '@next/swc-linux-arm64-musl@16.2.10': + resolution: {integrity: sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - "@next/swc-linux-x64-gnu@16.2.10": - resolution: - { - integrity: sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==, - } - engines: { node: ">= 10" } + '@next/swc-linux-x64-gnu@16.2.10': + resolution: {integrity: sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - "@next/swc-linux-x64-musl@16.2.10": - resolution: - { - integrity: sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==, - } - engines: { node: ">= 10" } + '@next/swc-linux-x64-musl@16.2.10': + resolution: {integrity: sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - "@next/swc-win32-arm64-msvc@16.2.10": - resolution: - { - integrity: sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==, - } - engines: { node: ">= 10" } + '@next/swc-win32-arm64-msvc@16.2.10': + resolution: {integrity: sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==} + engines: {node: '>= 10'} cpu: [arm64] os: [win32] - "@next/swc-win32-x64-msvc@16.2.10": - resolution: - { - integrity: sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==, - } - engines: { node: ">= 10" } + '@next/swc-win32-x64-msvc@16.2.10': + resolution: {integrity: sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==} + engines: {node: '>= 10'} cpu: [x64] os: [win32] - "@nodelib/fs.scandir@2.1.5": - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: ">= 8" } + '@otplib/core@12.0.1': + resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==} - "@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" } - - "@otplib/core@12.0.1": - resolution: - { - integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==, - } - - "@otplib/plugin-crypto@12.0.1": - resolution: - { - integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==, - } + '@otplib/plugin-crypto@12.0.1': + resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==} deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths - "@otplib/plugin-thirty-two@12.0.1": - resolution: - { - integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==, - } + '@otplib/plugin-thirty-two@12.0.1': + resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==} deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths - "@otplib/preset-default@12.0.1": - resolution: - { - integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==, - } + '@otplib/preset-default@12.0.1': + resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==} deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths - "@otplib/preset-v11@12.0.1": - resolution: - { - integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==, - } + '@otplib/preset-v11@12.0.1': + resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==} - "@panva/hkdf@1.2.1": - resolution: - { - integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==, - } + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} - "@parcel/watcher-android-arm64@2.5.6": - resolution: - { - integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] - "@parcel/watcher-darwin-arm64@2.5.6": - resolution: - { - integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] - "@parcel/watcher-darwin-x64@2.5.6": - resolution: - { - integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] - "@parcel/watcher-freebsd-x64@2.5.6": - resolution: - { - integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] - "@parcel/watcher-linux-arm-glibc@2.5.6": - resolution: - { - integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} + engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] libc: [glibc] - "@parcel/watcher-linux-arm-musl@2.5.6": - resolution: - { - integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} + engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] libc: [musl] - "@parcel/watcher-linux-arm64-glibc@2.5.6": - resolution: - { - integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] libc: [glibc] - "@parcel/watcher-linux-arm64-musl@2.5.6": - resolution: - { - integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] libc: [musl] - "@parcel/watcher-linux-x64-glibc@2.5.6": - resolution: - { - integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] libc: [glibc] - "@parcel/watcher-linux-x64-musl@2.5.6": - resolution: - { - integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] libc: [musl] - "@parcel/watcher-win32-arm64@2.5.6": - resolution: - { - integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} + engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] - "@parcel/watcher-win32-ia32@2.5.6": - resolution: - { - integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} + engines: {node: '>= 10.0.0'} cpu: [ia32] os: [win32] - "@parcel/watcher-win32-x64@2.5.6": - resolution: - { - integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} + engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] - "@parcel/watcher@2.5.6": - resolution: - { - integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==, - } - engines: { node: ">= 10.0.0" } + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} + engines: {node: '>= 10.0.0'} - "@petamoriken/float16@3.9.3": - resolution: - { - integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==, - } + '@petamoriken/float16@3.9.3': + resolution: {integrity: sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g==} - "@prisma/adapter-mariadb@7.8.0": - resolution: - { - integrity: sha512-mWsgcfbUjxB3qSzRlLs8E03vsKrqXzYK2zpx3e8u6wIgeHJM/sE46cuOGcYvHiZGmeQLCd3xL6YSSGM9QOLI6w==, - } + '@prisma/adapter-mariadb@7.8.0': + resolution: {integrity: sha512-mWsgcfbUjxB3qSzRlLs8E03vsKrqXzYK2zpx3e8u6wIgeHJM/sE46cuOGcYvHiZGmeQLCd3xL6YSSGM9QOLI6w==} - "@prisma/client-runtime-utils@7.8.0": - resolution: - { - integrity: sha512-5NQZztQ0oY/ADFkmd9gPuweH5A1/CCY8YQPorLLO0Mu6a87mY5gsnDkzmFmIHs9NFaLnZojzgddFVN4RpKYrdw==, - } + '@prisma/client-runtime-utils@7.8.0': + resolution: {integrity: sha512-5NQZztQ0oY/ADFkmd9gPuweH5A1/CCY8YQPorLLO0Mu6a87mY5gsnDkzmFmIHs9NFaLnZojzgddFVN4RpKYrdw==} - "@prisma/client@7.8.0": - resolution: - { - integrity: sha512-HFp3Dawv/3sU3JtlPha90IB+48lS7zHiH4LKZPjmcE8YH5P9DOXGPvo8dqOtO7MqLDd1p2hOWMcFlRT1DMblHw==, - } - engines: { node: ^20.19 || ^22.12 || >=24.0 } + '@prisma/client@7.8.0': + resolution: {integrity: sha512-HFp3Dawv/3sU3JtlPha90IB+48lS7zHiH4LKZPjmcE8YH5P9DOXGPvo8dqOtO7MqLDd1p2hOWMcFlRT1DMblHw==} + engines: {node: ^20.19 || ^22.12 || >=24.0} peerDependencies: - prisma: "*" - typescript: ">=5.4.0" + prisma: '*' + typescript: '>=5.4.0' peerDependenciesMeta: prisma: optional: true typescript: optional: true - "@prisma/config@7.8.0": - resolution: - { - integrity: sha512-HFESzd9rx2ZQxlK+TL7tu1HPvCqrHiL6LCxYykI2c34mvaUuIVVl3lYuicJD/MNnzgPnyeBEMlK4WTomJCV5jw==, - } + '@prisma/config@7.8.0': + resolution: {integrity: sha512-HFESzd9rx2ZQxlK+TL7tu1HPvCqrHiL6LCxYykI2c34mvaUuIVVl3lYuicJD/MNnzgPnyeBEMlK4WTomJCV5jw==} - "@prisma/debug@7.2.0": - resolution: - { - integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==, - } + '@prisma/debug@7.2.0': + resolution: {integrity: sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==} - "@prisma/debug@7.8.0": - resolution: - { - integrity: sha512-p+QZReysDUqXC+mk17q9a+Y/qzh4c2KYliDK30buYUyfrGeTGSyfmc0AIrJRhZJrLHhRiJa9Au/J72h3C+szvA==, - } + '@prisma/debug@7.8.0': + resolution: {integrity: sha512-p+QZReysDUqXC+mk17q9a+Y/qzh4c2KYliDK30buYUyfrGeTGSyfmc0AIrJRhZJrLHhRiJa9Au/J72h3C+szvA==} - "@prisma/dev@0.24.3": - resolution: - { - integrity: sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg==, - } + '@prisma/dev@0.24.3': + resolution: {integrity: sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg==} - "@prisma/driver-adapter-utils@7.8.0": - resolution: - { - integrity: sha512-/Q13o0ZT0rjc1Xk0Q9KhZYwuq2EW/vSbWUBKfgEKkaCuB/Sg6bqnjmTZqC5cD4d6y1vfFAEwBRzfzoSMIVJ55A==, - } + '@prisma/driver-adapter-utils@7.8.0': + resolution: {integrity: sha512-/Q13o0ZT0rjc1Xk0Q9KhZYwuq2EW/vSbWUBKfgEKkaCuB/Sg6bqnjmTZqC5cD4d6y1vfFAEwBRzfzoSMIVJ55A==} - "@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a": - resolution: - { - integrity: sha512-fJPQxCkLgA5EayWaW8eArgCvjJ+N+Kz3VyeNKMEeYiQC4alNkxRKFVAGxv/ZUzuJISKqdw+zGeDbS6mn6RCPOA==, - } + '@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a': + resolution: {integrity: sha512-fJPQxCkLgA5EayWaW8eArgCvjJ+N+Kz3VyeNKMEeYiQC4alNkxRKFVAGxv/ZUzuJISKqdw+zGeDbS6mn6RCPOA==} - "@prisma/engines@7.8.0": - resolution: - { - integrity: sha512-jx3rCnNNrt5uzbkKlegtQ2GZHxSlihMCzutgT/BP6UIDF1r9tDI39hV/0T/cHZgzJ3ELbuQPXlVZy+Y1n0pcgw==, - } + '@prisma/engines@7.8.0': + resolution: {integrity: sha512-jx3rCnNNrt5uzbkKlegtQ2GZHxSlihMCzutgT/BP6UIDF1r9tDI39hV/0T/cHZgzJ3ELbuQPXlVZy+Y1n0pcgw==} - "@prisma/fetch-engine@7.8.0": - resolution: - { - integrity: sha512-gwB0Euiz/DDRyxFRpLXYlK3RfaZUj1c5dAYMuhZYfApg7arknJlcb9bIsOHDppJmbqYaVA+yBIiFMDBfprsNPQ==, - } + '@prisma/fetch-engine@7.8.0': + resolution: {integrity: sha512-gwB0Euiz/DDRyxFRpLXYlK3RfaZUj1c5dAYMuhZYfApg7arknJlcb9bIsOHDppJmbqYaVA+yBIiFMDBfprsNPQ==} - "@prisma/get-platform@7.2.0": - resolution: - { - integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==, - } + '@prisma/get-platform@7.2.0': + resolution: {integrity: sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==} - "@prisma/get-platform@7.8.0": - resolution: - { - integrity: sha512-WlxgRGnolL8VH2EmkH1R/DkKNr/mVdS3G2h42IZFFZ3eUrH9OT6t73kIOSlkkrv50wG123Iq8d96ufv5LlZktw==, - } + '@prisma/get-platform@7.8.0': + resolution: {integrity: sha512-WlxgRGnolL8VH2EmkH1R/DkKNr/mVdS3G2h42IZFFZ3eUrH9OT6t73kIOSlkkrv50wG123Iq8d96ufv5LlZktw==} - "@prisma/query-plan-executor@7.2.0": - resolution: - { - integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==, - } + '@prisma/query-plan-executor@7.2.0': + resolution: {integrity: sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==} - "@prisma/streams-local@0.1.2": - resolution: - { - integrity: sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg==, - } - engines: { bun: ">=1.3.6", node: ">=22.0.0" } + '@prisma/streams-local@0.1.2': + resolution: {integrity: sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg==} + engines: {bun: '>=1.3.6', node: '>=22.0.0'} - "@prisma/studio-core@0.27.3": - resolution: - { - integrity: sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw==, - } - engines: { node: ^20.19 || ^22.12 || >=24.0, pnpm: "8" } + '@prisma/studio-core@0.27.3': + resolution: {integrity: sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw==} + engines: {node: ^20.19 || ^22.12 || >=24.0, pnpm: '8'} peerDependencies: - "@types/react": ^18.0.0 || ^19.0.0 + '@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/number@1.1.2": - resolution: - { - integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==, - } + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} - "@radix-ui/primitive@1.1.3": - resolution: - { - integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==, - } + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - "@radix-ui/primitive@1.1.5": - resolution: - { - integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==, - } + '@radix-ui/primitive@1.1.5': + resolution: {integrity: sha512-d86WIWFYNtGA0H/d8exstrTRTp7eWJYlYJbtNofxr/3ljupZYn6EFDG/Qgu/0Kc8v7yMUxySagqJsL1+PdYjWg==} - "@radix-ui/react-accessible-icon@1.1.11": - resolution: - { - integrity: sha512-HQDOFTKwSnmUij6l54wYJJtxTAnxI71+YJLOrjm2ladFB8HAV5Jt7hwaZPhWTGBkYoW4+ZAOfNZrLDh/qvxSYA==, - } + '@radix-ui/react-accessible-icon@1.1.11': + resolution: {integrity: sha512-HQDOFTKwSnmUij6l54wYJJtxTAnxI71+YJLOrjm2ladFB8HAV5Jt7hwaZPhWTGBkYoW4+ZAOfNZrLDh/qvxSYA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-accordion@1.2.16": - resolution: - { - integrity: sha512-BpZJNmetujnGgUI6OX0jEhEmlA46WPqgub8Rv09Kyquwd0cc1ndMKpiPYCjmBU6KSSRPAMtgLpEoZSG/tdNIWQ==, - } + '@radix-ui/react-accordion@1.2.16': + resolution: {integrity: sha512-BpZJNmetujnGgUI6OX0jEhEmlA46WPqgub8Rv09Kyquwd0cc1ndMKpiPYCjmBU6KSSRPAMtgLpEoZSG/tdNIWQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-alert-dialog@1.1.19": - resolution: - { - integrity: sha512-FA7n1f6D/DwGE0+AWxiY5LacNbbExQuEgMubeG06idEaH+mSLuf9dp/qBNqOnvbTQ+4gZ2ue1RATF1Ub91Mg5g==, - } + '@radix-ui/react-alert-dialog@1.1.19': + resolution: {integrity: sha512-FA7n1f6D/DwGE0+AWxiY5LacNbbExQuEgMubeG06idEaH+mSLuf9dp/qBNqOnvbTQ+4gZ2ue1RATF1Ub91Mg5g==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-arrow@1.1.11": - resolution: - { - integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==, - } + '@radix-ui/react-arrow@1.1.11': + resolution: {integrity: sha512-Kdil9BB1rIFC/khmf4hC35bn8701AJcizTU7G7cUbEbk5XqqbjDuHW60uUfKqO5WojjZcbAW51Q7P0hRmMLw8A==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-aspect-ratio@1.1.11": - resolution: - { - integrity: sha512-IUAhIVpBUvP5NNICjlaB1OFmtRLGqQqTF3ZOSGPoq3XeLXRFtHiWTRxSVEULgOd9GQR2c7tsYqDnhUennapZnw==, - } + '@radix-ui/react-aspect-ratio@1.1.11': + resolution: {integrity: sha512-IUAhIVpBUvP5NNICjlaB1OFmtRLGqQqTF3ZOSGPoq3XeLXRFtHiWTRxSVEULgOd9GQR2c7tsYqDnhUennapZnw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-avatar@1.2.2": - resolution: - { - integrity: sha512-sST0qh8GzOB7besQ3tMLWLyngnRuSk0gc/Hm+667KYKQFCt6Y6ZXv25WlqM7dIDK54ULCh5+CHmk4LIolzfz+A==, - } + '@radix-ui/react-avatar@1.2.2': + resolution: {integrity: sha512-sST0qh8GzOB7besQ3tMLWLyngnRuSk0gc/Hm+667KYKQFCt6Y6ZXv25WlqM7dIDK54ULCh5+CHmk4LIolzfz+A==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-checkbox@1.3.7": - resolution: - { - integrity: sha512-JroKHfQBfh+fDuzpPsBC+pESkhuq8ql4hljTguz8MWnS35cISr3d/Jhl9kYrB44FlDtxCArYdDvTx+BSsJ64rQ==, - } + '@radix-ui/react-checkbox@1.3.7': + resolution: {integrity: sha512-JroKHfQBfh+fDuzpPsBC+pESkhuq8ql4hljTguz8MWnS35cISr3d/Jhl9kYrB44FlDtxCArYdDvTx+BSsJ64rQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-collapsible@1.1.16": - resolution: - { - integrity: sha512-opfXRe6nnzyGmCDPx+l1Aqo/RbqWtQal2FnsBqF9hhePp6j0LsRoBaRxcMOlTv+uYTJVtWYZKg9t9wTe+BA/ZA==, - } + '@radix-ui/react-collapsible@1.1.16': + resolution: {integrity: sha512-opfXRe6nnzyGmCDPx+l1Aqo/RbqWtQal2FnsBqF9hhePp6j0LsRoBaRxcMOlTv+uYTJVtWYZKg9t9wTe+BA/ZA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-collection@1.1.12": - resolution: - { - integrity: sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==, - } + '@radix-ui/react-collection@1.1.12': + resolution: {integrity: sha512-nb67INpE0IahJKN7EYPp9m9YGwYeKlnzxT3MwXVkgCskaSJia97kG4T0ywpjNUSSnoJk/uvk12V8vbrEHEj+/Q==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-compose-refs@1.1.2": - resolution: - { - integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==, - } + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-compose-refs@1.1.3": - resolution: - { - integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==, - } + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-context-menu@2.3.3": - resolution: - { - integrity: sha512-PS+gKE0z2prJ74Y0sM+brAGK4mYOHIR7TlcV5EJgUQ6E0xMvyswkK2X4yRqyganrzsRL+WCSKAPu0NQITICRWg==, - } + '@radix-ui/react-context-menu@2.3.3': + resolution: {integrity: sha512-PS+gKE0z2prJ74Y0sM+brAGK4mYOHIR7TlcV5EJgUQ6E0xMvyswkK2X4yRqyganrzsRL+WCSKAPu0NQITICRWg==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-context@1.2.0": - resolution: - { - integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==, - } + '@radix-ui/react-context@1.2.0': + resolution: {integrity: sha512-fOE+JtN9rygNZkCnHRBEP0TAvLldlhyOxMsbwFvTP4nAs+nBmfnna+o/Zski2wkmY1YMrFC0aSzsHoLY47iLrg==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-dialog@1.1.19": - resolution: - { - integrity: sha512-+HhbN2+YtkRgVirjZ2afMeutQRuGOrdkWR5+EFC58SJojGmtyNQwYzgi6tHBpOxvFHefMtPeHdgtjz0BOGxFQg==, - } + '@radix-ui/react-dialog@1.1.19': + resolution: {integrity: sha512-+HhbN2+YtkRgVirjZ2afMeutQRuGOrdkWR5+EFC58SJojGmtyNQwYzgi6tHBpOxvFHefMtPeHdgtjz0BOGxFQg==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-direction@1.1.2": - resolution: - { - integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==, - } + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-dismissable-layer@1.1.15": - resolution: - { - integrity: sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==, - } + '@radix-ui/react-dismissable-layer@1.1.15': + resolution: {integrity: sha512-b0XaRlzn2QKuo10XyNgi2DAJDf5XC9d1nD3FJcuvCjbR7+4Ad28zmZsLsqx+hvDEzMnRuZaZxZm9gYObV6RmRA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-dropdown-menu@2.1.20": - resolution: - { - integrity: sha512-slfm+rRaZRuQBvHq60lXvSVUPhid0IPtjSZzIuUlWZMUs01iYZNlGS3mJgRD3ChLQVBAYlKiL/tFyWGX+dz8Xw==, - } + '@radix-ui/react-dropdown-menu@2.1.20': + resolution: {integrity: sha512-slfm+rRaZRuQBvHq60lXvSVUPhid0IPtjSZzIuUlWZMUs01iYZNlGS3mJgRD3ChLQVBAYlKiL/tFyWGX+dz8Xw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-focus-guards@1.1.4": - resolution: - { - integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==, - } + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-focus-scope@1.1.12": - resolution: - { - integrity: sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==, - } + '@radix-ui/react-focus-scope@1.1.12': + resolution: {integrity: sha512-jjk/lqTeNL0azUx5ZYzVrl4NgaDIrdzTNE4mABV9yBFI7FQqN7pIgzV1bTleUezP2QiTGA1BFTqY8MegDgWX9A==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-form@0.1.12": - resolution: - { - integrity: sha512-JTX94E4LDL91rzLg7X0mHPdxr0A8JEdVwZEmeOwZJSMDHCGW5DFtSlTSJozUyUs807IQmnvbfzKZFVCK5DmkqQ==, - } + '@radix-ui/react-form@0.1.12': + resolution: {integrity: sha512-JTX94E4LDL91rzLg7X0mHPdxr0A8JEdVwZEmeOwZJSMDHCGW5DFtSlTSJozUyUs807IQmnvbfzKZFVCK5DmkqQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-hover-card@1.1.19": - resolution: - { - integrity: sha512-2KTgMLQtKvicznQgbindEI2RZ3QbDIwU5gabjUPwFJsormjGDz+rUvO4NANmYwzEEpTcTONUt33vBHIfTIVSfw==, - } + '@radix-ui/react-hover-card@1.1.19': + resolution: {integrity: sha512-2KTgMLQtKvicznQgbindEI2RZ3QbDIwU5gabjUPwFJsormjGDz+rUvO4NANmYwzEEpTcTONUt33vBHIfTIVSfw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-id@1.1.2": - resolution: - { - integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==, - } + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-label@2.1.11": - resolution: - { - integrity: sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ==, - } + '@radix-ui/react-label@2.1.11': + resolution: {integrity: sha512-3PKvDDxOn62k0oV1n4QtNtD2vpu+zYjXR7ojLBPaO6SPvhy53yg0vAmgNeBQeJW5rV3dffoRG+HYfLBZuzw0CQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-menu@2.1.20": - resolution: - { - integrity: sha512-VsUrXxFe9d2ScbZF0fR/oPR1+qjyeLs5p0jzG8h90puMoA9bq4SirYlXbE+USRg9Q2qTeJSFNqjw2nts8jJe4w==, - } + '@radix-ui/react-menu@2.1.20': + resolution: {integrity: sha512-VsUrXxFe9d2ScbZF0fR/oPR1+qjyeLs5p0jzG8h90puMoA9bq4SirYlXbE+USRg9Q2qTeJSFNqjw2nts8jJe4w==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-menubar@1.1.20": - resolution: - { - integrity: sha512-gzFZvybgmwYsFBWDqanycIoEYnhyk8MMnuLamdFVHUZYGp4COM+sqXiwbnn0VMWqGLeeU7GV7jm+dXRa+Wufag==, - } + '@radix-ui/react-menubar@1.1.20': + resolution: {integrity: sha512-gzFZvybgmwYsFBWDqanycIoEYnhyk8MMnuLamdFVHUZYGp4COM+sqXiwbnn0VMWqGLeeU7GV7jm+dXRa+Wufag==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-navigation-menu@1.2.18": - resolution: - { - integrity: sha512-K9HiuxZ6xCwSaHcIuUpxyhy4w5gpwzWjh9dHTSbMN3Ix4qAyVObS9RlU3zMycb0PO3v9Tpk0BXMwWvXOUbVXew==, - } + '@radix-ui/react-navigation-menu@1.2.18': + resolution: {integrity: sha512-K9HiuxZ6xCwSaHcIuUpxyhy4w5gpwzWjh9dHTSbMN3Ix4qAyVObS9RlU3zMycb0PO3v9Tpk0BXMwWvXOUbVXew==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-one-time-password-field@0.1.12": - resolution: - { - integrity: sha512-nQLu5OAcORDQp1EHAv6k3mJGV1hjMTw2NTGVAsGE1g/mWeNqAd1R5jyaAs3U+A8ZD/W8XNPY2yKT0ZdQnqo3NA==, - } + '@radix-ui/react-one-time-password-field@0.1.12': + resolution: {integrity: sha512-nQLu5OAcORDQp1EHAv6k3mJGV1hjMTw2NTGVAsGE1g/mWeNqAd1R5jyaAs3U+A8ZD/W8XNPY2yKT0ZdQnqo3NA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-password-toggle-field@0.1.7": - resolution: - { - integrity: sha512-gB1Mr8vzdv1XzDjrtJTXmL0JORRs1B4g7ngUs0F+H2VvMOwXTZMTmLCl0wZZ3m7ylX8TssI7NCvgiSHmLuTm/A==, - } + '@radix-ui/react-password-toggle-field@0.1.7': + resolution: {integrity: sha512-gB1Mr8vzdv1XzDjrtJTXmL0JORRs1B4g7ngUs0F+H2VvMOwXTZMTmLCl0wZZ3m7ylX8TssI7NCvgiSHmLuTm/A==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-popover@1.1.19": - resolution: - { - integrity: sha512-jkrTdQVxnIB8fpn0NyyxW9CTB5aCXZZelVz5z+Xmii6g5WxMqS3fInNslZ63puP39+Puu4jYohUK31y3dT87gQ==, - } + '@radix-ui/react-popover@1.1.19': + resolution: {integrity: sha512-jkrTdQVxnIB8fpn0NyyxW9CTB5aCXZZelVz5z+Xmii6g5WxMqS3fInNslZ63puP39+Puu4jYohUK31y3dT87gQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-popper@1.3.3": - resolution: - { - integrity: sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==, - } + '@radix-ui/react-popper@1.3.3': + resolution: {integrity: sha512-mS7dGpyjv6b+gsDjLF7e0ia1W4Im1B1hSCy2yuXlHuvnZxHKagfDaobt/KAKt27EpZMit2pss8eJBVyVjEWM+g==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-portal@1.1.13": - resolution: - { - integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==, - } + '@radix-ui/react-portal@1.1.13': + resolution: {integrity: sha512-z3oXfmaHLJTF1wktbjgD6cn9jiEbq3WSondB10LIuIt2m2Ym4iJlrW04/euMwENDdWDdE7z+OuY7Qyp1YpRSwA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-presence@1.1.7": - resolution: - { - integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==, - } + '@radix-ui/react-presence@1.1.7': + resolution: {integrity: sha512-zBZ4QM5XG3JRanDmqXYf3MD6th4AFXFmgU6KNMFzUaV6F3uw9I5/zjMUvFriSEn5ewo1nxuibvyxJdmLlDcslA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-primitive@2.1.3": - resolution: - { - integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==, - } + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-primitive@2.1.7": - resolution: - { - integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==, - } + '@radix-ui/react-primitive@2.1.7': + resolution: {integrity: sha512-bC3NiwsprbxKjuon9l7X6BUTw7FPVzEYaL92MPEY5SCd/9hUTPXVFtVwRix7778wtRsVao+zE062gL79FZleeQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-progress@1.1.12": - resolution: - { - integrity: sha512-ZPHyI0JyzoH/rP0tq2uRaIZTj/4s8+kAbqPz+e2N8+ejHvwPJ889dHhqn+vh7PNvNeq+boAoH9yzqeoShzwF2w==, - } + '@radix-ui/react-progress@1.1.12': + resolution: {integrity: sha512-ZPHyI0JyzoH/rP0tq2uRaIZTj/4s8+kAbqPz+e2N8+ejHvwPJ889dHhqn+vh7PNvNeq+boAoH9yzqeoShzwF2w==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-radio-group@1.4.3": - resolution: - { - integrity: sha512-WwZFjWV4s3aC1QtR3k04R+oANHtX2q6fgKlc7MCEiDNlnTxCZ3H8k3mHtEgVlOejystwk1WQgarQhNOQZ2bK1g==, - } + '@radix-ui/react-radio-group@1.4.3': + resolution: {integrity: sha512-WwZFjWV4s3aC1QtR3k04R+oANHtX2q6fgKlc7MCEiDNlnTxCZ3H8k3mHtEgVlOejystwk1WQgarQhNOQZ2bK1g==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-roving-focus@1.1.15": - resolution: - { - integrity: sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==, - } + '@radix-ui/react-roving-focus@1.1.15': + resolution: {integrity: sha512-40svmmugfM3mUN7VUDGVE1tQGOhyi8enlGD0CNJEcMM36C1f71PKM21DFgNHUfem0XnA+d8H8oN3Z9ZpJjSslg==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-scroll-area@1.2.14": - resolution: - { - integrity: sha512-bBODCWZK7JTbQLHs0uIP4f73wIWatakK4OS33UzkR1x897wu0PuO658a3f+6P2GEGyDzGYMuHRatMVoAk9WZTw==, - } + '@radix-ui/react-scroll-area@1.2.14': + resolution: {integrity: sha512-bBODCWZK7JTbQLHs0uIP4f73wIWatakK4OS33UzkR1x897wu0PuO658a3f+6P2GEGyDzGYMuHRatMVoAk9WZTw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-select@2.3.3": - resolution: - { - integrity: sha512-L5RQTXz6Anxsf9CCv+pTgiAsUpyVj7rJxsGtmhFaEOJ++cVfXucv4qWfsIO0AIB4NAhi3yovWGVMKKS1Xf1Wrg==, - } + '@radix-ui/react-select@2.3.3': + resolution: {integrity: sha512-L5RQTXz6Anxsf9CCv+pTgiAsUpyVj7rJxsGtmhFaEOJ++cVfXucv4qWfsIO0AIB4NAhi3yovWGVMKKS1Xf1Wrg==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-separator@1.1.11": - resolution: - { - integrity: sha512-jRhe86+8PF7VZ1u14eOWVOuh2BuAhALg/FT1VcMC4OHedMTRUazDnDlKTt+yxo5cRNKHMfmvZ4sSQtWDeMV4CQ==, - } + '@radix-ui/react-separator@1.1.11': + resolution: {integrity: sha512-jRhe86+8PF7VZ1u14eOWVOuh2BuAhALg/FT1VcMC4OHedMTRUazDnDlKTt+yxo5cRNKHMfmvZ4sSQtWDeMV4CQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-slider@1.4.3": - resolution: - { - integrity: sha512-CWVVj+XaTom0SKCqw1EUgb0NuiLwS+N3OFG73mVEezKEjgNIvZiu0EevMelSSU+CbX3owbqJweG2gPU31WGC5A==, - } + '@radix-ui/react-slider@1.4.3': + resolution: {integrity: sha512-CWVVj+XaTom0SKCqw1EUgb0NuiLwS+N3OFG73mVEezKEjgNIvZiu0EevMelSSU+CbX3owbqJweG2gPU31WGC5A==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-slot@1.2.3": - resolution: - { - integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==, - } + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-slot@1.3.0": - resolution: - { - integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==, - } + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-switch@1.3.3": - resolution: - { - integrity: sha512-1+mlB4/lxJfk5tgJ4g+R5mUCbRpPE1T9+UsEyeLYbGgMtwiMgmuTnfKz4Mw1nHALHjuwyxw4MLd4cSHn6pNSlQ==, - } + '@radix-ui/react-switch@1.3.3': + resolution: {integrity: sha512-1+mlB4/lxJfk5tgJ4g+R5mUCbRpPE1T9+UsEyeLYbGgMtwiMgmuTnfKz4Mw1nHALHjuwyxw4MLd4cSHn6pNSlQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-tabs@1.1.17": - resolution: - { - integrity: sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ==, - } + '@radix-ui/react-tabs@1.1.17': + resolution: {integrity: sha512-nRyXnrAVCwjeXcHbvEbLS6ndbTeKHG1RqCP4A8Gw5L4cemDzPXdD8rAmr6wet0v57R69wGvuIIsFjHSVkZiMzQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-toast@1.2.19": - resolution: - { - integrity: sha512-SxfVZfVOibWKWdkf0Xx1awW2d09fQu4V4PXDY1j5hi4MVf7MWdJZqTBJMa1KWtOr1S6GGtCk02nniZ0Iia+dHw==, - } + '@radix-ui/react-toast@1.2.19': + resolution: {integrity: sha512-SxfVZfVOibWKWdkf0Xx1awW2d09fQu4V4PXDY1j5hi4MVf7MWdJZqTBJMa1KWtOr1S6GGtCk02nniZ0Iia+dHw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-toggle-group@1.1.15": - resolution: - { - integrity: sha512-gIC5Q+Xljg7lmUdzSuDoy0t97yZn1sZl00Ra37ZvKrYdWnQLU6sWLd09yG8cIB9jUAlQfHgJ2ACAG00MFwsqSQ==, - } + '@radix-ui/react-toggle-group@1.1.15': + resolution: {integrity: sha512-gIC5Q+Xljg7lmUdzSuDoy0t97yZn1sZl00Ra37ZvKrYdWnQLU6sWLd09yG8cIB9jUAlQfHgJ2ACAG00MFwsqSQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-toggle@1.1.10": - resolution: - { - integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==, - } + '@radix-ui/react-toggle@1.1.10': + resolution: {integrity: sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-toggle@1.1.14": - resolution: - { - integrity: sha512-QI/hB65XKWACA66P64A+aHxtLUgHJeJLkaQa+awUNXT6T3swndtY5DojeHA+vldrTspMTtFBd7HfZ9QGbM1Qrw==, - } + '@radix-ui/react-toggle@1.1.14': + resolution: {integrity: sha512-QI/hB65XKWACA66P64A+aHxtLUgHJeJLkaQa+awUNXT6T3swndtY5DojeHA+vldrTspMTtFBd7HfZ9QGbM1Qrw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-toolbar@1.1.15": - resolution: - { - integrity: sha512-t/iEuVjUnXXtrsGK40AA43uIx37sn3AqZ7oAVnPICK6lFJP6dzMzWR3U9b6eCfFjb6wtSEqkJ9Rn9xDjiOx20g==, - } + '@radix-ui/react-toolbar@1.1.15': + resolution: {integrity: sha512-t/iEuVjUnXXtrsGK40AA43uIx37sn3AqZ7oAVnPICK6lFJP6dzMzWR3U9b6eCfFjb6wtSEqkJ9Rn9xDjiOx20g==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-tooltip@1.2.12": - resolution: - { - integrity: sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==, - } + '@radix-ui/react-tooltip@1.2.12': + resolution: {integrity: sha512-U3HoftgWnmla78vzQbLvKKb7bUYJxoiiqYFzp1wu/TBMyDqMZSuCl3aRICsD6EfVEwcJD2mumGDGUXLFVqQHKA==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/react-use-callback-ref@1.1.2": - resolution: - { - integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==, - } + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-controllable-state@1.2.2": - resolution: - { - integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==, - } + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-controllable-state@1.2.3": - resolution: - { - integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==, - } + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-effect-event@0.0.2": - resolution: - { - integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==, - } + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-effect-event@0.0.3": - resolution: - { - integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==, - } + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-escape-keydown@1.1.3": - resolution: - { - integrity: sha512-3wEkMiPHXha/2VadZ68rYBcmYnPINVGl4Y3gtcM7fKRjANk0OscK+cdqBgUWdozb7YJxsh0vefM7vgAMHXOjqg==, - } + '@radix-ui/react-use-escape-keydown@1.1.3': + resolution: {integrity: sha512-3wEkMiPHXha/2VadZ68rYBcmYnPINVGl4Y3gtcM7fKRjANk0OscK+cdqBgUWdozb7YJxsh0vefM7vgAMHXOjqg==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-is-hydrated@0.1.1": - resolution: - { - integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==, - } + '@radix-ui/react-use-is-hydrated@0.1.1': + resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-layout-effect@1.1.1": - resolution: - { - integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==, - } + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-layout-effect@1.1.2": - resolution: - { - integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==, - } + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-previous@1.1.2": - resolution: - { - integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==, - } + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-rect@1.1.2": - resolution: - { - integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==, - } + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-use-size@1.1.2": - resolution: - { - integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==, - } + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@radix-ui/react-visually-hidden@1.2.7": - resolution: - { - integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==, - } + '@radix-ui/react-visually-hidden@1.2.7': + resolution: {integrity: sha512-1wNZBggTDK3GRuuQ6nP4k2yi7a6l7I5qbMPbZcRsrGsGVead/f/d5FhEzUvqFs0bcrDLx7n1zKQ3JvLR6whaaw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@radix-ui/rect@1.1.2": - resolution: - { - integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==, - } + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} - "@rollup/rollup-android-arm-eabi@4.62.2": - resolution: - { - integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==, - } + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] - "@rollup/rollup-android-arm64@4.62.2": - resolution: - { - integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==, - } + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] - "@rollup/rollup-darwin-arm64@4.62.2": - resolution: - { - integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==, - } + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] - "@rollup/rollup-darwin-x64@4.62.2": - resolution: - { - integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==, - } + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] - "@rollup/rollup-freebsd-arm64@4.62.2": - resolution: - { - integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==, - } + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] - "@rollup/rollup-freebsd-x64@4.62.2": - resolution: - { - integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==, - } + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] - "@rollup/rollup-linux-arm-gnueabihf@4.62.2": - resolution: - { - integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==, - } + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] libc: [glibc] - "@rollup/rollup-linux-arm-musleabihf@4.62.2": - resolution: - { - integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==, - } + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] libc: [musl] - "@rollup/rollup-linux-arm64-gnu@4.62.2": - resolution: - { - integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==, - } + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-arm64-musl@4.62.2": - resolution: - { - integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==, - } + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] libc: [musl] - "@rollup/rollup-linux-loong64-gnu@4.62.2": - resolution: - { - integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==, - } + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-loong64-musl@4.62.2": - resolution: - { - integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==, - } + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] libc: [musl] - "@rollup/rollup-linux-ppc64-gnu@4.62.2": - resolution: - { - integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==, - } + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-ppc64-musl@4.62.2": - resolution: - { - integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==, - } + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] libc: [musl] - "@rollup/rollup-linux-riscv64-gnu@4.62.2": - resolution: - { - integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==, - } + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-riscv64-musl@4.62.2": - resolution: - { - integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==, - } + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] libc: [musl] - "@rollup/rollup-linux-s390x-gnu@4.62.2": - resolution: - { - integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==, - } + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] libc: [glibc] - "@rollup/rollup-linux-x64-gnu@4.62.2": - resolution: - { - integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==, - } + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] libc: [glibc] - "@rollup/rollup-linux-x64-musl@4.62.2": - resolution: - { - integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==, - } + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] libc: [musl] - "@rollup/rollup-openbsd-x64@4.62.2": - resolution: - { - integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==, - } + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} cpu: [x64] os: [openbsd] - "@rollup/rollup-openharmony-arm64@4.62.2": - resolution: - { - integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==, - } + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} cpu: [arm64] os: [openharmony] - "@rollup/rollup-win32-arm64-msvc@4.62.2": - resolution: - { - integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==, - } + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] - "@rollup/rollup-win32-ia32-msvc@4.62.2": - resolution: - { - integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==, - } + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] - "@rollup/rollup-win32-x64-gnu@4.62.2": - resolution: - { - integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==, - } + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] - "@rollup/rollup-win32-x64-msvc@4.62.2": - resolution: - { - integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==, - } + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} cpu: [x64] os: [win32] - "@schummar/icu-type-parser@1.21.5": - resolution: - { - integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==, - } + '@schummar/icu-type-parser@1.21.5': + resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} - "@stablelib/base64@1.0.1": - resolution: - { - integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==, - } + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} - "@standard-schema/spec@1.1.0": - resolution: - { - integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, - } + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - "@standard-schema/utils@0.3.0": - resolution: - { - integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==, - } + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - "@swc/core-darwin-arm64@1.15.43": - resolution: - { - integrity: sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==, - } - engines: { node: ">=10" } + '@swc/core-darwin-arm64@1.15.43': + resolution: {integrity: sha512-v1aVuvXdo/BHxJzco9V2xpHrvwWmhfS8t6gziY5wJxd+Z2h8AeJRnAwPD8itCDaGXVBwJ/CaKfxEzTkG0Va0OA==} + engines: {node: '>=10'} cpu: [arm64] os: [darwin] - "@swc/core-darwin-x64@1.15.43": - resolution: - { - integrity: sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==, - } - engines: { node: ">=10" } + '@swc/core-darwin-x64@1.15.43': + resolution: {integrity: sha512-lp3d4Lamc8dt5huYdGLSR+9hLxmfr1jb0l+4XXG2zPqZwYWRN9R0U2qYoTrggiU2RWW0oV9VbWM3kBnqIc2kdQ==} + engines: {node: '>=10'} cpu: [x64] os: [darwin] - "@swc/core-linux-arm-gnueabihf@1.15.43": - resolution: - { - integrity: sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==, - } - engines: { node: ">=10" } + '@swc/core-linux-arm-gnueabihf@1.15.43': + resolution: {integrity: sha512-JWTQQELtsG5GgphDrr/XqqmM2pDN3cZqbMS0Mrg+iTiXL3F74sn/S2IyYE/5u4h2KLkTf9qQ7dXyxsbx7YzkeA==} + engines: {node: '>=10'} cpu: [arm] os: [linux] - "@swc/core-linux-arm64-gnu@1.15.43": - resolution: - { - integrity: sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==, - } - engines: { node: ">=10" } + '@swc/core-linux-arm64-gnu@1.15.43': + resolution: {integrity: sha512-B4otJRdPWIsmiSBf0uG7Z/+vMWmkufjz5MmYxubwKuZazDW14Zd3symga1N62QR4RT+kEFeHEgsXfZGyn/w0hw==} + engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [glibc] - "@swc/core-linux-arm64-musl@1.15.43": - resolution: - { - integrity: sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==, - } - engines: { node: ">=10" } + '@swc/core-linux-arm64-musl@1.15.43': + resolution: {integrity: sha512-6zB6OnpViBxYy4tgY3v2i6AZY9fwkcHZ032UOwtwUuW1d19sdT07qF0kZe6/3UR1tUaK6jjg2rmVcUIBCEYVjQ==} + engines: {node: '>=10'} cpu: [arm64] os: [linux] libc: [musl] - "@swc/core-linux-ppc64-gnu@1.15.43": - resolution: - { - integrity: sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==, - } - engines: { node: ">=10" } + '@swc/core-linux-ppc64-gnu@1.15.43': + resolution: {integrity: sha512-coxE1ZWdB3uSDVNoEtYNrRi/1epvckZx9cTJ8ICUxTMTxGk+yvQ/Twacp3ruZSaMPGCriUjP86C37VhaT6nyRg==} + engines: {node: '>=10'} cpu: [ppc64] os: [linux] libc: [glibc] - "@swc/core-linux-s390x-gnu@1.15.43": - resolution: - { - integrity: sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==, - } - engines: { node: ">=10" } + '@swc/core-linux-s390x-gnu@1.15.43': + resolution: {integrity: sha512-lXfLhs+LpBsD5inuYx+YDH5WsPPBQ95KPUiy8P5wq9ob9xKDZFqwNfU2QW6bGO8NqRO/H9JQomTSt5Yyh+FGfA==} + engines: {node: '>=10'} cpu: [s390x] os: [linux] libc: [glibc] - "@swc/core-linux-x64-gnu@1.15.43": - resolution: - { - integrity: sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==, - } - engines: { node: ">=10" } + '@swc/core-linux-x64-gnu@1.15.43': + resolution: {integrity: sha512-07XnKwTmKy8TGOZG3D9fRnLWGynxPjwQnZLVmBFbo6F+7vHYzBIOuwXEhemrChBWb6yDNZsVCcMWCPX6FDD2xg==} + engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [glibc] - "@swc/core-linux-x64-musl@1.15.43": - resolution: - { - integrity: sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==, - } - engines: { node: ">=10" } + '@swc/core-linux-x64-musl@1.15.43': + resolution: {integrity: sha512-TJc+bsSIaBh+hZvZ5GRtW/K1bw66TJ9vsUwvVIsZdiWxU5ObLwZvfcnZ3UpgVfMnFibRes9uriJrQNBHEEogRQ==} + engines: {node: '>=10'} cpu: [x64] os: [linux] libc: [musl] - "@swc/core-win32-arm64-msvc@1.15.43": - resolution: - { - integrity: sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==, - } - engines: { node: ">=10" } + '@swc/core-win32-arm64-msvc@1.15.43': + resolution: {integrity: sha512-jfd7s2/bUQYkOHLs+LWQNKZdmDa8+sufKLllhpWAhVQ2GDCwsHe3vR/j+OSiItZNtkzFuaawa3+SAKz9y5gYfw==} + engines: {node: '>=10'} cpu: [arm64] os: [win32] - "@swc/core-win32-ia32-msvc@1.15.43": - resolution: - { - integrity: sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==, - } - engines: { node: ">=10" } + '@swc/core-win32-ia32-msvc@1.15.43': + resolution: {integrity: sha512-rLAE8JvucqEW1ZGohxPQrQWPBQeJG4+ypKbWfdlU/qmKScvCkxf9/Jxnzki1dkUQCQ7P5Enp13RlvqOlvx/32g==} + engines: {node: '>=10'} cpu: [ia32] os: [win32] - "@swc/core-win32-x64-msvc@1.15.43": - resolution: - { - integrity: sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==, - } - engines: { node: ">=10" } + '@swc/core-win32-x64-msvc@1.15.43': + resolution: {integrity: sha512-h8MLDHZcfIukwQWj03rIJZx1I0E81AYj2X7J/nGErG4nz+QAv6G1Z+peotvinL3lqpbo32tLYSMFo32/ySzxKg==} + engines: {node: '>=10'} cpu: [x64] os: [win32] - "@swc/core@1.15.43": - resolution: - { - integrity: sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==, - } - engines: { node: ">=10" } + '@swc/core@1.15.43': + resolution: {integrity: sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==} + engines: {node: '>=10'} peerDependencies: - "@swc/helpers": ">=0.5.17" + '@swc/helpers': '>=0.5.17' peerDependenciesMeta: - "@swc/helpers": + '@swc/helpers': optional: true - "@swc/counter@0.1.3": - resolution: - { - integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==, - } + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - "@swc/helpers@0.5.15": - resolution: - { - integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==, - } + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - "@swc/types@0.1.27": - resolution: - { - integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==, - } + '@swc/types@0.1.27': + resolution: {integrity: sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==} - "@tailwindcss/forms@0.5.11": - resolution: - { - integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==, - } + '@tailwindcss/forms@0.5.11': + resolution: {integrity: sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==} peerDependencies: - tailwindcss: ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1' - "@tailwindcss/node@4.3.2": - resolution: - { - integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==, - } + '@tailwindcss/node@4.3.2': + resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} - "@tailwindcss/oxide-android-arm64@4.3.2": - resolution: - { - integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-android-arm64@4.3.2': + resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==} + engines: {node: '>= 20'} cpu: [arm64] os: [android] - "@tailwindcss/oxide-darwin-arm64@4.3.2": - resolution: - { - integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-darwin-arm64@4.3.2': + resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==} + engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - "@tailwindcss/oxide-darwin-x64@4.3.2": - resolution: - { - integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-darwin-x64@4.3.2': + resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==} + engines: {node: '>= 20'} cpu: [x64] os: [darwin] - "@tailwindcss/oxide-freebsd-x64@4.3.2": - resolution: - { - integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-freebsd-x64@4.3.2': + resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==} + engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - "@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2": - resolution: - { - integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==} + engines: {node: '>= 20'} cpu: [arm] os: [linux] - "@tailwindcss/oxide-linux-arm64-gnu@4.3.2": - resolution: - { - integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - "@tailwindcss/oxide-linux-arm64-musl@4.3.2": - resolution: - { - integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - "@tailwindcss/oxide-linux-x64-gnu@4.3.2": - resolution: - { - integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - "@tailwindcss/oxide-linux-x64-musl@4.3.2": - resolution: - { - integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - "@tailwindcss/oxide-wasm32-wasi@4.3.2": - resolution: - { - integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==, - } - engines: { node: ">=14.0.0" } + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==} + engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: - - "@napi-rs/wasm-runtime" - - "@emnapi/core" - - "@emnapi/runtime" - - "@tybys/wasm-util" - - "@emnapi/wasi-threads" + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' - tslib - "@tailwindcss/oxide-win32-arm64-msvc@4.3.2": - resolution: - { - integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==} + engines: {node: '>= 20'} cpu: [arm64] os: [win32] - "@tailwindcss/oxide-win32-x64-msvc@4.3.2": - resolution: - { - integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==} + engines: {node: '>= 20'} cpu: [x64] os: [win32] - "@tailwindcss/oxide@4.3.2": - resolution: - { - integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==, - } - engines: { node: ">= 20" } + '@tailwindcss/oxide@4.3.2': + resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} + engines: {node: '>= 20'} - "@tailwindcss/postcss@4.3.2": - resolution: - { - integrity: sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==, - } + '@tailwindcss/postcss@4.3.2': + resolution: {integrity: sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==} - "@tailwindcss/typography@0.5.20": - resolution: - { - integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==, - } + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} peerDependencies: - tailwindcss: ">=3.0.0 || >=4.0.0 || insiders" + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' - "@tanstack/react-virtual@3.14.5": - resolution: - { - integrity: sha512-4EKRXh7zBLkbKbFmG3AUVkircuHd+7OdT1pocJSepxtfBd3qnrJgJ5rtPkRYyo9fmyVb2+pI2xPy5oYvMLQy6A==, - } + '@tanstack/react-virtual@3.14.5': + resolution: {integrity: sha512-4EKRXh7zBLkbKbFmG3AUVkircuHd+7OdT1pocJSepxtfBd3qnrJgJ5rtPkRYyo9fmyVb2+pI2xPy5oYvMLQy6A==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - "@tanstack/virtual-core@3.17.3": - resolution: - { - integrity: sha512-8Np/TFELpI0ySuJoVmjvOrQYXH/8sTX0Biv9szhFhY39xOdAAY+smrMxjxOum/ux3eM8MUJQsEJ0/R0UpvC8dw==, - } + '@tanstack/virtual-core@3.17.3': + resolution: {integrity: sha512-8Np/TFELpI0ySuJoVmjvOrQYXH/8sTX0Biv9szhFhY39xOdAAY+smrMxjxOum/ux3eM8MUJQsEJ0/R0UpvC8dw==} - "@tokenizer/inflate@0.4.1": - resolution: - { - integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==, - } - 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==, - } + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - "@types/esrecurse@4.3.1": - resolution: - { - integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==, - } + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - "@types/estree@1.0.9": - resolution: - { - integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, - } + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - "@types/geojson@7946.0.16": - resolution: - { - integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==, - } + '@types/http-errors@1.8.2': + resolution: {integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==} - "@types/http-errors@1.8.2": - resolution: - { - integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==, - } - - "@types/jpeg-js@0.3.7": - resolution: - { - integrity: sha512-v3FEzt2TXOOIgvwwOqMoeEhqYqUoK4wcV/K7JbKj8CzwDE9sSuor8EZpSJKaOnuEtyO0W+Nd2uKk8MbtusYpEg==, - } + '@types/jpeg-js@0.3.7': + resolution: {integrity: sha512-v3FEzt2TXOOIgvwwOqMoeEhqYqUoK4wcV/K7JbKj8CzwDE9sSuor8EZpSJKaOnuEtyO0W+Nd2uKk8MbtusYpEg==} deprecated: This is a stub types definition. jpeg-js provides its own type definitions, so you do not need this installed. - "@types/json-schema@7.0.15": - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } + '@types/node@22.20.0': + resolution: {integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==} - "@types/node@22.20.0": - resolution: - { - integrity: sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==, - } + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} - "@types/node@24.13.2": - resolution: - { - integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==, - } + '@types/nodemailer@6.4.24': + resolution: {integrity: sha512-Ww4u0rT9wQNXh4JiQaIwx3QWdcOFXzOjQA2zc+jtFYNmQiT4mIUqcDin51bDFdkzKubFnQCZNK7FIHlPKQ/q9w==} - "@types/nodemailer@6.4.24": - resolution: - { - integrity: sha512-Ww4u0rT9wQNXh4JiQaIwx3QWdcOFXzOjQA2zc+jtFYNmQiT4mIUqcDin51bDFdkzKubFnQCZNK7FIHlPKQ/q9w==, - } - - "@types/react-dom@19.2.3": - resolution: - { - integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==, - } + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - "@types/react": ^19.2.0 + '@types/react': ^19.2.0 - "@types/react@19.2.17": - resolution: - { - integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==, - } + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} - "@types/sanitize-html@2.16.1": - resolution: - { - integrity: sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==, - } + '@types/sanitize-html@2.16.1': + resolution: {integrity: sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==} - "@typescript-eslint/eslint-plugin@8.63.0": - resolution: - { - integrity: sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - "@typescript-eslint/parser": ^8.63.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: ">=4.8.4 <6.1.0" + '@vitalets/google-translate-api@9.2.1': + resolution: {integrity: sha512-zlwQWSjXUZhbZQ6qwtIQ7GdYXFQmJ4wYqzcrYJUxtvzQQwUP+uKUb/SRJaBOQuBntjBjzcdcJoLFrpCKUbIkOg==} + engines: {node: '>=14'} - "@typescript-eslint/parser@8.63.0": - resolution: - { - integrity: sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==, - } - 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.1.0" + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} - "@typescript-eslint/project-service@8.63.0": - resolution: - { - integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/scope-manager@8.63.0": - resolution: - { - integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@typescript-eslint/tsconfig-utils@8.63.0": - resolution: - { - integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/type-utils@8.63.0": - resolution: - { - integrity: sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==, - } - 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.1.0" - - "@typescript-eslint/types@8.63.0": - resolution: - { - integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@typescript-eslint/typescript-estree@8.63.0": - resolution: - { - integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - peerDependencies: - typescript: ">=4.8.4 <6.1.0" - - "@typescript-eslint/utils@8.63.0": - resolution: - { - integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==, - } - 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.1.0" - - "@typescript-eslint/visitor-keys@8.63.0": - resolution: - { - integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - "@vitalets/google-translate-api@9.2.1": - resolution: - { - integrity: sha512-zlwQWSjXUZhbZQ6qwtIQ7GdYXFQmJ4wYqzcrYJUxtvzQQwUP+uKUb/SRJaBOQuBntjBjzcdcJoLFrpCKUbIkOg==, - } - engines: { node: ">=14" } - - "@vitest/expect@2.1.9": - resolution: - { - integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==, - } - - "@vitest/mocker@2.1.9": - resolution: - { - integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==, - } + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 @@ -3856,650 +2521,264 @@ packages: vite: optional: true - "@vitest/pretty-format@2.1.9": - resolution: - { - integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==, - } + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - "@vitest/runner@2.1.9": - resolution: - { - integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==, - } + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} - "@vitest/snapshot@2.1.9": - resolution: - { - integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==, - } + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - "@vitest/spy@2.1.9": - resolution: - { - integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==, - } + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} - "@vitest/utils@2.1.9": - resolution: - { - integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==, - } - - acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.17.0: - resolution: - { - integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==, - } - engines: { node: ">=0.4.0" } - hasBin: true - - ajv@6.15.0: - resolution: - { - integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==, - } + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} ajv@8.20.0: - resolution: - { - integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, - } + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} aria-hidden@1.2.6: - resolution: - { - integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==, - } - engines: { node: ">=10" } - - aria-query@5.3.2: - resolution: - { - integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, - } - engines: { node: ">= 0.4" } - - array-buffer-byte-length@1.0.2: - resolution: - { - integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==, - } - engines: { node: ">= 0.4" } - - array-includes@3.1.9: - resolution: - { - integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==, - } - 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" } + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} assertion-error@2.0.1: - resolution: - { - integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, - } - engines: { node: ">=12" } - - ast-types-flow@0.0.8: - resolution: - { - integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, - } - - async-function@1.0.0: - resolution: - { - integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==, - } - engines: { node: ">= 0.4" } - - available-typed-arrays@1.0.7: - resolution: - { - integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} aws-ssl-profiles@1.1.2: - resolution: - { - integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==, - } - engines: { node: ">= 6.0.0" } - - axe-core@4.12.1: - resolution: - { - integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==, - } - engines: { node: ">=4" } - - axobject-query@4.1.0: - resolution: - { - integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==, - } - engines: { node: ">= 0.4" } - - balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } - - balanced-match@4.0.4: - resolution: - { - integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, - } - engines: { node: 18 || 20 || >=22 } + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} baseline-browser-mapping@2.10.40: - resolution: - { - integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==, - } - engines: { node: ">=6.0.0" } - hasBin: true - - baseline-browser-mapping@2.10.42: - resolution: - { - integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==, - } - engines: { node: ">=6.0.0" } + resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} + engines: {node: '>=6.0.0'} hasBin: true bcryptjs@3.0.3: - resolution: - { - integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==, - } + resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==} hasBin: true better-result@2.9.2: - resolution: - { - integrity: sha512-WIFoBPCdnTOdk9inkE1ZRvCZ4P0CpSkAiLlchC65N7n9DcjZ3NhqkBOlafzpOVnO8ixyi37kicmSJ3ENhPZl7Q==, - } - - brace-expansion@1.1.16: - resolution: - { - integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==, - } - - brace-expansion@5.0.7: - resolution: - { - integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==, - } - engines: { node: 18 || 20 || >=22 } - - braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: ">=8" } - - browserslist@4.28.5: - resolution: - { - integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } - hasBin: true + resolution: {integrity: sha512-WIFoBPCdnTOdk9inkE1ZRvCZ4P0CpSkAiLlchC65N7n9DcjZ3NhqkBOlafzpOVnO8ixyi37kicmSJ3ENhPZl7Q==} buffer-from@1.1.2: - resolution: - { - integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, - } + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} c12@3.3.4: - resolution: - { - integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==, - } + resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==} peerDependencies: - magicast: "*" + magicast: '*' peerDependenciesMeta: magicast: optional: true cac@6.7.14: - resolution: - { - integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, - } - engines: { node: ">=8" } - - 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" } + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} caniuse-lite@1.0.30001799: - resolution: - { - integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==, - } - - caniuse-lite@1.0.30001803: - resolution: - { - integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==, - } + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} chai@5.3.3: - resolution: - { - integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} chart.js@4.5.1: - resolution: - { - integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==, - } - engines: { pnpm: ">=8" } + resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} + engines: {pnpm: '>=8'} check-error@2.1.3: - resolution: - { - integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==, - } - engines: { node: ">= 16" } + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} chokidar@5.0.0: - resolution: - { - integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==, - } - engines: { node: ">= 20.19.0" } + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} class-variance-authority@0.7.1: - resolution: - { - integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==, - } + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} client-only@0.0.1: - resolution: - { - integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==, - } + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} clsx@2.1.1: - resolution: - { - integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} cluster-key-slot@1.1.1: - resolution: - { - integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} + engines: {node: '>=0.10.0'} cmdk@1.1.1: - resolution: - { - integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==, - } + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} peerDependencies: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } - confbox@0.2.4: - resolution: - { - integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==, - } + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} content-type@2.0.0: - resolution: - { - integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==, - } - engines: { node: ">=18" } - - convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} core-util-is@1.0.3: - resolution: - { - integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, - } + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} croner@10.0.1: - resolution: - { - integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==, - } - engines: { node: ">=18.0" } + resolution: {integrity: sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==} + engines: {node: '>=18.0'} cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} hasBin: true csstype@3.2.3: - resolution: - { - integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, - } - - damerau-levenshtein@1.0.8: - resolution: - { - integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==, - } - - 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" } + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} dayjs@1.11.21: - resolution: - { - integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==, - } + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: - supports-color: "*" + supports-color: '*' peerDependenciesMeta: supports-color: optional: true deep-eql@5.0.2: - resolution: - { - integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==, - } - engines: { node: ">=6" } - - deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} deepmerge-ts@7.1.5: - resolution: - { - integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==, - } - engines: { node: ">=16.0.0" } + resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} + engines: {node: '>=16.0.0'} deepmerge@4.3.1: - resolution: - { - integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, - } - engines: { node: ">=0.10.0" } - - define-data-property@1.1.4: - resolution: - { - integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, - } - engines: { node: ">= 0.4" } - - define-properties@1.2.1: - resolution: - { - integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} defu@6.1.7: - resolution: - { - integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==, - } + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} denque@2.1.0: - resolution: - { - integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==, - } - engines: { node: ">=0.10" } + 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" } + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} destr@2.0.5: - resolution: - { - integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==, - } + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} detect-libc@2.1.2: - resolution: - { - integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} detect-node-es@1.1.0: - resolution: - { - integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==, - } + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} dom-serializer@2.0.0: - resolution: - { - integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, - } + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} domelementtype@2.3.0: - resolution: - { - integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, - } + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} domhandler@5.0.3: - resolution: - { - integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, - } - engines: { node: ">= 4" } + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} domutils@3.2.2: - resolution: - { - integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, - } + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} dotenv@16.6.1: - resolution: - { - integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} dotenv@17.4.2: - resolution: - { - integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} drizzle-kit@0.30.6: - resolution: - { - integrity: sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g==, - } + resolution: {integrity: sha512-U4wWit0fyZuGuP7iNmRleQyK2V8wCuv57vf5l3MnG4z4fzNTjY/U13M8owyQ5RavqvqxBifWORaR3wIUzlN64g==} hasBin: true drizzle-orm@0.38.4: - resolution: - { - integrity: sha512-s7/5BpLKO+WJRHspvpqTydxFob8i1vo2rEx4pY6TGY7QSMuUfWUuzaY0DIpXCkgHOo37BaFC+SJQb99dDUXT3Q==, - } + resolution: {integrity: sha512-s7/5BpLKO+WJRHspvpqTydxFob8i1vo2rEx4pY6TGY7QSMuUfWUuzaY0DIpXCkgHOo37BaFC+SJQb99dDUXT3Q==} peerDependencies: - "@aws-sdk/client-rds-data": ">=3" - "@cloudflare/workers-types": ">=4" - "@electric-sql/pglite": ">=0.2.0" - "@libsql/client": ">=0.10.0" - "@libsql/client-wasm": ">=0.10.0" - "@neondatabase/serverless": ">=0.10.0" - "@op-engineering/op-sqlite": ">=2" - "@opentelemetry/api": ^1.4.1 - "@planetscale/database": ">=1" - "@prisma/client": "*" - "@tidbcloud/serverless": "*" - "@types/better-sqlite3": "*" - "@types/pg": "*" - "@types/react": ">=18" - "@types/sql.js": "*" - "@vercel/postgres": ">=0.8.0" - "@xata.io/client": "*" - better-sqlite3: ">=7" - bun-types: "*" - expo-sqlite: ">=14.0.0" - knex: "*" - kysely: "*" - mysql2: ">=2" - pg: ">=8" - postgres: ">=3" - prisma: "*" - react: ">=18" - sql.js: ">=1" - sqlite3: ">=5" + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/react': '>=18' + '@types/sql.js': '*' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + react: '>=18' + sql.js: '>=1' + sqlite3: '>=5' peerDependenciesMeta: - "@aws-sdk/client-rds-data": + '@aws-sdk/client-rds-data': optional: true - "@cloudflare/workers-types": + '@cloudflare/workers-types': optional: true - "@electric-sql/pglite": + '@electric-sql/pglite': optional: true - "@libsql/client": + '@libsql/client': optional: true - "@libsql/client-wasm": + '@libsql/client-wasm': optional: true - "@neondatabase/serverless": + '@neondatabase/serverless': optional: true - "@op-engineering/op-sqlite": + '@op-engineering/op-sqlite': optional: true - "@opentelemetry/api": + '@opentelemetry/api': optional: true - "@planetscale/database": + '@planetscale/database': optional: true - "@prisma/client": + '@prisma/client': optional: true - "@tidbcloud/serverless": + '@tidbcloud/serverless': optional: true - "@types/better-sqlite3": + '@types/better-sqlite3': optional: true - "@types/pg": + '@types/pg': optional: true - "@types/react": + '@types/react': optional: true - "@types/sql.js": + '@types/sql.js': optional: true - "@vercel/postgres": + '@vercel/postgres': optional: true - "@xata.io/client": + '@xata.io/client': optional: true better-sqlite3: optional: true @@ -4526,1408 +2805,415 @@ packages: sqlite3: optional: true - dunder-proto@1.0.1: - resolution: - { - integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, - } - engines: { node: ">= 0.4" } - effect@3.20.0: - resolution: - { - integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==, - } - - electron-to-chromium@1.5.389: - resolution: - { - integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==, - } - - emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, - } + resolution: {integrity: sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==} empathic@2.0.0: - resolution: - { - integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} + engines: {node: '>=14'} enhanced-resolve@5.21.6: - resolution: - { - integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==, - } - engines: { node: ">=10.13.0" } + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} entities@4.5.0: - resolution: - { - integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, - } - engines: { node: ">=0.12" } + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} entities@7.0.1: - resolution: - { - integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==, - } - engines: { node: ">=0.12" } + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} env-paths@3.0.0: - resolution: - { - integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - - es-abstract-get@1.0.0: - resolution: - { - integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==, - } - engines: { node: ">= 0.4" } - - 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" } + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} es-module-lexer@1.7.0: - resolution: - { - integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==, - } - - es-object-atoms@1.1.2: - resolution: - { - integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==, - } - 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.4: - resolution: - { - integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} esbuild-register@3.6.0: - resolution: - { - integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==, - } + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} peerDependencies: - esbuild: ">=0.12 <1" + esbuild: '>=0.12 <1' esbuild@0.18.20: - resolution: - { - integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} hasBin: true esbuild@0.19.12: - resolution: - { - integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} hasBin: true esbuild@0.21.5: - resolution: - { - integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} hasBin: true esbuild@0.28.1: - resolution: - { - integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} hasBin: true - escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: ">=6" } - 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-plugin-jsx-a11y@6.10.2: - resolution: - { - integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==, - } - engines: { node: ">=4.0" } - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - - eslint-plugin-react-hooks@7.1.1: - resolution: - { - integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==, - } - engines: { node: ">=18" } - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 - - eslint-plugin-security@4.0.1: - resolution: - { - integrity: sha512-/lZCkOxPOWaf1jXAqgICrS8St3BMBccIPvhOSUYuV6VCr1o5nFVG998FnTLt6w2Nxb8Uo0nM8fzmnhp+GY/aEg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - - eslint-scope@9.1.2: - resolution: - { - integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - - eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - - eslint-visitor-keys@5.0.1: - resolution: - { - integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - - eslint@10.6.0: - resolution: - { - integrity: sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - hasBin: true - peerDependencies: - jiti: "*" - peerDependenciesMeta: - jiti: - optional: true - - espree@11.2.0: - resolution: - { - integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==, - } - engines: { node: ^20.19.0 || ^22.13.0 || >=24 } - - esquery@1.7.0: - resolution: - { - integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, - } - engines: { node: ">=0.10" } - - esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: ">=4.0" } - - estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: ">=4.0" } + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } - - esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} expect-type@1.4.0: - resolution: - { - integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} exsolve@1.1.0: - resolution: - { - integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==, - } + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} fast-check@3.23.2: - resolution: - { - integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==, - } - engines: { node: ">=8.0.0" } + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } - - fast-glob@3.3.1: - resolution: - { - integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==, - } - engines: { node: ">=8.6.0" } - - fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } - - fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-sha256@1.3.0: - resolution: - { - integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==, - } + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} fast-uri@3.1.3: - resolution: - { - integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==, - } - - fastq@1.20.1: - resolution: - { - integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, - } - - fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: ">=16.0.0" } + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} file-type@21.3.4: - resolution: - { - integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==, - } - engines: { node: ">=20" } - - fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: ">=8" } - - find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: ">=10" } - - flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: ">=16" } - - flatted@3.4.2: - resolution: - { - integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==, - } - - for-each@0.3.5: - resolution: - { - integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} + engines: {node: '>=20'} foreground-child@3.3.1: - resolution: - { - integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } - - function.prototype.name@1.2.0: - resolution: - { - integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==, - } - engines: { node: ">= 0.4" } - - functions-have-names@1.2.3: - resolution: - { - integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, - } - gel@2.2.0: - resolution: - { - integrity: sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ==, - } - engines: { node: ">= 18.0.0" } + resolution: {integrity: sha512-q0ma7z2swmoamHQusey8ayo8+ilVdzDt4WTxSPzq/yRqvucWRfymRVMvNgmSC0XK7eNjjEZEcplxpgaNojKdmQ==} + engines: {node: '>= 18.0.0'} hasBin: true 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-intrinsic@1.3.0: - resolution: - { - integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} get-nonce@1.0.1: - resolution: - { - integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} 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-symbol-description@1.1.0: - resolution: - { - integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} get-tsconfig@4.14.0: - resolution: - { - integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==, - } + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} giget@3.3.0: - resolution: - { - integrity: sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==, - } + resolution: {integrity: sha512-gzi2D96p+AMfDcmJHGDj3KJ9NRiwvlFAU5yfa3ROwWZmFUjX4P43x3BcyRaOMMLto1vUo7C+86+MFhYTl6Ryiw==} hasBin: true - glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: ">= 6" } - - glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: ">=10.13.0" } - - globalthis@1.0.4: - resolution: - { - integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==, - } - engines: { node: ">= 0.4" } - - gopd@1.2.0: - resolution: - { - integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, - } - engines: { node: ">= 0.4" } - graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} grammex@3.1.12: - resolution: - { - integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==, - } + resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} graphmatch@1.1.1: - resolution: - { - integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==, - } - - has-bigints@1.1.0: - resolution: - { - integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==, - } - engines: { node: ">= 0.4" } - - 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" } + resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} hash-wasm@4.12.0: - resolution: - { - integrity: sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==, - } - - hasown@2.0.4: - resolution: - { - integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==, - } - engines: { node: ">= 0.4" } - - hermes-estree@0.25.1: - resolution: - { - integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==, - } - - hermes-parser@0.25.1: - resolution: - { - integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==, - } + resolution: {integrity: sha512-+/2B2rYLb48I/evdOIhP+K/DD2ca2fgBjp6O+GBEnCDk2e4rpeXIK8GvIyRPjTezgmWn9gmKwkQjjx6BtqDHVQ==} hono@4.12.27: - resolution: - { - integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==, - } - engines: { node: ">=16.9.0" } + resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} + engines: {node: '>=16.9.0'} htmlparser2@10.1.0: - resolution: - { - integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==, - } + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} http-errors@2.0.1: - resolution: - { - integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==, - } - engines: { node: ">= 0.8" } + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} http-status-codes@2.3.0: - resolution: - { - integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==, - } + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} iconv-lite@0.6.3: - resolution: - { - integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} iconv-lite@0.7.2: - resolution: - { - integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} icu-minify@4.13.1: - resolution: - { - integrity: sha512-nFYW2im0WJ3RUVZwabd71J8QTZRtkK1xxZBY7klg7a6KS/os17LZSj9q1VhbRSnk3S8Mv2I7F1izr/aEJ6cUsw==, - } + resolution: {integrity: sha512-nFYW2im0WJ3RUVZwabd71J8QTZRtkK1xxZBY7klg7a6KS/os17LZSj9q1VhbRSnk3S8Mv2I7F1izr/aEJ6cUsw==} 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.6: - resolution: - { - integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==, - } - engines: { node: ">= 4" } + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} immediate@3.0.6: - resolution: - { - integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==, - } - - imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: ">=0.8.19" } + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} inherits@2.0.4: - resolution: - { - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, - } - - internal-slot@1.1.0: - resolution: - { - integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} intl-messageformat@11.2.9: - resolution: - { - integrity: sha512-cGzymZerpDhVXRKjKLgXKda9gI29TU2o88L7gwNMHp3WZVxA/0c5tX52udXbW9JklDApolvMXZG6Dhhdz5eirA==, - } + resolution: {integrity: sha512-cGzymZerpDhVXRKjKLgXKda9gI29TU2o88L7gwNMHp3WZVxA/0c5tX52udXbW9JklDApolvMXZG6Dhhdz5eirA==} ioredis@5.11.1: - resolution: - { - integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==, - } - engines: { node: ">=12.22.0" } - - is-array-buffer@3.0.5: - resolution: - { - integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==, - } - engines: { node: ">= 0.4" } - - is-async-function@2.1.1: - resolution: - { - integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==, - } - engines: { node: ">= 0.4" } - - is-bigint@1.1.0: - resolution: - { - integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==, - } - engines: { node: ">= 0.4" } - - is-boolean-object@1.2.2: - resolution: - { - integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==, - } - engines: { node: ">= 0.4" } - - is-callable@1.2.7: - resolution: - { - integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, - } - engines: { node: ">= 0.4" } - - is-data-view@1.0.2: - resolution: - { - integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==, - } - engines: { node: ">= 0.4" } - - is-date-object@1.1.0: - resolution: - { - integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==, - } - engines: { node: ">= 0.4" } - - is-document.all@1.0.0: - resolution: - { - integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==} + engines: {node: '>=12.22.0'} is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: ">=0.10.0" } - - is-finalizationregistry@1.1.1: - resolution: - { - integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==, - } - engines: { node: ">= 0.4" } - - is-generator-function@1.1.2: - resolution: - { - integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: ">=0.10.0" } - - is-map@2.0.3: - resolution: - { - integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==, - } - engines: { node: ">= 0.4" } - - is-negative-zero@2.0.3: - resolution: - { - integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==, - } - engines: { node: ">= 0.4" } - - is-number-object@1.1.1: - resolution: - { - integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==, - } - engines: { node: ">= 0.4" } - - is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: ">=0.12.0" } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-plain-object@5.0.0: - resolution: - { - integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} is-property@1.0.2: - resolution: - { - integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==, - } - - is-regex@1.2.1: - resolution: - { - integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==, - } - engines: { node: ">= 0.4" } - - is-set@2.0.3: - resolution: - { - integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==, - } - engines: { node: ">= 0.4" } - - is-shared-array-buffer@1.0.4: - resolution: - { - integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==, - } - engines: { node: ">= 0.4" } - - is-string@1.1.1: - resolution: - { - integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==, - } - engines: { node: ">= 0.4" } - - is-symbol@1.1.1: - resolution: - { - integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==, - } - engines: { node: ">= 0.4" } - - is-typed-array@1.1.15: - resolution: - { - integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==, - } - engines: { node: ">= 0.4" } - - is-weakmap@2.0.2: - resolution: - { - integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==, - } - engines: { node: ">= 0.4" } - - is-weakref@1.1.1: - resolution: - { - integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==, - } - engines: { node: ">= 0.4" } - - is-weakset@2.0.4: - resolution: - { - integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} isarray@1.0.0: - resolution: - { - integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, - } - - isarray@2.0.5: - resolution: - { - integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, - } + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} isexe@3.1.5: - resolution: - { - integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} jiti@2.7.0: - resolution: - { - integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==, - } + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true jose@6.2.3: - resolution: - { - integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==, - } + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} jpeg-js@0.4.4: - resolution: - { - integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==, - } - - js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } - - 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-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } + resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==} 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==, - } + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true - jsx-ast-utils@3.3.5: - resolution: - { - integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==, - } - engines: { node: ">=4.0" } - jszip@3.10.1: - resolution: - { - integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==, - } - - keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } - - language-subtag-registry@0.3.23: - resolution: - { - integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==, - } - - language-tags@1.0.9: - resolution: - { - integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==, - } - engines: { node: ">=0.10" } + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} launder@1.7.1: - resolution: - { - integrity: sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==, - } - - levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: ">= 0.8.0" } + resolution: {integrity: sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==} lie@3.3.0: - resolution: - { - integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==, - } + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} lightningcss-android-arm64@1.32.0: - resolution: - { - integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: - resolution: - { - integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: - resolution: - { - integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: - resolution: - { - integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: - { - integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: - resolution: - { - integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: - resolution: - { - integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: - resolution: - { - integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: - resolution: - { - integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: - resolution: - { - integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: - resolution: - { - integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.32.0: - resolution: - { - integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, - } - engines: { node: ">= 12.0.0" } - - locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} long@5.3.2: - resolution: - { - integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==, - } + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} loupe@3.2.1: - resolution: - { - integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==, - } + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} lru-cache@10.4.3: - resolution: - { - integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, - } - - lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + 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" } + resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} lucide-react@1.23.0: - resolution: - { - integrity: sha512-38BpJcD0JhFosxHApP/BYsBetLpQFRoTRzEzstM/XCc3jsAG7wqaY1lgVwxiUe3xqYE+lNxo2PkCmYwXWrwwIw==, - } + resolution: {integrity: sha512-38BpJcD0JhFosxHApP/BYsBetLpQFRoTRzEzstM/XCc3jsAG7wqaY1lgVwxiUe3xqYE+lNxo2PkCmYwXWrwwIw==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 lzma@2.3.2: - resolution: - { - integrity: sha512-DcfiawQ1avYbW+hsILhF38IKAlnguc/fjHrychs9hdxe4qLykvhT5VTGNs5YRWgaNePh7NTxGD4uv4gKsRomCQ==, - } + resolution: {integrity: sha512-DcfiawQ1avYbW+hsILhF38IKAlnguc/fjHrychs9hdxe4qLykvhT5VTGNs5YRWgaNePh7NTxGD4uv4gKsRomCQ==} hasBin: true magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} mariadb@3.4.5: - resolution: - { - integrity: sha512-gThTYkhIS5rRqkVr+Y0cIdzr+GRqJ9sA2Q34e0yzmyhMCwyApf3OKAC1jnF23aSlIOqJuyaUFUcj7O1qZslmmQ==, - } - engines: { node: ">= 14" } - - math-intrinsics@1.1.0: - resolution: - { - integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-gThTYkhIS5rRqkVr+Y0cIdzr+GRqJ9sA2Q34e0yzmyhMCwyApf3OKAC1jnF23aSlIOqJuyaUFUcj7O1qZslmmQ==} + engines: {node: '>= 14'} media-typer@2.0.0: - resolution: - { - integrity: sha512-kOy3OxT2HH39N70UnKgu4NWDZjLOz8W/mfyvniHjRH/DrL3f2pOfvWQ4p60offbbtDAnXWp0v9LfMIqMec269Q==, - } - engines: { node: ">=18" } - - merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: ">= 8" } - - micromatch@4.0.8: - resolution: - { - integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, - } - engines: { node: ">=8.6" } + resolution: {integrity: sha512-kOy3OxT2HH39N70UnKgu4NWDZjLOz8W/mfyvniHjRH/DrL3f2pOfvWQ4p60offbbtDAnXWp0v9LfMIqMec269Q==} + engines: {node: '>=18'} mini-svg-data-uri@1.4.4: - resolution: - { - integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==, - } + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true - minimatch@10.2.5: - resolution: - { - integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==, - } - engines: { node: 18 || 20 || >=22 } - - minimatch@3.1.5: - resolution: - { - integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==, - } - ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} music-metadata@11.13.0: - resolution: - { - integrity: sha512-uXRaov9dfjSpQufXIU7sMxVZnh+FilCQv2mXn+K5EJ/decP3dTWrgvPYa5r6MtRbieNSCE708Da4J0u1UGfQIw==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-uXRaov9dfjSpQufXIU7sMxVZnh+FilCQv2mXn+K5EJ/decP3dTWrgvPYa5r6MtRbieNSCE708Da4J0u1UGfQIw==} + engines: {node: '>=18'} mysql2@3.15.3: - resolution: - { - integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==, - } - engines: { node: ">= 8.0" } + resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} + engines: {node: '>= 8.0'} mysql2@3.22.5: - resolution: - { - integrity: sha512-95uZ2TrPWAZdwpB3vvvDbmEMcNG8yIeNCyu6GUcr/QnWEE/wXm7+mhOCsdQfWQDTV7qYT/PDUZ4U4UPP4AsXqQ==, - } - engines: { node: ">= 8.0" } + resolution: {integrity: sha512-95uZ2TrPWAZdwpB3vvvDbmEMcNG8yIeNCyu6GUcr/QnWEE/wXm7+mhOCsdQfWQDTV7qYT/PDUZ4U4UPP4AsXqQ==} + engines: {node: '>= 8.0'} peerDependencies: - "@types/node": ">= 8" + '@types/node': '>= 8' mysql2@3.22.6: - resolution: - { - integrity: sha512-fPKmeDGUzvFP7bMD5SASlJ5zIgvCC4hbanTmhbUlEmhyrY1hR4Hi3xLOWgTd3luYjLVifx6uGvMNJ2m/LlqEpg==, - } - engines: { node: ">= 8.0" } + resolution: {integrity: sha512-fPKmeDGUzvFP7bMD5SASlJ5zIgvCC4hbanTmhbUlEmhyrY1hR4Hi3xLOWgTd3luYjLVifx6uGvMNJ2m/LlqEpg==} + engines: {node: '>= 8.0'} peerDependencies: - "@types/node": ">= 8" + '@types/node': '>= 8' named-placeholders@1.1.6: - resolution: - { - integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==, - } - engines: { node: ">=8.0.0" } + resolution: {integrity: sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==} + engines: {node: '>=8.0.0'} nanoid@3.3.15: - resolution: - { - integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 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" } + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} next-auth@5.0.0-beta.31: - resolution: - { - integrity: sha512-1OBgCKPzo+S7UWWMp3xgvGvIJ0OpV7B3vR4ZDRqD9a4Ch+OT6dakLXG9ivhtmIWVa71nTSXattOHyCg8sNi8/Q==, - } + resolution: {integrity: sha512-1OBgCKPzo+S7UWWMp3xgvGvIJ0OpV7B3vR4ZDRqD9a4Ch+OT6dakLXG9ivhtmIWVa71nTSXattOHyCg8sNi8/Q==} peerDependencies: - "@simplewebauthn/browser": ^9.0.1 - "@simplewebauthn/server": ^9.0.2 + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 next: ^14.0.0-0 || ^15.0.0 || ^16.0.0 nodemailer: ^7.0.7 react: ^18.2.0 || ^19.0.0 peerDependenciesMeta: - "@simplewebauthn/browser": + '@simplewebauthn/browser': optional: true - "@simplewebauthn/server": + '@simplewebauthn/server': optional: true nodemailer: optional: true next-intl-swc-plugin-extractor@4.13.1: - resolution: - { - integrity: sha512-RhlH2DR1ViEXzcX7G3tDXAvzNrBL2Ph54Hq/q/9oP9eXQV/okh3UQpA/lx2k9U5Ck83CZOSgH0eFTNi5U+zyXw==, - } + resolution: {integrity: sha512-RhlH2DR1ViEXzcX7G3tDXAvzNrBL2Ph54Hq/q/9oP9eXQV/okh3UQpA/lx2k9U5Ck83CZOSgH0eFTNi5U+zyXw==} next-intl@4.13.1: - resolution: - { - integrity: sha512-aS8KTA+nNhSNJJBlIhxgvU135WzoObwzFwav4wTDti/Gmhxqe0fs/Q343igo8Z7HGqPB/xgmoagwySZAlHmIfA==, - } + resolution: {integrity: sha512-aS8KTA+nNhSNJJBlIhxgvU135WzoObwzFwav4wTDti/Gmhxqe0fs/Q343igo8Z7HGqPB/xgmoagwySZAlHmIfA==} peerDependencies: next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 - typescript: "*" + typescript: '*' peerDependenciesMeta: typescript: optional: true next@16.2.10: - resolution: - { - integrity: sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==, - } - engines: { node: ">=20.9.0" } + resolution: {integrity: sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==} + engines: {node: '>=20.9.0'} hasBin: true peerDependencies: - "@opentelemetry/api": ^1.1.0 - "@playwright/test": ^1.51.1 - babel-plugin-react-compiler: "*" + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: - "@opentelemetry/api": + '@opentelemetry/api': optional: true - "@playwright/test": + '@playwright/test': optional: true babel-plugin-react-compiler: optional: true @@ -5935,281 +3221,100 @@ packages: optional: true node-addon-api@7.1.1: - resolution: - { - integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==, - } + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} node-fetch@2.7.0: - resolution: - { - integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==, - } - engines: { node: 4.x || >=6.0.0 } + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: encoding: optional: true - node-releases@2.0.51: - resolution: - { - integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==, - } - engines: { node: ">=18" } - nodemailer@9.0.3: - resolution: - { - integrity: sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw==, - } - engines: { node: ">=6.0.0" } + resolution: {integrity: sha512-n+YP+NKwR5zRWa60k3GiQ6Q3B4KXCoAw40dAKeCtYn020iNN74aWK2liXIC3ZEATeGql7we3tE3t8QwhY0eskw==} + engines: {node: '>=6.0.0'} oauth4webapi@3.8.6: - resolution: - { - integrity: sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==, - } - - 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.fromentries@2.0.8: - resolution: - { - integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==, - } - engines: { node: ">= 0.4" } - - object.values@1.2.1: - resolution: - { - integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==} ohash@2.0.11: - resolution: - { - integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==, - } - - optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: ">= 0.8.0" } + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} otplib@12.0.1: - resolution: - { - integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==, - } - - own-keys@1.0.1: - resolution: - { - integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==, - } - engines: { node: ">= 0.4" } - - p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: ">=10" } - - p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==} pako@1.0.11: - resolution: - { - integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==, - } + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} parse-srcset@1.0.2: - resolution: - { - integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==, - } - - path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} pathe@1.1.2: - resolution: - { - integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==, - } + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} pathe@2.0.3: - resolution: - { - integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, - } + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} pathval@2.0.1: - resolution: - { - integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==, - } - engines: { node: ">= 14.16" } + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} perfect-debounce@2.1.0: - resolution: - { - integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==, - } + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } - - picomatch@2.3.2: - resolution: - { - integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, - } - engines: { node: ">=8.6" } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@4.0.4: - resolution: - { - integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} pkg-types@2.3.1: - resolution: - { - integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==, - } + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} po-parser@2.1.1: - resolution: - { - integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==, - } - - possible-typed-array-names@1.1.0: - resolution: - { - integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==} postal-mime@2.7.4: - resolution: - { - integrity: sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==, - } + resolution: {integrity: sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==} postcss-selector-parser@6.0.10: - resolution: - { - integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} postcss@8.5.15: - resolution: - { - integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} postcss@8.5.16: - resolution: - { - integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + engines: {node: ^10 || ^12 || >=14} postgres@3.4.7: - resolution: - { - integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} preact-render-to-string@6.5.11: - resolution: - { - integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==, - } + resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==} peerDependencies: - preact: ">=10" + preact: '>=10' preact@10.24.3: - resolution: - { - integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==, - } - - prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: ">= 0.8.0" } - - prettier@3.9.5: - resolution: - { - integrity: sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==, - } - engines: { node: ">=14" } - hasBin: true + resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} prisma@7.8.0: - resolution: - { - integrity: sha512-yfN4yrw7HV9kEJhoy1+jgah0jafEIQsf7uWouSsM8MvJtlubsk+kM7AIBWZ8+GJl74Yj3c+nbYqBkMOxtsZ3Lw==, - } - engines: { node: ^20.19 || ^22.12 || >=24.0 } + resolution: {integrity: sha512-yfN4yrw7HV9kEJhoy1+jgah0jafEIQsf7uWouSsM8MvJtlubsk+kM7AIBWZ8+GJl74Yj3c+nbYqBkMOxtsZ3Lw==} + engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: - better-sqlite3: ">=9.0.0" - typescript: ">=5.4.0" + better-sqlite3: '>=9.0.0' + typescript: '>=5.4.0' peerDependenciesMeta: better-sqlite3: optional: true @@ -6217,840 +3322,357 @@ packages: optional: true process-nextick-args@2.0.1: - resolution: - { - integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, - } + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} proper-lockfile@4.1.2: - resolution: - { - integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==, - } - - punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} pure-rand@6.1.0: - resolution: - { - integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==, - } - - queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} radix-ui@1.6.2: - resolution: - { - integrity: sha512-OwYUjzMwiInCUxgAWpPsavXC3Kh4iyi/49uU1/qZTG3RQDlvegyk1GOMiGvSkjua1RDb3JD3fo3eroL9FV4GQw==, - } + resolution: {integrity: sha512-OwYUjzMwiInCUxgAWpPsavXC3Kh4iyi/49uU1/qZTG3RQDlvegyk1GOMiGvSkjua1RDb3JD3fo3eroL9FV4GQw==} peerDependencies: - "@types/react": "*" - "@types/react-dom": "*" + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true rc9@3.0.1: - resolution: - { - integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==, - } + resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==} react-dom@19.2.7: - resolution: - { - integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==, - } + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: react: ^19.2.7 react-hook-form@7.81.0: - resolution: - { - integrity: sha512-ocbmr2p5KBMoAfj4WCUvped33lVi1Kd5DuDUvQDnB6VEAacOjPI/jMbtDdbhco4y9ct4xUuCmMY0b/C9L0QHjw==, - } - engines: { node: ">=18.0.0" } + resolution: {integrity: sha512-ocbmr2p5KBMoAfj4WCUvped33lVi1Kd5DuDUvQDnB6VEAacOjPI/jMbtDdbhco4y9ct4xUuCmMY0b/C9L0QHjw==} + engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 react-remove-scroll-bar@2.3.8: - resolution: - { - integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: - "@types/react": + '@types/react': optional: true react-remove-scroll@2.7.2: - resolution: - { - integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true react-style-singleton@2.2.3: - resolution: - { - integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true react@19.2.7: - resolution: - { - integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} readable-stream@2.3.8: - resolution: - { - integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, - } + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readdirp@5.0.0: - resolution: - { - integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==, - } - engines: { node: ">= 20.19.0" } + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} redis-errors@1.2.0: - resolution: - { - integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} redis-parser@3.0.0: - resolution: - { - integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==, - } - engines: { node: ">=4" } - - reflect.getprototypeof@1.0.10: - resolution: - { - integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==, - } - engines: { node: ">= 0.4" } - - regexp-tree@0.1.27: - resolution: - { - integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==, - } - hasBin: true - - regexp.prototype.flags@1.5.4: - resolution: - { - integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} remeda@2.33.4: - resolution: - { - integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==, - } + resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} resend@6.17.1: - resolution: - { - integrity: sha512-QhHHIRPPM9Tq2uilWmNF8C8kd6nLkUmiFgDQCt1v4eR4o+6p86qrK+Vn12jnAs0jR8Gf6ABdxI18/90LGQ7GVA==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-QhHHIRPPM9Tq2uilWmNF8C8kd6nLkUmiFgDQCt1v4eR4o+6p86qrK+Vn12jnAs0jR8Gf6ABdxI18/90LGQ7GVA==} + engines: {node: '>=20'} peerDependencies: - "@react-email/render": "*" + '@react-email/render': '*' peerDependenciesMeta: - "@react-email/render": + '@react-email/render': optional: true resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} retry@0.12.0: - resolution: - { - integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==, - } - engines: { node: ">= 4" } - - reusify@1.1.0: - resolution: - { - integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, - } - engines: { iojs: ">=1.0.0", node: ">=0.10.0" } + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} rollup@4.62.2: - resolution: - { - integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==, - } - engines: { node: ">=18.0.0", npm: ">=8.0.0" } + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } - - safe-array-concat@1.1.4: - resolution: - { - integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==, - } - engines: { node: ">=0.4" } - safe-buffer@5.1.2: - resolution: - { - integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, - } - - 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-regex@2.1.1: - resolution: - { - integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==, - } + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safer-buffer@2.1.2: - resolution: - { - integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, - } + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} sanitize-html@2.17.5: - resolution: - { - integrity: sha512-ZmU1joGRrvoyctKIiuwUxqR6moLoU2Wk+2bMccN6f7UwhAmwYDvWziqPxRDDN2Qip62NqnIrVrT9akbL6Wretg==, - } + resolution: {integrity: sha512-ZmU1joGRrvoyctKIiuwUxqR6moLoU2Wk+2bMccN6f7UwhAmwYDvWziqPxRDDN2Qip62NqnIrVrT9akbL6Wretg==} scheduler@0.27.0: - resolution: - { - integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, - } - - semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } - hasBin: true + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@7.8.5: - resolution: - { - integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} hasBin: true seq-queue@0.0.5: - resolution: - { - integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==, - } - - 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" } + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} setimmediate@1.0.5: - resolution: - { - integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==, - } + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} setprototypeof@1.2.0: - resolution: - { - integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, - } + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} sharp@0.34.5: - resolution: - { - integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==, - } - engines: { node: ^18.17.0 || ^20.3.0 || >=21.0.0 } + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} shell-quote@1.9.0: - resolution: - { - integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==, - } - 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.1: - resolution: - { - integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} + engines: {node: '>= 0.4'} siginfo@2.0.0: - resolution: - { - integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, - } + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@3.0.7: - resolution: - { - integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, - } + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} sonner@2.0.7: - resolution: - { - integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==, - } + resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map-support@0.5.21: - resolution: - { - integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, - } + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} sql-escaper@1.3.3: - resolution: - { - integrity: sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==, - } - engines: { bun: ">=1.0.0", deno: ">=2.0.0", node: ">=12.0.0" } + resolution: {integrity: sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==} + engines: {bun: '>=1.0.0', deno: '>=2.0.0', node: '>=12.0.0'} sql-escaper@1.4.0: - resolution: - { - integrity: sha512-Ti/Zx9J3aITMYUMFhCKbkplQjyi7Kk2SyYXp+rzrkyKZetIy19XbQtVZ+0ZR5aVm/178tIJ6+aVvBqXkH+Xs7w==, - } - engines: { bun: ">=1.0.0", deno: ">=2.0.0", node: ">=12.0.0" } + resolution: {integrity: sha512-Ti/Zx9J3aITMYUMFhCKbkplQjyi7Kk2SyYXp+rzrkyKZetIy19XbQtVZ+0ZR5aVm/178tIJ6+aVvBqXkH+Xs7w==} + engines: {bun: '>=1.0.0', deno: '>=2.0.0', node: '>=12.0.0'} sqlstring@2.3.3: - resolution: - { - integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==, - } - engines: { node: ">= 0.6" } + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} stackback@0.0.2: - resolution: - { - integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, - } + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} standard-as-callback@2.1.0: - resolution: - { - integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==, - } + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} standardwebhooks@1.0.0: - resolution: - { - integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==, - } + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} statuses@2.0.2: - resolution: - { - integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==, - } - engines: { node: ">= 0.8" } + 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" } - - string.prototype.includes@2.0.1: - resolution: - { - integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==, - } - engines: { node: ">= 0.4" } - - string.prototype.trim@1.2.11: - resolution: - { - integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==, - } - engines: { node: ">= 0.4" } - - string.prototype.trimend@1.0.10: - resolution: - { - integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==, - } - engines: { node: ">= 0.4" } - - string.prototype.trimstart@1.0.8: - resolution: - { - integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} string_decoder@1.1.1: - resolution: - { - integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, - } + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} strtok3@10.3.5: - resolution: - { - integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} + engines: {node: '>=18'} styled-jsx@5.1.6: - resolution: - { - integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} peerDependencies: - "@babel/core": "*" - babel-plugin-macros: "*" - react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' peerDependenciesMeta: - "@babel/core": + '@babel/core': optional: true babel-plugin-macros: optional: true tailwind-merge@3.6.0: - resolution: - { - integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==, - } + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} tailwindcss@4.3.2: - resolution: - { - integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==, - } + resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} tapable@2.3.3: - resolution: - { - integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} thirty-two@1.0.2: - resolution: - { - integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==, - } - engines: { node: ">=0.2.6" } + resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==} + engines: {node: '>=0.2.6'} tinybench@2.9.0: - resolution: - { - integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, - } + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@0.3.2: - resolution: - { - integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, - } - - tinyglobby@0.2.17: - resolution: - { - integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} tinypool@1.1.1: - resolution: - { - integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@1.2.0: - resolution: - { - integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==, - } - engines: { node: ">=14.0.0" } + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} tinyspy@3.0.2: - resolution: - { - integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==, - } - engines: { node: ">=14.0.0" } - - to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: ">=8.0" } + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} toidentifier@1.0.1: - resolution: - { - integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, - } - engines: { node: ">=0.6" } + 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" } + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + engines: {node: '>=14.16'} tr46@0.0.3: - resolution: - { - integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, - } - - ts-api-utils@2.5.0: - resolution: - { - integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, - } - engines: { node: ">=18.12" } - peerDependencies: - typescript: ">=4.8.4" + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tsx@4.22.5: - resolution: - { - integrity: sha512-F7JnSfPl5ASt6LqwWyUQ3T8BwN3q0eQEbFMYa2iRWaVQmmudo0d7fRmwM4O002gsvW1bs0yBYioutsAjqLJMvQ==, - } - engines: { node: ">=18.0.0" } + resolution: {integrity: sha512-F7JnSfPl5ASt6LqwWyUQ3T8BwN3q0eQEbFMYa2iRWaVQmmudo0d7fRmwM4O002gsvW1bs0yBYioutsAjqLJMvQ==} + engines: {node: '>=18.0.0'} hasBin: true - type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: ">= 0.8.0" } - - 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.8: - resolution: - { - integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==, - } - engines: { node: ">= 0.4" } - - typescript-eslint@8.63.0: - resolution: - { - integrity: sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==, - } - 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.1.0" - typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: ">=14.17" } + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true 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" } + resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} + engines: {node: '>=18'} undici-types@6.21.0: - resolution: - { - integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, - } + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} undici-types@7.18.2: - resolution: - { - integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==, - } - - 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==, - } + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} use-callback-ref@1.3.3: - resolution: - { - integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true use-intl@4.13.1: - resolution: - { - integrity: sha512-UU5C3zAC7yVg3m7rq5C8VF5J5jhAfvS19Wi9bPNCB9xB7jQYBsUcrqfdxs4Mxl9XR3x6BDB5K++iAw7/rcm3gg==, - } + resolution: {integrity: sha512-UU5C3zAC7yVg3m7rq5C8VF5J5jhAfvS19Wi9bPNCB9xB7jQYBsUcrqfdxs4Mxl9XR3x6BDB5K++iAw7/rcm3gg==} peerDependencies: react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 use-sidecar@1.1.3: - resolution: - { - integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} peerDependencies: - "@types/react": "*" + '@types/react': '*' react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc peerDependenciesMeta: - "@types/react": + '@types/react': optional: true util-deprecate@1.0.2: - resolution: - { - integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, - } + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} valibot@1.2.0: - resolution: - { - integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==, - } + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} peerDependencies: - typescript: ">=5" + typescript: '>=5' peerDependenciesMeta: typescript: optional: true vite-node@2.1.9: - resolution: - { - integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true vite@5.4.21: - resolution: - { - integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - "@types/node": ^18.0.0 || >=20.0.0 - less: "*" + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' lightningcss: ^1.21.0 - sass: "*" - sass-embedded: "*" - stylus: "*" - sugarss: "*" + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' terser: ^5.4.0 peerDependenciesMeta: - "@types/node": + '@types/node': optional: true less: optional: true @@ -7068,27 +3690,24 @@ packages: optional: true vitest@2.1.9: - resolution: - { - integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==, - } - engines: { node: ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - "@edge-runtime/vm": "*" - "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": 2.1.9 - "@vitest/ui": 2.1.9 - happy-dom: "*" - jsdom: "*" + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 + happy-dom: '*' + jsdom: '*' peerDependenciesMeta: - "@edge-runtime/vm": + '@edge-runtime/vm': optional: true - "@types/node": + '@types/node': optional: true - "@vitest/browser": + '@vitest/browser': optional: true - "@vitest/ui": + '@vitest/ui': optional: true happy-dom: optional: true @@ -7096,122 +3715,42 @@ packages: optional: true webidl-conversions@3.0.1: - resolution: - { - integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==, - } + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} whatwg-url@5.0.0: - resolution: - { - integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, - } - - which-boxed-primitive@1.1.1: - resolution: - { - integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==, - } - engines: { node: ">= 0.4" } - - which-builtin-type@1.2.1: - resolution: - { - integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==, - } - engines: { node: ">= 0.4" } - - which-collection@1.0.2: - resolution: - { - integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==, - } - engines: { node: ">= 0.4" } - - which-typed-array@1.1.22: - resolution: - { - integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==, - } - engines: { node: ">= 0.4" } + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: ">= 8" } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} hasBin: true which@4.0.0: - resolution: - { - integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==, - } - engines: { node: ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} hasBin: true why-is-node-running@2.3.0: - resolution: - { - integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} hasBin: true win-guid@0.2.1: - resolution: - { - integrity: sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==, - } - - word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: ">=0.10.0" } - - yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } - - yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==} zeptomatch@2.1.0: - resolution: - { - integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==, - } - - zod-validation-error@4.0.2: - resolution: - { - integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==, - } - engines: { node: ">=18.0.0" } - peerDependencies: - zod: ^3.25.0 || ^4.0.0 + resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} zod@3.25.76: - resolution: - { - integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==, - } + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: - "@alloc/quick-lru@5.2.0": {} - "@auth/core@0.41.2(nodemailer@9.0.3)": + '@alloc/quick-lru@5.2.0': {} + + '@auth/core@0.41.2(nodemailer@9.0.3)': dependencies: - "@panva/hkdf": 1.2.1 + '@panva/hkdf': 1.2.1 jose: 6.2.3 oauth4webapi: 3.8.6 preact: 10.24.3 @@ -7219,794 +3758,663 @@ snapshots: optionalDependencies: nodemailer: 9.0.3 - "@babel/code-frame@7.29.7": - dependencies: - "@babel/helper-validator-identifier": 7.29.7 - js-tokens: 4.0.0 - picocolors: 1.1.1 + '@biomejs/biome@2.5.3': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.5.3 + '@biomejs/cli-darwin-x64': 2.5.3 + '@biomejs/cli-linux-arm64': 2.5.3 + '@biomejs/cli-linux-arm64-musl': 2.5.3 + '@biomejs/cli-linux-x64': 2.5.3 + '@biomejs/cli-linux-x64-musl': 2.5.3 + '@biomejs/cli-win32-arm64': 2.5.3 + '@biomejs/cli-win32-x64': 2.5.3 - "@babel/compat-data@7.29.7": {} + '@biomejs/cli-darwin-arm64@2.5.3': + optional: true - "@babel/core@7.29.7": - dependencies: - "@babel/code-frame": 7.29.7 - "@babel/generator": 7.29.7 - "@babel/helper-compilation-targets": 7.29.7 - "@babel/helper-module-transforms": 7.29.7(@babel/core@7.29.7) - "@babel/helpers": 7.29.7 - "@babel/parser": 7.29.7 - "@babel/template": 7.29.7 - "@babel/traverse": 7.29.7 - "@babel/types": 7.29.7 - "@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 + '@biomejs/cli-darwin-x64@2.5.3': + optional: true - "@babel/generator@7.29.7": - dependencies: - "@babel/parser": 7.29.7 - "@babel/types": 7.29.7 - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 - jsesc: 3.1.0 + '@biomejs/cli-linux-arm64-musl@2.5.3': + optional: true - "@babel/helper-compilation-targets@7.29.7": - dependencies: - "@babel/compat-data": 7.29.7 - "@babel/helper-validator-option": 7.29.7 - browserslist: 4.28.5 - lru-cache: 5.1.1 - semver: 6.3.1 + '@biomejs/cli-linux-arm64@2.5.3': + optional: true - "@babel/helper-globals@7.29.7": {} + '@biomejs/cli-linux-x64-musl@2.5.3': + optional: true - "@babel/helper-module-imports@7.29.7": - dependencies: - "@babel/traverse": 7.29.7 - "@babel/types": 7.29.7 - transitivePeerDependencies: - - supports-color + '@biomejs/cli-linux-x64@2.5.3': + optional: true - "@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)": - dependencies: - "@babel/core": 7.29.7 - "@babel/helper-module-imports": 7.29.7 - "@babel/helper-validator-identifier": 7.29.7 - "@babel/traverse": 7.29.7 - transitivePeerDependencies: - - supports-color + '@biomejs/cli-win32-arm64@2.5.3': + optional: true - "@babel/helper-string-parser@7.29.7": {} + '@biomejs/cli-win32-x64@2.5.3': + optional: true - "@babel/helper-validator-identifier@7.29.7": {} + '@borewit/text-codec@0.2.2': {} - "@babel/helper-validator-option@7.29.7": {} - - "@babel/helpers@7.29.7": - dependencies: - "@babel/template": 7.29.7 - "@babel/types": 7.29.7 - - "@babel/parser@7.29.7": - dependencies: - "@babel/types": 7.29.7 - - "@babel/template@7.29.7": - dependencies: - "@babel/code-frame": 7.29.7 - "@babel/parser": 7.29.7 - "@babel/types": 7.29.7 - - "@babel/traverse@7.29.7": - dependencies: - "@babel/code-frame": 7.29.7 - "@babel/generator": 7.29.7 - "@babel/helper-globals": 7.29.7 - "@babel/parser": 7.29.7 - "@babel/template": 7.29.7 - "@babel/types": 7.29.7 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - "@babel/types@7.29.7": - dependencies: - "@babel/helper-string-parser": 7.29.7 - "@babel/helper-validator-identifier": 7.29.7 - - "@borewit/text-codec@0.2.2": {} - - "@dnd-kit/accessibility@3.1.1(react@19.2.7)": + '@dnd-kit/accessibility@3.1.1(react@19.2.7)': dependencies: react: 19.2.7 tslib: 2.8.1 - "@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@dnd-kit/accessibility": 3.1.1(react@19.2.7) - "@dnd-kit/utilities": 3.2.2(react@19.2.7) + '@dnd-kit/accessibility': 3.1.1(react@19.2.7) + '@dnd-kit/utilities': 3.2.2(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) tslib: 2.8.1 - "@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)": + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)': dependencies: - "@dnd-kit/core": 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@dnd-kit/utilities": 3.2.2(react@19.2.7) + '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@dnd-kit/utilities': 3.2.2(react@19.2.7) react: 19.2.7 tslib: 2.8.1 - "@dnd-kit/utilities@3.2.2(react@19.2.7)": + '@dnd-kit/utilities@3.2.2(react@19.2.7)': dependencies: react: 19.2.7 tslib: 2.8.1 - "@drizzle-team/brocli@0.10.2": {} + '@drizzle-team/brocli@0.10.2': {} - "@electric-sql/pglite-socket@0.1.1(@electric-sql/pglite@0.4.1)": + '@electric-sql/pglite-socket@0.1.1(@electric-sql/pglite@0.4.1)': dependencies: - "@electric-sql/pglite": 0.4.1 + '@electric-sql/pglite': 0.4.1 - "@electric-sql/pglite-tools@0.3.1(@electric-sql/pglite@0.4.1)": + '@electric-sql/pglite-tools@0.3.1(@electric-sql/pglite@0.4.1)': dependencies: - "@electric-sql/pglite": 0.4.1 + '@electric-sql/pglite': 0.4.1 - "@electric-sql/pglite@0.4.1": {} + '@electric-sql/pglite@0.4.1': {} - "@emnapi/runtime@1.11.1": + '@emnapi/runtime@1.11.1': dependencies: tslib: 2.8.1 optional: true - "@esbuild-kit/core-utils@3.3.2": + '@esbuild-kit/core-utils@3.3.2': dependencies: esbuild: 0.18.20 source-map-support: 0.5.21 - "@esbuild-kit/esm-loader@2.6.5": + '@esbuild-kit/esm-loader@2.6.5': dependencies: - "@esbuild-kit/core-utils": 3.3.2 + '@esbuild-kit/core-utils': 3.3.2 get-tsconfig: 4.14.0 - "@esbuild/aix-ppc64@0.19.12": + '@esbuild/aix-ppc64@0.19.12': optional: true - "@esbuild/aix-ppc64@0.21.5": + '@esbuild/aix-ppc64@0.21.5': optional: true - "@esbuild/aix-ppc64@0.28.1": + '@esbuild/aix-ppc64@0.28.1': optional: true - "@esbuild/android-arm64@0.18.20": + '@esbuild/android-arm64@0.18.20': optional: true - "@esbuild/android-arm64@0.19.12": + '@esbuild/android-arm64@0.19.12': optional: true - "@esbuild/android-arm64@0.21.5": + '@esbuild/android-arm64@0.21.5': optional: true - "@esbuild/android-arm64@0.28.1": + '@esbuild/android-arm64@0.28.1': optional: true - "@esbuild/android-arm@0.18.20": + '@esbuild/android-arm@0.18.20': optional: true - "@esbuild/android-arm@0.19.12": + '@esbuild/android-arm@0.19.12': optional: true - "@esbuild/android-arm@0.21.5": + '@esbuild/android-arm@0.21.5': optional: true - "@esbuild/android-arm@0.28.1": + '@esbuild/android-arm@0.28.1': optional: true - "@esbuild/android-x64@0.18.20": + '@esbuild/android-x64@0.18.20': optional: true - "@esbuild/android-x64@0.19.12": + '@esbuild/android-x64@0.19.12': optional: true - "@esbuild/android-x64@0.21.5": + '@esbuild/android-x64@0.21.5': optional: true - "@esbuild/android-x64@0.28.1": + '@esbuild/android-x64@0.28.1': optional: true - "@esbuild/darwin-arm64@0.18.20": + '@esbuild/darwin-arm64@0.18.20': optional: true - "@esbuild/darwin-arm64@0.19.12": + '@esbuild/darwin-arm64@0.19.12': optional: true - "@esbuild/darwin-arm64@0.21.5": + '@esbuild/darwin-arm64@0.21.5': optional: true - "@esbuild/darwin-arm64@0.28.1": + '@esbuild/darwin-arm64@0.28.1': optional: true - "@esbuild/darwin-x64@0.18.20": + '@esbuild/darwin-x64@0.18.20': optional: true - "@esbuild/darwin-x64@0.19.12": + '@esbuild/darwin-x64@0.19.12': optional: true - "@esbuild/darwin-x64@0.21.5": + '@esbuild/darwin-x64@0.21.5': optional: true - "@esbuild/darwin-x64@0.28.1": + '@esbuild/darwin-x64@0.28.1': optional: true - "@esbuild/freebsd-arm64@0.18.20": + '@esbuild/freebsd-arm64@0.18.20': optional: true - "@esbuild/freebsd-arm64@0.19.12": + '@esbuild/freebsd-arm64@0.19.12': optional: true - "@esbuild/freebsd-arm64@0.21.5": + '@esbuild/freebsd-arm64@0.21.5': optional: true - "@esbuild/freebsd-arm64@0.28.1": + '@esbuild/freebsd-arm64@0.28.1': optional: true - "@esbuild/freebsd-x64@0.18.20": + '@esbuild/freebsd-x64@0.18.20': optional: true - "@esbuild/freebsd-x64@0.19.12": + '@esbuild/freebsd-x64@0.19.12': optional: true - "@esbuild/freebsd-x64@0.21.5": + '@esbuild/freebsd-x64@0.21.5': optional: true - "@esbuild/freebsd-x64@0.28.1": + '@esbuild/freebsd-x64@0.28.1': optional: true - "@esbuild/linux-arm64@0.18.20": + '@esbuild/linux-arm64@0.18.20': optional: true - "@esbuild/linux-arm64@0.19.12": + '@esbuild/linux-arm64@0.19.12': optional: true - "@esbuild/linux-arm64@0.21.5": + '@esbuild/linux-arm64@0.21.5': optional: true - "@esbuild/linux-arm64@0.28.1": + '@esbuild/linux-arm64@0.28.1': optional: true - "@esbuild/linux-arm@0.18.20": + '@esbuild/linux-arm@0.18.20': optional: true - "@esbuild/linux-arm@0.19.12": + '@esbuild/linux-arm@0.19.12': optional: true - "@esbuild/linux-arm@0.21.5": + '@esbuild/linux-arm@0.21.5': optional: true - "@esbuild/linux-arm@0.28.1": + '@esbuild/linux-arm@0.28.1': optional: true - "@esbuild/linux-ia32@0.18.20": + '@esbuild/linux-ia32@0.18.20': optional: true - "@esbuild/linux-ia32@0.19.12": + '@esbuild/linux-ia32@0.19.12': optional: true - "@esbuild/linux-ia32@0.21.5": + '@esbuild/linux-ia32@0.21.5': optional: true - "@esbuild/linux-ia32@0.28.1": + '@esbuild/linux-ia32@0.28.1': optional: true - "@esbuild/linux-loong64@0.18.20": + '@esbuild/linux-loong64@0.18.20': optional: true - "@esbuild/linux-loong64@0.19.12": + '@esbuild/linux-loong64@0.19.12': optional: true - "@esbuild/linux-loong64@0.21.5": + '@esbuild/linux-loong64@0.21.5': optional: true - "@esbuild/linux-loong64@0.28.1": + '@esbuild/linux-loong64@0.28.1': optional: true - "@esbuild/linux-mips64el@0.18.20": + '@esbuild/linux-mips64el@0.18.20': optional: true - "@esbuild/linux-mips64el@0.19.12": + '@esbuild/linux-mips64el@0.19.12': optional: true - "@esbuild/linux-mips64el@0.21.5": + '@esbuild/linux-mips64el@0.21.5': optional: true - "@esbuild/linux-mips64el@0.28.1": + '@esbuild/linux-mips64el@0.28.1': optional: true - "@esbuild/linux-ppc64@0.18.20": + '@esbuild/linux-ppc64@0.18.20': optional: true - "@esbuild/linux-ppc64@0.19.12": + '@esbuild/linux-ppc64@0.19.12': optional: true - "@esbuild/linux-ppc64@0.21.5": + '@esbuild/linux-ppc64@0.21.5': optional: true - "@esbuild/linux-ppc64@0.28.1": + '@esbuild/linux-ppc64@0.28.1': optional: true - "@esbuild/linux-riscv64@0.18.20": + '@esbuild/linux-riscv64@0.18.20': optional: true - "@esbuild/linux-riscv64@0.19.12": + '@esbuild/linux-riscv64@0.19.12': optional: true - "@esbuild/linux-riscv64@0.21.5": + '@esbuild/linux-riscv64@0.21.5': optional: true - "@esbuild/linux-riscv64@0.28.1": + '@esbuild/linux-riscv64@0.28.1': optional: true - "@esbuild/linux-s390x@0.18.20": + '@esbuild/linux-s390x@0.18.20': optional: true - "@esbuild/linux-s390x@0.19.12": + '@esbuild/linux-s390x@0.19.12': optional: true - "@esbuild/linux-s390x@0.21.5": + '@esbuild/linux-s390x@0.21.5': optional: true - "@esbuild/linux-s390x@0.28.1": + '@esbuild/linux-s390x@0.28.1': optional: true - "@esbuild/linux-x64@0.18.20": + '@esbuild/linux-x64@0.18.20': optional: true - "@esbuild/linux-x64@0.19.12": + '@esbuild/linux-x64@0.19.12': optional: true - "@esbuild/linux-x64@0.21.5": + '@esbuild/linux-x64@0.21.5': optional: true - "@esbuild/linux-x64@0.28.1": + '@esbuild/linux-x64@0.28.1': optional: true - "@esbuild/netbsd-arm64@0.28.1": + '@esbuild/netbsd-arm64@0.28.1': optional: true - "@esbuild/netbsd-x64@0.18.20": + '@esbuild/netbsd-x64@0.18.20': optional: true - "@esbuild/netbsd-x64@0.19.12": + '@esbuild/netbsd-x64@0.19.12': optional: true - "@esbuild/netbsd-x64@0.21.5": + '@esbuild/netbsd-x64@0.21.5': optional: true - "@esbuild/netbsd-x64@0.28.1": + '@esbuild/netbsd-x64@0.28.1': optional: true - "@esbuild/openbsd-arm64@0.28.1": + '@esbuild/openbsd-arm64@0.28.1': optional: true - "@esbuild/openbsd-x64@0.18.20": + '@esbuild/openbsd-x64@0.18.20': optional: true - "@esbuild/openbsd-x64@0.19.12": + '@esbuild/openbsd-x64@0.19.12': optional: true - "@esbuild/openbsd-x64@0.21.5": + '@esbuild/openbsd-x64@0.21.5': optional: true - "@esbuild/openbsd-x64@0.28.1": + '@esbuild/openbsd-x64@0.28.1': optional: true - "@esbuild/openharmony-arm64@0.28.1": + '@esbuild/openharmony-arm64@0.28.1': optional: true - "@esbuild/sunos-x64@0.18.20": + '@esbuild/sunos-x64@0.18.20': optional: true - "@esbuild/sunos-x64@0.19.12": + '@esbuild/sunos-x64@0.19.12': optional: true - "@esbuild/sunos-x64@0.21.5": + '@esbuild/sunos-x64@0.21.5': optional: true - "@esbuild/sunos-x64@0.28.1": + '@esbuild/sunos-x64@0.28.1': optional: true - "@esbuild/win32-arm64@0.18.20": + '@esbuild/win32-arm64@0.18.20': optional: true - "@esbuild/win32-arm64@0.19.12": + '@esbuild/win32-arm64@0.19.12': optional: true - "@esbuild/win32-arm64@0.21.5": + '@esbuild/win32-arm64@0.21.5': optional: true - "@esbuild/win32-arm64@0.28.1": + '@esbuild/win32-arm64@0.28.1': optional: true - "@esbuild/win32-ia32@0.18.20": + '@esbuild/win32-ia32@0.18.20': optional: true - "@esbuild/win32-ia32@0.19.12": + '@esbuild/win32-ia32@0.19.12': optional: true - "@esbuild/win32-ia32@0.21.5": + '@esbuild/win32-ia32@0.21.5': optional: true - "@esbuild/win32-ia32@0.28.1": + '@esbuild/win32-ia32@0.28.1': optional: true - "@esbuild/win32-x64@0.18.20": + '@esbuild/win32-x64@0.18.20': optional: true - "@esbuild/win32-x64@0.19.12": + '@esbuild/win32-x64@0.19.12': optional: true - "@esbuild/win32-x64@0.21.5": + '@esbuild/win32-x64@0.21.5': optional: true - "@esbuild/win32-x64@0.28.1": + '@esbuild/win32-x64@0.28.1': optional: true - "@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))": + '@floating-ui/core@1.8.0': dependencies: - eslint: 10.6.0(jiti@2.7.0) - eslint-visitor-keys: 3.4.3 + '@floating-ui/utils': 0.2.12 - "@eslint-community/regexpp@4.12.2": {} - - "@eslint/config-array@0.23.5": + '@floating-ui/dom@1.8.0': dependencies: - "@eslint/object-schema": 3.0.5 - debug: 4.4.3 - minimatch: 10.2.5 - transitivePeerDependencies: - - supports-color + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 - "@eslint/config-helpers@0.6.0": + '@floating-ui/react-dom@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@eslint/core": 1.2.1 - - "@eslint/core@1.2.1": - dependencies: - "@types/json-schema": 7.0.15 - - "@eslint/js@10.0.1(eslint@10.6.0(jiti@2.7.0))": - optionalDependencies: - eslint: 10.6.0(jiti@2.7.0) - - "@eslint/object-schema@3.0.5": {} - - "@eslint/plugin-kit@0.7.2": - dependencies: - "@eslint/core": 1.2.1 - levn: 0.4.1 - - "@floating-ui/core@1.8.0": - dependencies: - "@floating-ui/utils": 0.2.12 - - "@floating-ui/dom@1.8.0": - dependencies: - "@floating-ui/core": 1.8.0 - "@floating-ui/utils": 0.2.12 - - "@floating-ui/react-dom@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": - dependencies: - "@floating-ui/dom": 1.8.0 + '@floating-ui/dom': 1.8.0 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - "@floating-ui/utils@0.2.12": {} + '@floating-ui/utils@0.2.12': {} - "@formatjs/fast-memoize@3.1.6": {} + '@formatjs/fast-memoize@3.1.6': {} - "@formatjs/icu-messageformat-parser@3.5.12": + '@formatjs/icu-messageformat-parser@3.5.12': dependencies: - "@formatjs/icu-skeleton-parser": 2.1.10 + '@formatjs/icu-skeleton-parser': 2.1.10 - "@formatjs/icu-skeleton-parser@2.1.10": {} + '@formatjs/icu-skeleton-parser@2.1.10': {} - "@formatjs/intl-localematcher@0.8.10": + '@formatjs/intl-localematcher@0.8.10': dependencies: - "@formatjs/fast-memoize": 3.1.6 + '@formatjs/fast-memoize': 3.1.6 - "@hono/node-server@1.19.14(hono@4.12.27)": + '@hono/node-server@1.19.14(hono@4.12.27)': dependencies: hono: 4.12.27 - "@hookform/resolvers@5.4.0(react-hook-form@7.81.0(react@19.2.7))": + '@hookform/resolvers@5.4.0(react-hook-form@7.81.0(react@19.2.7))': dependencies: - "@standard-schema/utils": 0.3.0 + '@standard-schema/utils': 0.3.0 react-hook-form: 7.81.0(react@19.2.7) - "@humanfs/core@0.19.2": - dependencies: - "@humanfs/types": 0.15.0 - - "@humanfs/node@0.16.8": - dependencies: - "@humanfs/core": 0.19.2 - "@humanfs/types": 0.15.0 - "@humanwhocodes/retry": 0.4.3 - - "@humanfs/types@0.15.0": {} - - "@humanwhocodes/module-importer@1.0.1": {} - - "@humanwhocodes/retry@0.4.3": {} - - "@img/colour@1.1.0": + '@img/colour@1.1.0': optional: true - "@img/sharp-darwin-arm64@0.34.5": + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-darwin-arm64": 1.2.4 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - "@img/sharp-darwin-x64@0.34.5": + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-darwin-x64": 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - "@img/sharp-libvips-darwin-arm64@1.2.4": + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - "@img/sharp-libvips-darwin-x64@1.2.4": + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - "@img/sharp-libvips-linux-arm64@1.2.4": + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - "@img/sharp-libvips-linux-arm@1.2.4": + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - "@img/sharp-libvips-linux-ppc64@1.2.4": + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - "@img/sharp-libvips-linux-riscv64@1.2.4": + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - "@img/sharp-libvips-linux-s390x@1.2.4": + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - "@img/sharp-libvips-linux-x64@1.2.4": + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - "@img/sharp-libvips-linuxmusl-arm64@1.2.4": + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - "@img/sharp-libvips-linuxmusl-x64@1.2.4": + '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true - "@img/sharp-linux-arm64@0.34.5": + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-arm64": 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - "@img/sharp-linux-arm@0.34.5": + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-arm": 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - "@img/sharp-linux-ppc64@0.34.5": + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-ppc64": 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - "@img/sharp-linux-riscv64@0.34.5": + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-riscv64": 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - "@img/sharp-linux-s390x@0.34.5": + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-s390x": 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - "@img/sharp-linux-x64@0.34.5": + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linux-x64": 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - "@img/sharp-linuxmusl-arm64@0.34.5": + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - "@img/sharp-linuxmusl-x64@0.34.5": + '@img/sharp-linuxmusl-x64@0.34.5': optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64": 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true - "@img/sharp-wasm32@0.34.5": + '@img/sharp-wasm32@0.34.5': dependencies: - "@emnapi/runtime": 1.11.1 + '@emnapi/runtime': 1.11.1 optional: true - "@img/sharp-win32-arm64@0.34.5": + '@img/sharp-win32-arm64@0.34.5': optional: true - "@img/sharp-win32-ia32@0.34.5": + '@img/sharp-win32-ia32@0.34.5': optional: true - "@img/sharp-win32-x64@0.34.5": + '@img/sharp-win32-x64@0.34.5': optional: true - "@ioredis/commands@1.10.0": {} + '@ioredis/commands@1.10.0': {} - "@jridgewell/gen-mapping@0.3.13": + '@jridgewell/gen-mapping@0.3.13': dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 - "@jridgewell/trace-mapping": 0.3.31 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 - "@jridgewell/remapping@2.3.5": + '@jridgewell/remapping@2.3.5': dependencies: - "@jridgewell/gen-mapping": 0.3.13 - "@jridgewell/trace-mapping": 0.3.31 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 - "@jridgewell/resolve-uri@3.1.2": {} + '@jridgewell/resolve-uri@3.1.2': {} - "@jridgewell/sourcemap-codec@1.5.5": {} + '@jridgewell/sourcemap-codec@1.5.5': {} - "@jridgewell/trace-mapping@0.3.31": + '@jridgewell/trace-mapping@0.3.31': dependencies: - "@jridgewell/resolve-uri": 3.1.2 - "@jridgewell/sourcemap-codec": 1.5.5 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - "@kurkle/color@0.3.4": {} + '@kurkle/color@0.3.4': {} - "@next/env@16.2.10": {} + '@next/env@16.2.10': {} - "@next/eslint-plugin-next@16.2.10": + '@next/swc-darwin-arm64@16.2.10': + optional: true + + '@next/swc-darwin-x64@16.2.10': + optional: true + + '@next/swc-linux-arm64-gnu@16.2.10': + optional: true + + '@next/swc-linux-arm64-musl@16.2.10': + optional: true + + '@next/swc-linux-x64-gnu@16.2.10': + optional: true + + '@next/swc-linux-x64-musl@16.2.10': + optional: true + + '@next/swc-win32-arm64-msvc@16.2.10': + optional: true + + '@next/swc-win32-x64-msvc@16.2.10': + optional: true + + '@otplib/core@12.0.1': {} + + '@otplib/plugin-crypto@12.0.1': dependencies: - fast-glob: 3.3.1 + '@otplib/core': 12.0.1 - "@next/swc-darwin-arm64@16.2.10": - optional: true - - "@next/swc-darwin-x64@16.2.10": - optional: true - - "@next/swc-linux-arm64-gnu@16.2.10": - optional: true - - "@next/swc-linux-arm64-musl@16.2.10": - optional: true - - "@next/swc-linux-x64-gnu@16.2.10": - optional: true - - "@next/swc-linux-x64-musl@16.2.10": - optional: true - - "@next/swc-win32-arm64-msvc@16.2.10": - optional: true - - "@next/swc-win32-x64-msvc@16.2.10": - optional: true - - "@nodelib/fs.scandir@2.1.5": + '@otplib/plugin-thirty-two@12.0.1': 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 - - "@otplib/core@12.0.1": {} - - "@otplib/plugin-crypto@12.0.1": - dependencies: - "@otplib/core": 12.0.1 - - "@otplib/plugin-thirty-two@12.0.1": - dependencies: - "@otplib/core": 12.0.1 + '@otplib/core': 12.0.1 thirty-two: 1.0.2 - "@otplib/preset-default@12.0.1": + '@otplib/preset-default@12.0.1': dependencies: - "@otplib/core": 12.0.1 - "@otplib/plugin-crypto": 12.0.1 - "@otplib/plugin-thirty-two": 12.0.1 + '@otplib/core': 12.0.1 + '@otplib/plugin-crypto': 12.0.1 + '@otplib/plugin-thirty-two': 12.0.1 - "@otplib/preset-v11@12.0.1": + '@otplib/preset-v11@12.0.1': dependencies: - "@otplib/core": 12.0.1 - "@otplib/plugin-crypto": 12.0.1 - "@otplib/plugin-thirty-two": 12.0.1 + '@otplib/core': 12.0.1 + '@otplib/plugin-crypto': 12.0.1 + '@otplib/plugin-thirty-two': 12.0.1 - "@panva/hkdf@1.2.1": {} + '@panva/hkdf@1.2.1': {} - "@parcel/watcher-android-arm64@2.5.6": + '@parcel/watcher-android-arm64@2.5.6': optional: true - "@parcel/watcher-darwin-arm64@2.5.6": + '@parcel/watcher-darwin-arm64@2.5.6': optional: true - "@parcel/watcher-darwin-x64@2.5.6": + '@parcel/watcher-darwin-x64@2.5.6': optional: true - "@parcel/watcher-freebsd-x64@2.5.6": + '@parcel/watcher-freebsd-x64@2.5.6': optional: true - "@parcel/watcher-linux-arm-glibc@2.5.6": + '@parcel/watcher-linux-arm-glibc@2.5.6': optional: true - "@parcel/watcher-linux-arm-musl@2.5.6": + '@parcel/watcher-linux-arm-musl@2.5.6': optional: true - "@parcel/watcher-linux-arm64-glibc@2.5.6": + '@parcel/watcher-linux-arm64-glibc@2.5.6': optional: true - "@parcel/watcher-linux-arm64-musl@2.5.6": + '@parcel/watcher-linux-arm64-musl@2.5.6': optional: true - "@parcel/watcher-linux-x64-glibc@2.5.6": + '@parcel/watcher-linux-x64-glibc@2.5.6': optional: true - "@parcel/watcher-linux-x64-musl@2.5.6": + '@parcel/watcher-linux-x64-musl@2.5.6': optional: true - "@parcel/watcher-win32-arm64@2.5.6": + '@parcel/watcher-win32-arm64@2.5.6': optional: true - "@parcel/watcher-win32-ia32@2.5.6": + '@parcel/watcher-win32-ia32@2.5.6': optional: true - "@parcel/watcher-win32-x64@2.5.6": + '@parcel/watcher-win32-x64@2.5.6': optional: true - "@parcel/watcher@2.5.6": + '@parcel/watcher@2.5.6': dependencies: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 picomatch: 4.0.4 optionalDependencies: - "@parcel/watcher-android-arm64": 2.5.6 - "@parcel/watcher-darwin-arm64": 2.5.6 - "@parcel/watcher-darwin-x64": 2.5.6 - "@parcel/watcher-freebsd-x64": 2.5.6 - "@parcel/watcher-linux-arm-glibc": 2.5.6 - "@parcel/watcher-linux-arm-musl": 2.5.6 - "@parcel/watcher-linux-arm64-glibc": 2.5.6 - "@parcel/watcher-linux-arm64-musl": 2.5.6 - "@parcel/watcher-linux-x64-glibc": 2.5.6 - "@parcel/watcher-linux-x64-musl": 2.5.6 - "@parcel/watcher-win32-arm64": 2.5.6 - "@parcel/watcher-win32-ia32": 2.5.6 - "@parcel/watcher-win32-x64": 2.5.6 + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 - "@petamoriken/float16@3.9.3": {} + '@petamoriken/float16@3.9.3': {} - "@prisma/adapter-mariadb@7.8.0": + '@prisma/adapter-mariadb@7.8.0': dependencies: - "@prisma/driver-adapter-utils": 7.8.0 + '@prisma/driver-adapter-utils': 7.8.0 mariadb: 3.4.5 - "@prisma/client-runtime-utils@7.8.0": {} + '@prisma/client-runtime-utils@7.8.0': {} - "@prisma/client@7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3)": + '@prisma/client@7.8.0(prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3))(typescript@5.9.3)': dependencies: - "@prisma/client-runtime-utils": 7.8.0 + '@prisma/client-runtime-utils': 7.8.0 optionalDependencies: prisma: 7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3) typescript: 5.9.3 - "@prisma/config@7.8.0": + '@prisma/config@7.8.0': dependencies: c12: 3.3.4 deepmerge-ts: 7.1.5 @@ -8015,19 +4423,19 @@ snapshots: transitivePeerDependencies: - magicast - "@prisma/debug@7.2.0": {} + '@prisma/debug@7.2.0': {} - "@prisma/debug@7.8.0": {} + '@prisma/debug@7.8.0': {} - "@prisma/dev@0.24.3(typescript@5.9.3)": + '@prisma/dev@0.24.3(typescript@5.9.3)': dependencies: - "@electric-sql/pglite": 0.4.1 - "@electric-sql/pglite-socket": 0.1.1(@electric-sql/pglite@0.4.1) - "@electric-sql/pglite-tools": 0.3.1(@electric-sql/pglite@0.4.1) - "@hono/node-server": 1.19.14(hono@4.12.27) - "@prisma/get-platform": 7.2.0 - "@prisma/query-plan-executor": 7.2.0 - "@prisma/streams-local": 0.1.2 + '@electric-sql/pglite': 0.4.1 + '@electric-sql/pglite-socket': 0.1.1(@electric-sql/pglite@0.4.1) + '@electric-sql/pglite-tools': 0.3.1(@electric-sql/pglite@0.4.1) + '@hono/node-server': 1.19.14(hono@4.12.27) + '@prisma/get-platform': 7.2.0 + '@prisma/query-plan-executor': 7.2.0 + '@prisma/streams-local': 0.1.2 foreground-child: 3.3.1 get-port-please: 3.2.0 hono: 4.12.27 @@ -8041,1009 +4449,1009 @@ snapshots: transitivePeerDependencies: - typescript - "@prisma/driver-adapter-utils@7.8.0": + '@prisma/driver-adapter-utils@7.8.0': dependencies: - "@prisma/debug": 7.8.0 + '@prisma/debug': 7.8.0 - "@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a": {} + '@prisma/engines-version@7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a': {} - "@prisma/engines@7.8.0": + '@prisma/engines@7.8.0': dependencies: - "@prisma/debug": 7.8.0 - "@prisma/engines-version": 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a - "@prisma/fetch-engine": 7.8.0 - "@prisma/get-platform": 7.8.0 + '@prisma/debug': 7.8.0 + '@prisma/engines-version': 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a + '@prisma/fetch-engine': 7.8.0 + '@prisma/get-platform': 7.8.0 - "@prisma/fetch-engine@7.8.0": + '@prisma/fetch-engine@7.8.0': dependencies: - "@prisma/debug": 7.8.0 - "@prisma/engines-version": 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a - "@prisma/get-platform": 7.8.0 + '@prisma/debug': 7.8.0 + '@prisma/engines-version': 7.8.0-6.3c6e192761c0362d496ed980de936e2f3cebcd3a + '@prisma/get-platform': 7.8.0 - "@prisma/get-platform@7.2.0": + '@prisma/get-platform@7.2.0': dependencies: - "@prisma/debug": 7.2.0 + '@prisma/debug': 7.2.0 - "@prisma/get-platform@7.8.0": + '@prisma/get-platform@7.8.0': dependencies: - "@prisma/debug": 7.8.0 + '@prisma/debug': 7.8.0 - "@prisma/query-plan-executor@7.2.0": {} + '@prisma/query-plan-executor@7.2.0': {} - "@prisma/streams-local@0.1.2": + '@prisma/streams-local@0.1.2': dependencies: ajv: 8.20.0 better-result: 2.9.2 env-paths: 3.0.0 proper-lockfile: 4.1.2 - "@prisma/studio-core@0.27.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@prisma/studio-core@0.27.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-toggle": 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@types/react": 19.2.17 + '@radix-ui/react-toggle': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@types/react': 19.2.17 chart.js: 4.5.1 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - - "@types/react-dom" + - '@types/react-dom' - "@radix-ui/number@1.1.2": {} + '@radix-ui/number@1.1.2': {} - "@radix-ui/primitive@1.1.3": {} + '@radix-ui/primitive@1.1.3': {} - "@radix-ui/primitive@1.1.5": {} + '@radix-ui/primitive@1.1.5': {} - "@radix-ui/react-accessible-icon@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-accessible-icon@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-visually-hidden": 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-accordion@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-accordion@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collapsible": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collapsible': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-alert-dialog@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-alert-dialog@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dialog": 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-arrow@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-aspect-ratio@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-aspect-ratio@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-avatar@1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-avatar@1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-checkbox@1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-checkbox@1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-collapsible@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-collapsible@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-collection@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-collection@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-context-menu@2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-context-menu@2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-menu": 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-context@1.2.0(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-dialog@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-dialog@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-focus-scope": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-dismissable-layer@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-dismissable-layer@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-dropdown-menu@2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-dropdown-menu@2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-menu": 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-focus-scope@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-focus-scope@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-form@0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-form@0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-label": 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-label': 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-hover-card@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-hover-card@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-popper": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-label@2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-label@2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-menu@2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-menu@2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-focus-scope": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-popper": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-menubar@1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-menubar@1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-menu": 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-navigation-menu@1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-navigation-menu@1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-visually-hidden": 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-one-time-password-field@0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-one-time-password-field@0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/number": 1.1.2 - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-password-toggle-field@0.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-password-toggle-field@0.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-popover@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-popover@1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-focus-scope": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-popper": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-popper@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-popper@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@floating-ui/react-dom": 2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-arrow": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-rect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/rect": 1.1.2 + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-portal@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-presence@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-presence@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-slot": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-primitive@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-progress@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-progress@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-radio-group@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-radio-group@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-roving-focus@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-roving-focus@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-scroll-area@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-scroll-area@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/number": 1.1.2 - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-select@2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-select@2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/number": 1.1.2 - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-focus-scope": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-popper": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-visually-hidden": 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) aria-hidden: 1.2.6 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-separator@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-separator@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-slider@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-slider@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/number": 1.1.2 - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-switch@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-switch@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-previous": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-tabs@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-tabs@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-toast@1.2.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-toast@1.2.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-visually-hidden": 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-toggle-group@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-toggle-group@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-toggle": 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.3 - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-toggle@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-toggle@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-toolbar@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-toolbar@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-separator": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-toggle-group": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-tooltip@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-tooltip@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-popper": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-visually-hidden": 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-effect-event": 0.0.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-layout-effect": 1.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-escape-keydown@1.1.3(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-escape-keydown@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/rect": 1.1.2 + '@radix-ui/rect': 1.1.2 react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)": + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) react: 19.2.7 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@radix-ui/react-visually-hidden@1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - "@radix-ui/rect@1.1.2": {} + '@radix-ui/rect@1.1.2': {} - "@rollup/rollup-android-arm-eabi@4.62.2": + '@rollup/rollup-android-arm-eabi@4.62.2': optional: true - "@rollup/rollup-android-arm64@4.62.2": + '@rollup/rollup-android-arm64@4.62.2': optional: true - "@rollup/rollup-darwin-arm64@4.62.2": + '@rollup/rollup-darwin-arm64@4.62.2': optional: true - "@rollup/rollup-darwin-x64@4.62.2": + '@rollup/rollup-darwin-x64@4.62.2': optional: true - "@rollup/rollup-freebsd-arm64@4.62.2": + '@rollup/rollup-freebsd-arm64@4.62.2': optional: true - "@rollup/rollup-freebsd-x64@4.62.2": + '@rollup/rollup-freebsd-x64@4.62.2': optional: true - "@rollup/rollup-linux-arm-gnueabihf@4.62.2": + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': optional: true - "@rollup/rollup-linux-arm-musleabihf@4.62.2": + '@rollup/rollup-linux-arm-musleabihf@4.62.2': optional: true - "@rollup/rollup-linux-arm64-gnu@4.62.2": + '@rollup/rollup-linux-arm64-gnu@4.62.2': optional: true - "@rollup/rollup-linux-arm64-musl@4.62.2": + '@rollup/rollup-linux-arm64-musl@4.62.2': optional: true - "@rollup/rollup-linux-loong64-gnu@4.62.2": + '@rollup/rollup-linux-loong64-gnu@4.62.2': optional: true - "@rollup/rollup-linux-loong64-musl@4.62.2": + '@rollup/rollup-linux-loong64-musl@4.62.2': optional: true - "@rollup/rollup-linux-ppc64-gnu@4.62.2": + '@rollup/rollup-linux-ppc64-gnu@4.62.2': optional: true - "@rollup/rollup-linux-ppc64-musl@4.62.2": + '@rollup/rollup-linux-ppc64-musl@4.62.2': optional: true - "@rollup/rollup-linux-riscv64-gnu@4.62.2": + '@rollup/rollup-linux-riscv64-gnu@4.62.2': optional: true - "@rollup/rollup-linux-riscv64-musl@4.62.2": + '@rollup/rollup-linux-riscv64-musl@4.62.2': optional: true - "@rollup/rollup-linux-s390x-gnu@4.62.2": + '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true - "@rollup/rollup-linux-x64-gnu@4.62.2": + '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true - "@rollup/rollup-linux-x64-musl@4.62.2": + '@rollup/rollup-linux-x64-musl@4.62.2': optional: true - "@rollup/rollup-openbsd-x64@4.62.2": + '@rollup/rollup-openbsd-x64@4.62.2': optional: true - "@rollup/rollup-openharmony-arm64@4.62.2": + '@rollup/rollup-openharmony-arm64@4.62.2': optional: true - "@rollup/rollup-win32-arm64-msvc@4.62.2": + '@rollup/rollup-win32-arm64-msvc@4.62.2': optional: true - "@rollup/rollup-win32-ia32-msvc@4.62.2": + '@rollup/rollup-win32-ia32-msvc@4.62.2': optional: true - "@rollup/rollup-win32-x64-gnu@4.62.2": + '@rollup/rollup-win32-x64-gnu@4.62.2': optional: true - "@rollup/rollup-win32-x64-msvc@4.62.2": + '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true - "@schummar/icu-type-parser@1.21.5": {} + '@schummar/icu-type-parser@1.21.5': {} - "@stablelib/base64@1.0.1": {} + '@stablelib/base64@1.0.1': {} - "@standard-schema/spec@1.1.0": {} + '@standard-schema/spec@1.1.0': {} - "@standard-schema/utils@0.3.0": {} + '@standard-schema/utils@0.3.0': {} - "@swc/core-darwin-arm64@1.15.43": + '@swc/core-darwin-arm64@1.15.43': optional: true - "@swc/core-darwin-x64@1.15.43": + '@swc/core-darwin-x64@1.15.43': optional: true - "@swc/core-linux-arm-gnueabihf@1.15.43": + '@swc/core-linux-arm-gnueabihf@1.15.43': optional: true - "@swc/core-linux-arm64-gnu@1.15.43": + '@swc/core-linux-arm64-gnu@1.15.43': optional: true - "@swc/core-linux-arm64-musl@1.15.43": + '@swc/core-linux-arm64-musl@1.15.43': optional: true - "@swc/core-linux-ppc64-gnu@1.15.43": + '@swc/core-linux-ppc64-gnu@1.15.43': optional: true - "@swc/core-linux-s390x-gnu@1.15.43": + '@swc/core-linux-s390x-gnu@1.15.43': optional: true - "@swc/core-linux-x64-gnu@1.15.43": + '@swc/core-linux-x64-gnu@1.15.43': optional: true - "@swc/core-linux-x64-musl@1.15.43": + '@swc/core-linux-x64-musl@1.15.43': optional: true - "@swc/core-win32-arm64-msvc@1.15.43": + '@swc/core-win32-arm64-msvc@1.15.43': optional: true - "@swc/core-win32-ia32-msvc@1.15.43": + '@swc/core-win32-ia32-msvc@1.15.43': optional: true - "@swc/core-win32-x64-msvc@1.15.43": + '@swc/core-win32-x64-msvc@1.15.43': optional: true - "@swc/core@1.15.43": + '@swc/core@1.15.43': dependencies: - "@swc/counter": 0.1.3 - "@swc/types": 0.1.27 + '@swc/counter': 0.1.3 + '@swc/types': 0.1.27 optionalDependencies: - "@swc/core-darwin-arm64": 1.15.43 - "@swc/core-darwin-x64": 1.15.43 - "@swc/core-linux-arm-gnueabihf": 1.15.43 - "@swc/core-linux-arm64-gnu": 1.15.43 - "@swc/core-linux-arm64-musl": 1.15.43 - "@swc/core-linux-ppc64-gnu": 1.15.43 - "@swc/core-linux-s390x-gnu": 1.15.43 - "@swc/core-linux-x64-gnu": 1.15.43 - "@swc/core-linux-x64-musl": 1.15.43 - "@swc/core-win32-arm64-msvc": 1.15.43 - "@swc/core-win32-ia32-msvc": 1.15.43 - "@swc/core-win32-x64-msvc": 1.15.43 + '@swc/core-darwin-arm64': 1.15.43 + '@swc/core-darwin-x64': 1.15.43 + '@swc/core-linux-arm-gnueabihf': 1.15.43 + '@swc/core-linux-arm64-gnu': 1.15.43 + '@swc/core-linux-arm64-musl': 1.15.43 + '@swc/core-linux-ppc64-gnu': 1.15.43 + '@swc/core-linux-s390x-gnu': 1.15.43 + '@swc/core-linux-x64-gnu': 1.15.43 + '@swc/core-linux-x64-musl': 1.15.43 + '@swc/core-win32-arm64-msvc': 1.15.43 + '@swc/core-win32-ia32-msvc': 1.15.43 + '@swc/core-win32-x64-msvc': 1.15.43 - "@swc/counter@0.1.3": {} + '@swc/counter@0.1.3': {} - "@swc/helpers@0.5.15": + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 - "@swc/types@0.1.27": + '@swc/types@0.1.27': dependencies: - "@swc/counter": 0.1.3 + '@swc/counter': 0.1.3 - "@tailwindcss/forms@0.5.11(tailwindcss@4.3.2)": + '@tailwindcss/forms@0.5.11(tailwindcss@4.3.2)': dependencies: mini-svg-data-uri: 1.4.4 tailwindcss: 4.3.2 - "@tailwindcss/node@4.3.2": + '@tailwindcss/node@4.3.2': dependencies: - "@jridgewell/remapping": 2.3.5 + '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.21.6 jiti: 2.7.0 lightningcss: 1.32.0 @@ -9051,285 +5459,177 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.3.2 - "@tailwindcss/oxide-android-arm64@4.3.2": + '@tailwindcss/oxide-android-arm64@4.3.2': optional: true - "@tailwindcss/oxide-darwin-arm64@4.3.2": + '@tailwindcss/oxide-darwin-arm64@4.3.2': optional: true - "@tailwindcss/oxide-darwin-x64@4.3.2": + '@tailwindcss/oxide-darwin-x64@4.3.2': optional: true - "@tailwindcss/oxide-freebsd-x64@4.3.2": + '@tailwindcss/oxide-freebsd-x64@4.3.2': optional: true - "@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2": + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': optional: true - "@tailwindcss/oxide-linux-arm64-gnu@4.3.2": + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': optional: true - "@tailwindcss/oxide-linux-arm64-musl@4.3.2": + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': optional: true - "@tailwindcss/oxide-linux-x64-gnu@4.3.2": + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': optional: true - "@tailwindcss/oxide-linux-x64-musl@4.3.2": + '@tailwindcss/oxide-linux-x64-musl@4.3.2': optional: true - "@tailwindcss/oxide-wasm32-wasi@4.3.2": + '@tailwindcss/oxide-wasm32-wasi@4.3.2': optional: true - "@tailwindcss/oxide-win32-arm64-msvc@4.3.2": + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': optional: true - "@tailwindcss/oxide-win32-x64-msvc@4.3.2": + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': optional: true - "@tailwindcss/oxide@4.3.2": + '@tailwindcss/oxide@4.3.2': optionalDependencies: - "@tailwindcss/oxide-android-arm64": 4.3.2 - "@tailwindcss/oxide-darwin-arm64": 4.3.2 - "@tailwindcss/oxide-darwin-x64": 4.3.2 - "@tailwindcss/oxide-freebsd-x64": 4.3.2 - "@tailwindcss/oxide-linux-arm-gnueabihf": 4.3.2 - "@tailwindcss/oxide-linux-arm64-gnu": 4.3.2 - "@tailwindcss/oxide-linux-arm64-musl": 4.3.2 - "@tailwindcss/oxide-linux-x64-gnu": 4.3.2 - "@tailwindcss/oxide-linux-x64-musl": 4.3.2 - "@tailwindcss/oxide-wasm32-wasi": 4.3.2 - "@tailwindcss/oxide-win32-arm64-msvc": 4.3.2 - "@tailwindcss/oxide-win32-x64-msvc": 4.3.2 + '@tailwindcss/oxide-android-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-x64': 4.3.2 + '@tailwindcss/oxide-freebsd-x64': 4.3.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-x64-musl': 4.3.2 + '@tailwindcss/oxide-wasm32-wasi': 4.3.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 - "@tailwindcss/postcss@4.3.2": + '@tailwindcss/postcss@4.3.2': dependencies: - "@alloc/quick-lru": 5.2.0 - "@tailwindcss/node": 4.3.2 - "@tailwindcss/oxide": 4.3.2 + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 postcss: 8.5.16 tailwindcss: 4.3.2 - "@tailwindcss/typography@0.5.20(tailwindcss@4.3.2)": + '@tailwindcss/typography@0.5.20(tailwindcss@4.3.2)': dependencies: postcss-selector-parser: 6.0.10 tailwindcss: 4.3.2 - "@tanstack/react-virtual@3.14.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)": + '@tanstack/react-virtual@3.14.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - "@tanstack/virtual-core": 3.17.3 + '@tanstack/virtual-core': 3.17.3 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - "@tanstack/virtual-core@3.17.3": {} + '@tanstack/virtual-core@3.17.3': {} - "@tokenizer/inflate@0.4.1": + '@tokenizer/inflate@0.4.1': dependencies: debug: 4.4.3 token-types: 6.1.2 transitivePeerDependencies: - supports-color - "@tokenizer/token@0.3.0": {} + '@tokenizer/token@0.3.0': {} - "@types/esrecurse@4.3.1": {} + '@types/estree@1.0.9': {} - "@types/estree@1.0.9": {} + '@types/geojson@7946.0.16': {} - "@types/geojson@7946.0.16": {} + '@types/http-errors@1.8.2': {} - "@types/http-errors@1.8.2": {} - - "@types/jpeg-js@0.3.7": + '@types/jpeg-js@0.3.7': dependencies: jpeg-js: 0.4.4 - "@types/json-schema@7.0.15": {} - - "@types/node@22.20.0": + '@types/node@22.20.0': dependencies: undici-types: 6.21.0 - "@types/node@24.13.2": + '@types/node@24.13.2': dependencies: undici-types: 7.18.2 - "@types/nodemailer@6.4.24": + '@types/nodemailer@6.4.24': dependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 - "@types/react-dom@19.2.3(@types/react@19.2.17)": + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 - "@types/react@19.2.17": + '@types/react@19.2.17': dependencies: csstype: 3.2.3 - "@types/sanitize-html@2.16.1": + '@types/sanitize-html@2.16.1': dependencies: htmlparser2: 10.1.0 - "@typescript-eslint/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3)": + '@vitalets/google-translate-api@9.2.1': dependencies: - "@eslint-community/regexpp": 4.12.2 - "@typescript-eslint/parser": 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - "@typescript-eslint/scope-manager": 8.63.0 - "@typescript-eslint/type-utils": 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - "@typescript-eslint/utils": 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.63.0 - eslint: 10.6.0(jiti@2.7.0) - ignore: 7.0.6 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3)": - dependencies: - "@typescript-eslint/scope-manager": 8.63.0 - "@typescript-eslint/types": 8.63.0 - "@typescript-eslint/typescript-estree": 8.63.0(typescript@5.9.3) - "@typescript-eslint/visitor-keys": 8.63.0 - debug: 4.4.3 - eslint: 10.6.0(jiti@2.7.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/project-service@8.63.0(typescript@5.9.3)": - dependencies: - "@typescript-eslint/tsconfig-utils": 8.63.0(typescript@5.9.3) - "@typescript-eslint/types": 8.63.0 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/scope-manager@8.63.0": - dependencies: - "@typescript-eslint/types": 8.63.0 - "@typescript-eslint/visitor-keys": 8.63.0 - - "@typescript-eslint/tsconfig-utils@8.63.0(typescript@5.9.3)": - dependencies: - typescript: 5.9.3 - - "@typescript-eslint/type-utils@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3)": - dependencies: - "@typescript-eslint/types": 8.63.0 - "@typescript-eslint/typescript-estree": 8.63.0(typescript@5.9.3) - "@typescript-eslint/utils": 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - debug: 4.4.3 - eslint: 10.6.0(jiti@2.7.0) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/types@8.63.0": {} - - "@typescript-eslint/typescript-estree@8.63.0(typescript@5.9.3)": - dependencies: - "@typescript-eslint/project-service": 8.63.0(typescript@5.9.3) - "@typescript-eslint/tsconfig-utils": 8.63.0(typescript@5.9.3) - "@typescript-eslint/types": 8.63.0 - "@typescript-eslint/visitor-keys": 8.63.0 - debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.8.5 - tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/utils@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3)": - dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@10.6.0(jiti@2.7.0)) - "@typescript-eslint/scope-manager": 8.63.0 - "@typescript-eslint/types": 8.63.0 - "@typescript-eslint/typescript-estree": 8.63.0(typescript@5.9.3) - eslint: 10.6.0(jiti@2.7.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - "@typescript-eslint/visitor-keys@8.63.0": - dependencies: - "@typescript-eslint/types": 8.63.0 - eslint-visitor-keys: 5.0.1 - - "@vitalets/google-translate-api@9.2.1": - dependencies: - "@types/http-errors": 1.8.2 + '@types/http-errors': 1.8.2 http-errors: 2.0.1 node-fetch: 2.7.0 transitivePeerDependencies: - encoding - "@vitest/expect@2.1.9": + '@vitest/expect@2.1.9': dependencies: - "@vitest/spy": 2.1.9 - "@vitest/utils": 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 chai: 5.3.3 tinyrainbow: 1.2.0 - "@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.20.0)(lightningcss@1.32.0))": + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.20.0)(lightningcss@1.32.0))': dependencies: - "@vitest/spy": 2.1.9 + '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 5.4.21(@types/node@22.20.0)(lightningcss@1.32.0) - "@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.20.0))": + '@vitest/mocker@2.1.9(vite@5.4.21(@types/node@22.20.0))': dependencies: - "@vitest/spy": 2.1.9 + '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 5.4.21(@types/node@22.20.0) - "@vitest/pretty-format@2.1.9": + '@vitest/pretty-format@2.1.9': dependencies: tinyrainbow: 1.2.0 - "@vitest/runner@2.1.9": + '@vitest/runner@2.1.9': dependencies: - "@vitest/utils": 2.1.9 + '@vitest/utils': 2.1.9 pathe: 1.1.2 - "@vitest/snapshot@2.1.9": + '@vitest/snapshot@2.1.9': dependencies: - "@vitest/pretty-format": 2.1.9 + '@vitest/pretty-format': 2.1.9 magic-string: 0.30.21 pathe: 1.1.2 - "@vitest/spy@2.1.9": + '@vitest/spy@2.1.9': dependencies: tinyspy: 3.0.2 - "@vitest/utils@2.1.9": + '@vitest/utils@2.1.9': dependencies: - "@vitest/pretty-format": 2.1.9 + '@vitest/pretty-format': 2.1.9 loupe: 3.2.1 tinyrainbow: 1.2.0 - acorn-jsx@5.3.2(acorn@8.17.0): - dependencies: - acorn: 8.17.0 - - acorn@8.17.0: {} - - ajv@6.15.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.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -9341,97 +5641,16 @@ snapshots: dependencies: tslib: 2.8.1 - aria-query@5.3.2: {} - - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - - 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.2 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - math-intrinsics: 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 - assertion-error@2.0.1: {} - ast-types-flow@0.0.8: {} - - async-function@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - aws-ssl-profiles@1.1.2: {} - axe-core@4.12.1: {} - - axobject-query@4.1.0: {} - - balanced-match@1.0.2: {} - - balanced-match@4.0.4: {} - baseline-browser-mapping@2.10.40: {} - baseline-browser-mapping@2.10.42: {} - bcryptjs@3.0.3: {} better-result@2.9.2: {} - brace-expansion@1.1.16: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@5.0.7: - dependencies: - balanced-match: 4.0.4 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.5: - dependencies: - baseline-browser-mapping: 2.10.42 - caniuse-lite: 1.0.30001803 - electron-to-chromium: 1.5.389 - node-releases: 2.0.51 - update-browserslist-db: 1.2.3(browserslist@4.28.5) - buffer-from@1.1.2: {} c12@3.3.4: @@ -9451,27 +5670,8 @@ snapshots: cac@6.7.14: {} - 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 - caniuse-lite@1.0.30001799: {} - caniuse-lite@1.0.30001803: {} - chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -9482,7 +5682,7 @@ snapshots: chart.js@4.5.1: dependencies: - "@kurkle/color": 0.3.4 + '@kurkle/color': 0.3.4 check-error@2.1.3: {} @@ -9502,24 +5702,20 @@ snapshots: cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - "@radix-ui/react-compose-refs": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dialog": 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-id": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-primitive": 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - - "@types/react" - - "@types/react-dom" - - concat-map@0.0.1: {} + - '@types/react' + - '@types/react-dom' confbox@0.2.4: {} content-type@2.0.0: {} - convert-source-map@2.0.0: {} - core-util-is@1.0.3: {} croner@10.0.1: {} @@ -9534,26 +5730,6 @@ snapshots: csstype@3.2.3: {} - damerau-levenshtein@1.0.8: {} - - 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 - dayjs@1.11.21: {} debug@4.4.3: @@ -9562,24 +5738,10 @@ snapshots: deep-eql@5.0.2: {} - deep-is@0.1.4: {} - deepmerge-ts@7.1.5: {} deepmerge@4.3.1: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - defu@6.1.7: {} denque@2.1.0: {} @@ -9616,8 +5778,8 @@ snapshots: drizzle-kit@0.30.6: dependencies: - "@drizzle-team/brocli": 0.10.2 - "@esbuild-kit/esm-loader": 2.6.5 + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 esbuild: 0.19.12 esbuild-register: 3.6.0(esbuild@0.19.12) gel: 2.2.0 @@ -9628,21 +5790,11 @@ snapshots: optionalDependencies: mysql2: 3.22.5(@types/node@22.20.0) - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - effect@3.20.0: dependencies: - "@standard-schema/spec": 1.1.0 + '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 - electron-to-chromium@1.5.389: {} - - emoji-regex@9.2.2: {} - empathic@2.0.0: {} enhanced-resolve@5.21.6: @@ -9656,100 +5808,8 @@ snapshots: env-paths@3.0.0: {} - es-abstract-get@1.0.0: - dependencies: - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - is-callable: 1.2.7 - object-inspect: 1.13.4 - - 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.2 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.4 - function.prototype.name: 1.2.0 - 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.4 - 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.4 - 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.11 - string.prototype.trimend: 1.0.10 - 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.8 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.22 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} - es-object-atoms@1.1.2: - 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.4 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.4 - - es-to-primitive@1.3.4: - dependencies: - es-abstract-get: 1.0.0 - es-define-property: 1.0.1 - es-errors: 1.3.0 - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - esbuild-register@3.6.0(esbuild@0.19.12): dependencies: debug: 4.4.3 @@ -9759,221 +5819,115 @@ snapshots: esbuild@0.18.20: optionalDependencies: - "@esbuild/android-arm": 0.18.20 - "@esbuild/android-arm64": 0.18.20 - "@esbuild/android-x64": 0.18.20 - "@esbuild/darwin-arm64": 0.18.20 - "@esbuild/darwin-x64": 0.18.20 - "@esbuild/freebsd-arm64": 0.18.20 - "@esbuild/freebsd-x64": 0.18.20 - "@esbuild/linux-arm": 0.18.20 - "@esbuild/linux-arm64": 0.18.20 - "@esbuild/linux-ia32": 0.18.20 - "@esbuild/linux-loong64": 0.18.20 - "@esbuild/linux-mips64el": 0.18.20 - "@esbuild/linux-ppc64": 0.18.20 - "@esbuild/linux-riscv64": 0.18.20 - "@esbuild/linux-s390x": 0.18.20 - "@esbuild/linux-x64": 0.18.20 - "@esbuild/netbsd-x64": 0.18.20 - "@esbuild/openbsd-x64": 0.18.20 - "@esbuild/sunos-x64": 0.18.20 - "@esbuild/win32-arm64": 0.18.20 - "@esbuild/win32-ia32": 0.18.20 - "@esbuild/win32-x64": 0.18.20 + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 esbuild@0.19.12: optionalDependencies: - "@esbuild/aix-ppc64": 0.19.12 - "@esbuild/android-arm": 0.19.12 - "@esbuild/android-arm64": 0.19.12 - "@esbuild/android-x64": 0.19.12 - "@esbuild/darwin-arm64": 0.19.12 - "@esbuild/darwin-x64": 0.19.12 - "@esbuild/freebsd-arm64": 0.19.12 - "@esbuild/freebsd-x64": 0.19.12 - "@esbuild/linux-arm": 0.19.12 - "@esbuild/linux-arm64": 0.19.12 - "@esbuild/linux-ia32": 0.19.12 - "@esbuild/linux-loong64": 0.19.12 - "@esbuild/linux-mips64el": 0.19.12 - "@esbuild/linux-ppc64": 0.19.12 - "@esbuild/linux-riscv64": 0.19.12 - "@esbuild/linux-s390x": 0.19.12 - "@esbuild/linux-x64": 0.19.12 - "@esbuild/netbsd-x64": 0.19.12 - "@esbuild/openbsd-x64": 0.19.12 - "@esbuild/sunos-x64": 0.19.12 - "@esbuild/win32-arm64": 0.19.12 - "@esbuild/win32-ia32": 0.19.12 - "@esbuild/win32-x64": 0.19.12 + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 esbuild@0.21.5: optionalDependencies: - "@esbuild/aix-ppc64": 0.21.5 - "@esbuild/android-arm": 0.21.5 - "@esbuild/android-arm64": 0.21.5 - "@esbuild/android-x64": 0.21.5 - "@esbuild/darwin-arm64": 0.21.5 - "@esbuild/darwin-x64": 0.21.5 - "@esbuild/freebsd-arm64": 0.21.5 - "@esbuild/freebsd-x64": 0.21.5 - "@esbuild/linux-arm": 0.21.5 - "@esbuild/linux-arm64": 0.21.5 - "@esbuild/linux-ia32": 0.21.5 - "@esbuild/linux-loong64": 0.21.5 - "@esbuild/linux-mips64el": 0.21.5 - "@esbuild/linux-ppc64": 0.21.5 - "@esbuild/linux-riscv64": 0.21.5 - "@esbuild/linux-s390x": 0.21.5 - "@esbuild/linux-x64": 0.21.5 - "@esbuild/netbsd-x64": 0.21.5 - "@esbuild/openbsd-x64": 0.21.5 - "@esbuild/sunos-x64": 0.21.5 - "@esbuild/win32-arm64": 0.21.5 - "@esbuild/win32-ia32": 0.21.5 - "@esbuild/win32-x64": 0.21.5 + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 esbuild@0.28.1: optionalDependencies: - "@esbuild/aix-ppc64": 0.28.1 - "@esbuild/android-arm": 0.28.1 - "@esbuild/android-arm64": 0.28.1 - "@esbuild/android-x64": 0.28.1 - "@esbuild/darwin-arm64": 0.28.1 - "@esbuild/darwin-x64": 0.28.1 - "@esbuild/freebsd-arm64": 0.28.1 - "@esbuild/freebsd-x64": 0.28.1 - "@esbuild/linux-arm": 0.28.1 - "@esbuild/linux-arm64": 0.28.1 - "@esbuild/linux-ia32": 0.28.1 - "@esbuild/linux-loong64": 0.28.1 - "@esbuild/linux-mips64el": 0.28.1 - "@esbuild/linux-ppc64": 0.28.1 - "@esbuild/linux-riscv64": 0.28.1 - "@esbuild/linux-s390x": 0.28.1 - "@esbuild/linux-x64": 0.28.1 - "@esbuild/netbsd-arm64": 0.28.1 - "@esbuild/netbsd-x64": 0.28.1 - "@esbuild/openbsd-arm64": 0.28.1 - "@esbuild/openbsd-x64": 0.28.1 - "@esbuild/openharmony-arm64": 0.28.1 - "@esbuild/sunos-x64": 0.28.1 - "@esbuild/win32-arm64": 0.28.1 - "@esbuild/win32-ia32": 0.28.1 - "@esbuild/win32-x64": 0.28.1 - - escalade@3.2.0: {} + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.6.0(jiti@2.7.0)): - dependencies: - eslint: 10.6.0(jiti@2.7.0) - - eslint-plugin-jsx-a11y@6.10.2(eslint@10.6.0(jiti@2.7.0)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.9 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.12.1 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 10.6.0(jiti@2.7.0) - hasown: 2.0.4 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.5 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - - eslint-plugin-react-hooks@7.1.1(eslint@10.6.0(jiti@2.7.0)): - dependencies: - "@babel/core": 7.29.7 - "@babel/parser": 7.29.7 - eslint: 10.6.0(jiti@2.7.0) - hermes-parser: 0.25.1 - zod: 3.25.76 - zod-validation-error: 4.0.2(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - eslint-plugin-security@4.0.1: - dependencies: - safe-regex: 2.1.1 - - eslint-scope@9.1.2: - dependencies: - "@types/esrecurse": 4.3.1 - "@types/estree": 1.0.9 - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@5.0.1: {} - - eslint@10.6.0(jiti@2.7.0): - dependencies: - "@eslint-community/eslint-utils": 4.9.1(eslint@10.6.0(jiti@2.7.0)) - "@eslint-community/regexpp": 4.12.2 - "@eslint/config-array": 0.23.5 - "@eslint/config-helpers": 0.6.0 - "@eslint/core": 1.2.1 - "@eslint/plugin-kit": 0.7.2 - "@humanfs/node": 0.16.8 - "@humanwhocodes/module-importer": 1.0.1 - "@humanwhocodes/retry": 0.4.3 - "@types/estree": 1.0.9 - ajv: 6.15.0 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 9.1.2 - eslint-visitor-keys: 5.0.1 - espree: 11.2.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 - minimatch: 10.2.5 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.7.0 - transitivePeerDependencies: - - supports-color - - espree@11.2.0: - dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) - eslint-visitor-keys: 5.0.1 - - esquery@1.7.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - estree-walker@3.0.3: dependencies: - "@types/estree": 1.0.9 - - esutils@2.0.3: {} + '@types/estree': 1.0.9 expect-type@1.4.0: {} @@ -9985,63 +5939,19 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.1: - dependencies: - "@nodelib/fs.stat": 2.0.5 - "@nodelib/fs.walk": 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - fast-sha256@1.3.0: {} fast-uri@3.1.3: {} - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - file-type@21.3.4: dependencies: - "@tokenizer/inflate": 0.4.1 + '@tokenizer/inflate': 0.4.1 strtok3: 10.3.5 token-types: 6.1.2 uint8array-extras: 1.5.0 transitivePeerDependencies: - supports-color - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.4.2 - keyv: 4.5.4 - - flatted@3.4.2: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -10050,25 +5960,9 @@ snapshots: fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.2.0: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - es-define-property: 1.0.1 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - hasown: 2.0.4 - is-callable: 1.2.7 - is-document.all: 1.0.0 - - functions-have-names@1.2.3: {} - gel@2.2.0: dependencies: - "@petamoriken/float16": 3.9.3 + '@petamoriken/float16': 3.9.3 debug: 4.4.3 env-paths: 3.0.0 semver: 7.8.5 @@ -10081,93 +5975,24 @@ snapshots: dependencies: is-property: 1.0.2 - generator-function@2.0.1: {} - - gensync@1.0.0-beta.2: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.4 - math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} get-port-please@3.2.0: {} - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.2 - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 giget@3.3.0: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - gopd@1.2.0: {} - graceful-fs@4.2.11: {} grammex@3.1.12: {} graphmatch@1.1.1: {} - has-bigints@1.1.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - hash-wasm@4.12.0: {} - hasown@2.0.4: - dependencies: - function-bind: 1.1.2 - - hermes-estree@0.25.1: {} - - hermes-parser@0.25.1: - dependencies: - hermes-estree: 0.25.1 - hono@4.12.27: {} htmlparser2@10.1.0: @@ -10197,34 +6022,22 @@ snapshots: icu-minify@4.13.1: dependencies: - "@formatjs/icu-messageformat-parser": 3.5.12 + '@formatjs/icu-messageformat-parser': 3.5.12 ieee754@1.2.1: {} - ignore@5.3.2: {} - - ignore@7.0.6: {} - immediate@3.0.6: {} - imurmurhash@0.1.4: {} - inherits@2.0.4: {} - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.4 - side-channel: 1.1.1 - intl-messageformat@11.2.9: dependencies: - "@formatjs/fast-memoize": 3.1.6 - "@formatjs/icu-messageformat-parser": 3.5.12 + '@formatjs/fast-memoize': 3.1.6 + '@formatjs/icu-messageformat-parser': 3.5.12 ioredis@5.11.1: dependencies: - "@ioredis/commands": 1.10.0 + '@ioredis/commands': 1.10.0 cluster-key-slot: 1.1.1 debug: 4.4.3 denque: 2.1.0 @@ -10234,122 +6047,18 @@ snapshots: transitivePeerDependencies: - supports-color - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-document.all@1.0.0: - dependencies: - call-bound: 1.0.4 - is-extglob@2.1.1: {} - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-generator-function@1.1.2: - dependencies: - call-bound: 1.0.4 - generator-function: 2.0.1 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - is-plain-object@5.0.0: {} is-property@1.0.2: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.4 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.22 - - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - isarray@1.0.0: {} - isarray@2.0.5: {} - isexe@2.0.0: {} isexe@3.1.5: {} @@ -10360,27 +6069,10 @@ snapshots: jpeg-js@0.4.4: {} - js-tokens@4.0.0: {} - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - json5@2.2.3: {} - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.9 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - jszip@3.10.1: dependencies: lie: 3.3.0 @@ -10388,25 +6080,10 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - language-subtag-registry@0.3.23: {} - - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 - launder@1.7.1: dependencies: dayjs: 1.11.21 - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - lie@3.3.0: dependencies: immediate: 3.0.6 @@ -10460,20 +6137,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - long@5.3.2: {} loupe@3.2.1: {} lru-cache@10.4.3: {} - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - lru.min@1.1.4: {} lucide-react@1.23.0(react@19.2.7): @@ -10484,43 +6153,26 @@ snapshots: magic-string@0.30.21: dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 + '@jridgewell/sourcemap-codec': 1.5.5 mariadb@3.4.5: dependencies: - "@types/geojson": 7946.0.16 - "@types/node": 24.13.2 + '@types/geojson': 7946.0.16 + '@types/node': 24.13.2 denque: 2.1.0 iconv-lite: 0.6.3 lru-cache: 10.4.3 - math-intrinsics@1.1.0: {} - media-typer@2.0.0: {} - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - mini-svg-data-uri@1.4.4: {} - minimatch@10.2.5: - dependencies: - brace-expansion: 5.0.7 - - minimatch@3.1.5: - dependencies: - brace-expansion: 1.1.16 - ms@2.1.3: {} music-metadata@11.13.0: dependencies: - "@borewit/text-codec": 0.2.2 - "@tokenizer/token": 0.3.0 + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 content-type: 2.0.0 debug: 4.4.3 file-type: 21.3.4 @@ -10546,7 +6198,7 @@ snapshots: mysql2@3.22.5(@types/node@22.20.0): dependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 @@ -10558,7 +6210,7 @@ snapshots: mysql2@3.22.6(@types/node@22.20.0): dependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 @@ -10574,28 +6226,26 @@ snapshots: nanoid@3.3.15: {} - natural-compare@1.4.0: {} - negotiator@1.0.0: {} - next-auth@5.0.0-beta.31(next@16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(nodemailer@9.0.3)(react@19.2.7): + next-auth@5.0.0-beta.31(next@16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(nodemailer@9.0.3)(react@19.2.7): dependencies: - "@auth/core": 0.41.2(nodemailer@9.0.3) - next: 16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@auth/core': 0.41.2(nodemailer@9.0.3) + next: 16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 optionalDependencies: nodemailer: 9.0.3 next-intl-swc-plugin-extractor@4.13.1: {} - next-intl@4.13.1(next@16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(typescript@5.9.3): + next-intl@4.13.1(next@16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(typescript@5.9.3): dependencies: - "@formatjs/intl-localematcher": 0.8.10 - "@parcel/watcher": 2.5.6 - "@swc/core": 1.15.43 + '@formatjs/intl-localematcher': 0.8.10 + '@parcel/watcher': 2.5.6 + '@swc/core': 1.15.43 icu-minify: 4.13.1 negotiator: 1.0.0 - next: 16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-intl-swc-plugin-extractor: 4.13.1 po-parser: 2.1.1 react: 19.2.7 @@ -10603,30 +6253,30 @@ snapshots: optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - - "@swc/helpers" + - '@swc/helpers' - next@16.2.10(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + next@16.2.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - "@next/env": 16.2.10 - "@swc/helpers": 0.5.15 + '@next/env': 16.2.10 + '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.40 caniuse-lite: 1.0.30001799 postcss: 8.5.16 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) - styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.7) + styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: - "@next/swc-darwin-arm64": 16.2.10 - "@next/swc-darwin-x64": 16.2.10 - "@next/swc-linux-arm64-gnu": 16.2.10 - "@next/swc-linux-arm64-musl": 16.2.10 - "@next/swc-linux-x64-gnu": 16.2.10 - "@next/swc-linux-x64-musl": 16.2.10 - "@next/swc-win32-arm64-msvc": 16.2.10 - "@next/swc-win32-x64-msvc": 16.2.10 + '@next/swc-darwin-arm64': 16.2.10 + '@next/swc-darwin-x64': 16.2.10 + '@next/swc-linux-arm64-gnu': 16.2.10 + '@next/swc-linux-arm64-musl': 16.2.10 + '@next/swc-linux-x64-gnu': 16.2.10 + '@next/swc-linux-x64-musl': 16.2.10 + '@next/swc-win32-arm64-msvc': 16.2.10 + '@next/swc-win32-x64-msvc': 16.2.10 sharp: 0.34.5 transitivePeerDependencies: - - "@babel/core" + - '@babel/core' - babel-plugin-macros node-addon-api@7.1.1: {} @@ -10635,76 +6285,22 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-releases@2.0.51: {} - nodemailer@9.0.3: {} oauth4webapi@3.8.6: {} - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.2 - has-symbols: 1.1.0 - object-keys: 1.1.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.2 - - 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.2 - ohash@2.0.11: {} - 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 - otplib@12.0.1: dependencies: - "@otplib/core": 12.0.1 - "@otplib/preset-default": 12.0.1 - "@otplib/preset-v11": 12.0.1 - - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 + '@otplib/core': 12.0.1 + '@otplib/preset-default': 12.0.1 + '@otplib/preset-v11': 12.0.1 pako@1.0.11: {} parse-srcset@1.0.2: {} - path-exists@4.0.0: {} - path-key@3.1.1: {} pathe@1.1.2: {} @@ -10717,8 +6313,6 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.4: {} pkg-types@2.3.1: @@ -10729,8 +6323,6 @@ snapshots: po-parser@2.1.1: {} - possible-typed-array-names@1.1.0: {} - postal-mime@2.7.4: {} postcss-selector-parser@6.0.10: @@ -10758,23 +6350,19 @@ snapshots: preact@10.24.3: {} - prelude-ls@1.2.1: {} - - prettier@3.9.5: {} - prisma@7.8.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@5.9.3): dependencies: - "@prisma/config": 7.8.0 - "@prisma/dev": 0.24.3(typescript@5.9.3) - "@prisma/engines": 7.8.0 - "@prisma/studio-core": 0.27.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@prisma/config': 7.8.0 + '@prisma/dev': 0.24.3(typescript@5.9.3) + '@prisma/engines': 7.8.0 + '@prisma/studio-core': 0.27.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) mysql2: 3.15.3 postgres: 3.4.7 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - - "@types/react" - - "@types/react-dom" + - '@types/react' + - '@types/react-dom' - magicast - react - react-dom @@ -10787,74 +6375,70 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - punycode@2.3.1: {} - pure-rand@6.1.0: {} - queue-microtask@1.2.3: {} - radix-ui@1.6.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - "@radix-ui/primitive": 1.1.5 - "@radix-ui/react-accessible-icon": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-accordion": 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-alert-dialog": 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-arrow": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-aspect-ratio": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-avatar": 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-checkbox": 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-collapsible": 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-collection": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-compose-refs": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context": 1.2.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-context-menu": 2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-dialog": 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-direction": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-dismissable-layer": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-dropdown-menu": 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-focus-guards": 1.1.4(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-focus-scope": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-form": 0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-hover-card": 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-label": 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-menu": 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-menubar": 1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-navigation-menu": 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-one-time-password-field": 0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-password-toggle-field": 0.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-popover": 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-popper": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-portal": 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-presence": 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-primitive": 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-progress": 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-radio-group": 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-roving-focus": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-scroll-area": 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-select": 2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-separator": 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slider": 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-slot": 1.3.0(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-switch": 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-tabs": 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-toast": 1.2.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-toggle": 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-toggle-group": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-toolbar": 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-tooltip": 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - "@radix-ui/react-use-callback-ref": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-controllable-state": 1.2.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-effect-event": 0.0.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-escape-keydown": 1.1.3(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-is-hydrated": 0.1.1(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-layout-effect": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-use-size": 1.1.2(@types/react@19.2.17)(react@19.2.7) - "@radix-ui/react-visually-hidden": 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/primitive': 1.1.5 + '@radix-ui/react-accessible-icon': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-accordion': 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-alert-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-aspect-ratio': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-avatar': 1.2.2(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-checkbox': 1.3.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collapsible': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.2.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context-menu': 2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-form': 0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-hover-card': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-label': 2.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menu': 2.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-menubar': 1.1.20(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-navigation-menu': 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-one-time-password-field': 0.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-password-toggle-field': 0.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popover': 1.1.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-progress': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-radio-group': 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-scroll-area': 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-select': 2.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-separator': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slider': 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-switch': 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toast': 1.2.19(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle-group': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toolbar': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: 19.2.7 react-dom: 19.2.7(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 - "@types/react-dom": 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) rc9@3.0.1: dependencies: @@ -10876,7 +6460,7 @@ snapshots: react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): dependencies: @@ -10887,7 +6471,7 @@ snapshots: use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): dependencies: @@ -10895,7 +6479,7 @@ snapshots: react: 19.2.7 tslib: 2.8.1 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 react@19.2.7: {} @@ -10917,28 +6501,6 @@ snapshots: dependencies: redis-errors: 1.2.0 - 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.2 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - - regexp-tree@0.1.27: {} - - 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 - remeda@2.33.4: {} require-from-string@2.0.2: {} @@ -10952,68 +6514,39 @@ snapshots: retry@0.12.0: {} - reusify@1.1.0: {} - rollup@4.62.2: dependencies: - "@types/estree": 1.0.9 + '@types/estree': 1.0.9 optionalDependencies: - "@rollup/rollup-android-arm-eabi": 4.62.2 - "@rollup/rollup-android-arm64": 4.62.2 - "@rollup/rollup-darwin-arm64": 4.62.2 - "@rollup/rollup-darwin-x64": 4.62.2 - "@rollup/rollup-freebsd-arm64": 4.62.2 - "@rollup/rollup-freebsd-x64": 4.62.2 - "@rollup/rollup-linux-arm-gnueabihf": 4.62.2 - "@rollup/rollup-linux-arm-musleabihf": 4.62.2 - "@rollup/rollup-linux-arm64-gnu": 4.62.2 - "@rollup/rollup-linux-arm64-musl": 4.62.2 - "@rollup/rollup-linux-loong64-gnu": 4.62.2 - "@rollup/rollup-linux-loong64-musl": 4.62.2 - "@rollup/rollup-linux-ppc64-gnu": 4.62.2 - "@rollup/rollup-linux-ppc64-musl": 4.62.2 - "@rollup/rollup-linux-riscv64-gnu": 4.62.2 - "@rollup/rollup-linux-riscv64-musl": 4.62.2 - "@rollup/rollup-linux-s390x-gnu": 4.62.2 - "@rollup/rollup-linux-x64-gnu": 4.62.2 - "@rollup/rollup-linux-x64-musl": 4.62.2 - "@rollup/rollup-openbsd-x64": 4.62.2 - "@rollup/rollup-openharmony-arm64": 4.62.2 - "@rollup/rollup-win32-arm64-msvc": 4.62.2 - "@rollup/rollup-win32-ia32-msvc": 4.62.2 - "@rollup/rollup-win32-x64-gnu": 4.62.2 - "@rollup/rollup-win32-x64-msvc": 4.62.2 + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-array-concat@1.1.4: - 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-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-regex@2.1.1: - dependencies: - regexp-tree: 0.1.27 - safer-buffer@2.1.2: {} sanitize-html@2.17.5: @@ -11028,68 +6561,44 @@ snapshots: scheduler@0.27.0: {} - semver@6.3.1: {} - semver@7.8.5: {} seq-queue@0.0.5: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.2 - setimmediate@1.0.5: {} setprototypeof@1.2.0: {} sharp@0.34.5: dependencies: - "@img/colour": 1.1.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.8.5 optionalDependencies: - "@img/sharp-darwin-arm64": 0.34.5 - "@img/sharp-darwin-x64": 0.34.5 - "@img/sharp-libvips-darwin-arm64": 1.2.4 - "@img/sharp-libvips-darwin-x64": 1.2.4 - "@img/sharp-libvips-linux-arm": 1.2.4 - "@img/sharp-libvips-linux-arm64": 1.2.4 - "@img/sharp-libvips-linux-ppc64": 1.2.4 - "@img/sharp-libvips-linux-riscv64": 1.2.4 - "@img/sharp-libvips-linux-s390x": 1.2.4 - "@img/sharp-libvips-linux-x64": 1.2.4 - "@img/sharp-libvips-linuxmusl-arm64": 1.2.4 - "@img/sharp-libvips-linuxmusl-x64": 1.2.4 - "@img/sharp-linux-arm": 0.34.5 - "@img/sharp-linux-arm64": 0.34.5 - "@img/sharp-linux-ppc64": 0.34.5 - "@img/sharp-linux-riscv64": 0.34.5 - "@img/sharp-linux-s390x": 0.34.5 - "@img/sharp-linux-x64": 0.34.5 - "@img/sharp-linuxmusl-arm64": 0.34.5 - "@img/sharp-linuxmusl-x64": 0.34.5 - "@img/sharp-wasm32": 0.34.5 - "@img/sharp-win32-arm64": 0.34.5 - "@img/sharp-win32-ia32": 0.34.5 - "@img/sharp-win32-x64": 0.34.5 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -11100,34 +6609,6 @@ snapshots: shell-quote@1.9.0: {} - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.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 - siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -11160,62 +6641,25 @@ snapshots: standardwebhooks@1.0.0: dependencies: - "@stablelib/base64": 1.0.1 + '@stablelib/base64': 1.0.1 fast-sha256: 1.3.0 statuses@2.0.2: {} std-env@3.10.0: {} - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - - string.prototype.includes@2.0.1: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-abstract: 1.24.2 - - string.prototype.trim@1.2.11: - 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.2 - has-property-descriptors: 1.0.2 - safe-regex-test: 1.1.0 - - string.prototype.trimend@1.0.10: - dependencies: - call-bind: 1.0.9 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.2 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.9 - define-properties: 1.2.1 - es-object-atoms: 1.1.2 - string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 strtok3@10.3.5: dependencies: - "@tokenizer/token": 0.3.0 + '@tokenizer/token': 0.3.0 - styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.7): + styled-jsx@5.1.6(react@19.2.7): dependencies: client-only: 0.0.1 react: 19.2.7 - optionalDependencies: - "@babel/core": 7.29.7 tailwind-merge@3.6.0: {} @@ -11229,35 +6673,22 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.17: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - tinypool@1.1.1: {} tinyrainbow@1.2.0: {} tinyspy@3.0.2: {} - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - toidentifier@1.0.1: {} token-types@6.1.2: dependencies: - "@borewit/text-codec": 0.2.2 - "@tokenizer/token": 0.3.0 + '@borewit/text-codec': 0.2.2 + '@tokenizer/token': 0.3.0 ieee754: 1.2.1 tr46@0.0.3: {} - ts-api-utils@2.5.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - tslib@2.8.1: {} tsx@4.22.5: @@ -11266,90 +6697,25 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - 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.8: - 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 - - typescript-eslint@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3): - dependencies: - "@typescript-eslint/eslint-plugin": 8.63.0(@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - "@typescript-eslint/parser": 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - "@typescript-eslint/typescript-estree": 8.63.0(typescript@5.9.3) - "@typescript-eslint/utils": 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@5.9.3) - eslint: 10.6.0(jiti@2.7.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - typescript@5.9.3: {} 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 - undici-types@6.21.0: {} undici-types@7.18.2: {} - update-browserslist-db@1.2.3(browserslist@4.28.5): - dependencies: - browserslist: 4.28.5 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): dependencies: react: 19.2.7 tslib: 2.8.1 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 use-intl@4.13.1(react@19.2.7): dependencies: - "@formatjs/fast-memoize": 3.1.6 - "@schummar/icu-type-parser": 1.21.5 + '@formatjs/fast-memoize': 3.1.6 + '@schummar/icu-type-parser': 1.21.5 icu-minify: 4.13.1 intl-messageformat: 11.2.9 react: 19.2.7 @@ -11360,7 +6726,7 @@ snapshots: react: 19.2.7 tslib: 2.8.1 optionalDependencies: - "@types/react": 19.2.17 + '@types/react': 19.2.17 util-deprecate@1.0.2: {} @@ -11376,7 +6742,7 @@ snapshots: pathe: 1.1.2 vite: 5.4.21(@types/node@22.20.0) transitivePeerDependencies: - - "@types/node" + - '@types/node' - less - lightningcss - sass @@ -11394,7 +6760,7 @@ snapshots: pathe: 1.1.2 vite: 5.4.21(@types/node@22.20.0)(lightningcss@1.32.0) transitivePeerDependencies: - - "@types/node" + - '@types/node' - less - lightningcss - sass @@ -11410,7 +6776,7 @@ snapshots: postcss: 8.5.15 rollup: 4.62.2 optionalDependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 fsevents: 2.3.3 vite@5.4.21(@types/node@22.20.0)(lightningcss@1.32.0): @@ -11419,19 +6785,19 @@ snapshots: postcss: 8.5.16 rollup: 4.62.2 optionalDependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 fsevents: 2.3.3 lightningcss: 1.32.0 vitest@2.1.9(@types/node@22.20.0): dependencies: - "@vitest/expect": 2.1.9 - "@vitest/mocker": 2.1.9(vite@5.4.21(@types/node@22.20.0)) - "@vitest/pretty-format": 2.1.9 - "@vitest/runner": 2.1.9 - "@vitest/snapshot": 2.1.9 - "@vitest/spy": 2.1.9 - "@vitest/utils": 2.1.9 + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.20.0)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 chai: 5.3.3 debug: 4.4.3 expect-type: 1.4.0 @@ -11446,7 +6812,7 @@ snapshots: vite-node: 2.1.9(@types/node@22.20.0) why-is-node-running: 2.3.0 optionalDependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 transitivePeerDependencies: - less - lightningcss @@ -11460,13 +6826,13 @@ snapshots: vitest@2.1.9(@types/node@22.20.0)(lightningcss@1.32.0): dependencies: - "@vitest/expect": 2.1.9 - "@vitest/mocker": 2.1.9(vite@5.4.21(@types/node@22.20.0)(lightningcss@1.32.0)) - "@vitest/pretty-format": 2.1.9 - "@vitest/runner": 2.1.9 - "@vitest/snapshot": 2.1.9 - "@vitest/spy": 2.1.9 - "@vitest/utils": 2.1.9 + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@22.20.0)(lightningcss@1.32.0)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 chai: 5.3.3 debug: 4.4.3 expect-type: 1.4.0 @@ -11481,7 +6847,7 @@ snapshots: vite-node: 2.1.9(@types/node@22.20.0)(lightningcss@1.32.0) why-is-node-running: 2.3.0 optionalDependencies: - "@types/node": 22.20.0 + '@types/node': 22.20.0 transitivePeerDependencies: - less - lightningcss @@ -11500,47 +6866,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.2.0 - 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.22 - - 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.22: - 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 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -11556,19 +6881,9 @@ snapshots: win-guid@0.2.1: {} - word-wrap@1.2.5: {} - - yallist@3.1.1: {} - - yocto-queue@0.1.0: {} - zeptomatch@2.1.0: dependencies: grammex: 3.1.12 graphmatch: 1.1.1 - zod-validation-error@4.0.2(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod@3.25.76: {} diff --git a/postcss.config.mjs b/postcss.config.mjs index c2ddf74822..017b34b9cb 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,5 +1,5 @@ export default { - plugins: { - "@tailwindcss/postcss": {}, - }, + plugins: { + "@tailwindcss/postcss": {}, + }, }; diff --git a/prisma.config.ts b/prisma.config.ts index 56b385e840..d3152ad78d 100644 --- a/prisma.config.ts +++ b/prisma.config.ts @@ -6,11 +6,11 @@ import { defineConfig, env } from "prisma/config"; // live with the Arcturus emulator. CMS-only schema changes go in // prisma/migrations/*.sql (idempotent) applied via `pnpm db:migrate`. export default defineConfig({ - schema: "prisma/schema.prisma", - migrations: { - path: "prisma/migrations", - }, - datasource: { - url: env("DATABASE_URL"), - }, + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + datasource: { + url: env("DATABASE_URL"), + }, }); diff --git a/public/api/openapi.json b/public/api/openapi.json index 0a0dd9f209..cfbb38e86b 100644 --- a/public/api/openapi.json +++ b/public/api/openapi.json @@ -1,112 +1,125 @@ { - "openapi": "3.1.0", - "info": { - "title": "AtomCMS-Next API", - "version": "1.0.0", - "description": "Public and administrative REST API for the AtomCMS-Next Habbo retro hotel CMS." - }, - "servers": [{ "url": "/api", "description": "Local API" }], - "security": [{ "bearerAuth": [], "sessionAuth": [] }], - "components": { - "securitySchemes": { - "bearerAuth": { - "type": "http", - "scheme": "bearer", - "description": "Laravel Sanctum-compatible Bearer token from /api/tokens" - }, - "sessionAuth": { - "type": "apiKey", - "in": "cookie", - "name": "next-auth.session-token", - "description": "NextAuth session cookie (auto-sent by browser)" - } - } - }, - "paths": { - "/health": { - "get": { - "summary": "Health check", - "responses": { "200": { "description": "OK" } } - } - }, - "/articles": { - "get": { - "summary": "List published articles", - "parameters": [ - { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 10 } }, - { "name": "offset", "in": "query", "schema": { "type": "integer", "default": 0 } } - ], - "responses": { - "200": { - "description": "Article list", - "content": { "application/json": { "schema": { "type": "object" } } } - } - } - } - }, - "/online": { - "get": { - "summary": "Currently online users count", - "responses": { - "200": { - "description": "Online count", - "content": { - "application/json": { - "schema": { "type": "object", "properties": { "count": { "type": "integer" } } } - } - } - } - } - } - }, - "/leaderboard": { - "get": { - "summary": "User leaderboard (credits, achievement score, etc.)", - "responses": { "200": { "description": "Leaderboard data" } } - } - }, - "/client/sso": { - "get": { - "summary": "Generate SSO ticket for the game client (requires auth)", - "security": [{ "sessionAuth": [] }], - "responses": { - "200": { - "description": "SSO ticket + hotel name + client URL", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "ticket": { "type": "string" }, - "hotelName": { "type": "string" }, - "clientUrl": { "type": "string" } - } - } - } - } - }, - "401": { "description": "Unauthorized" } - } - } - }, - "/me": { - "get": { - "summary": "Current user profile (requires auth)", - "security": [{ "sessionAuth": [] }], - "responses": { "200": { "description": "User profile" } } - } - }, - "/settings": { - "get": { - "summary": "Public site settings (non-sensitive keys only)", - "responses": { "200": { "description": "Settings object" } } - } - }, - "/shop/packages": { - "get": { - "summary": "Available shop packages", - "responses": { "200": { "description": "Package list" } } - } - } - } + "openapi": "3.1.0", + "info": { + "title": "AtomCMS-Next API", + "version": "1.0.0", + "description": "Public and administrative REST API for the AtomCMS-Next Habbo retro hotel CMS." + }, + "servers": [{ "url": "/api", "description": "Local API" }], + "security": [{ "bearerAuth": [], "sessionAuth": [] }], + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "description": "Laravel Sanctum-compatible Bearer token from /api/tokens" + }, + "sessionAuth": { + "type": "apiKey", + "in": "cookie", + "name": "next-auth.session-token", + "description": "NextAuth session cookie (auto-sent by browser)" + } + } + }, + "paths": { + "/health": { + "get": { + "summary": "Health check", + "responses": { "200": { "description": "OK" } } + } + }, + "/articles": { + "get": { + "summary": "List published articles", + "parameters": [ + { + "name": "limit", + "in": "query", + "schema": { "type": "integer", "default": 10 } + }, + { + "name": "offset", + "in": "query", + "schema": { "type": "integer", "default": 0 } + } + ], + "responses": { + "200": { + "description": "Article list", + "content": { + "application/json": { "schema": { "type": "object" } } + } + } + } + } + }, + "/online": { + "get": { + "summary": "Currently online users count", + "responses": { + "200": { + "description": "Online count", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "count": { "type": "integer" } } + } + } + } + } + } + } + }, + "/leaderboard": { + "get": { + "summary": "User leaderboard (credits, achievement score, etc.)", + "responses": { "200": { "description": "Leaderboard data" } } + } + }, + "/client/sso": { + "get": { + "summary": "Generate SSO ticket for the game client (requires auth)", + "security": [{ "sessionAuth": [] }], + "responses": { + "200": { + "description": "SSO ticket + hotel name + client URL", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "ticket": { "type": "string" }, + "hotelName": { "type": "string" }, + "clientUrl": { "type": "string" } + } + } + } + } + }, + "401": { "description": "Unauthorized" } + } + } + }, + "/me": { + "get": { + "summary": "Current user profile (requires auth)", + "security": [{ "sessionAuth": [] }], + "responses": { "200": { "description": "User profile" } } + } + }, + "/settings": { + "get": { + "summary": "Public site settings (non-sensitive keys only)", + "responses": { "200": { "description": "Settings object" } } + } + }, + "/shop/packages": { + "get": { + "summary": "Available shop packages", + "responses": { "200": { "description": "Package list" } } + } + } + } } diff --git a/public/scripts/theme-init.js b/public/scripts/theme-init.js index b89b736e44..12adc95f47 100644 --- a/public/scripts/theme-init.js +++ b/public/scripts/theme-init.js @@ -1,12 +1,14 @@ -(function () { - try { - var s = localStorage.getItem("theme"); - var dd = document.querySelector('meta[name="theme-default-dark"]'); - var defaultDark = dd ? dd.getAttribute("content") === "true" : false; - if (s === "dark" || (!s && defaultDark)) document.documentElement.classList.add("dark"); - var nc = localStorage.getItem("navbarColor"), - nt = localStorage.getItem("navbarTextColor"); - if (nc) document.documentElement.style.setProperty("--color-navbar", nc); - if (nt) document.documentElement.style.setProperty("--color-navbar-text", nt); - } catch (e) {} +(() => { + try { + var s = localStorage.getItem("theme"); + var dd = document.querySelector('meta[name="theme-default-dark"]'); + var defaultDark = dd ? dd.getAttribute("content") === "true" : false; + if (s === "dark" || (!s && defaultDark)) + document.documentElement.classList.add("dark"); + var nc = localStorage.getItem("navbarColor"), + nt = localStorage.getItem("navbarTextColor"); + if (nc) document.documentElement.style.setProperty("--color-navbar", nc); + if (nt) + document.documentElement.style.setProperty("--color-navbar-text", nt); + } catch (_e) {} })(); diff --git a/public/sw.js b/public/sw.js index 0d2e25c5f8..e9d05b590c 100644 --- a/public/sw.js +++ b/public/sw.js @@ -4,54 +4,61 @@ const CACHE = "atom-v1"; self.addEventListener("install", () => { - self.skipWaiting(); + self.skipWaiting(); }); self.addEventListener("activate", (event) => { - event.waitUntil( - caches - .keys() - .then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) - .then(() => self.clients.claim()), - ); + event.waitUntil( + caches + .keys() + .then((keys) => + Promise.all( + keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)), + ), + ) + .then(() => self.clients.claim()), + ); }); self.addEventListener("fetch", (event) => { - const req = event.request; - if (req.method !== "GET") return; - const url = new URL(req.url); - if (url.origin !== self.location.origin) return; + const req = event.request; + if (req.method !== "GET") return; + const url = new URL(req.url); + if (url.origin !== self.location.origin) return; - // Cache-first for immutable static assets. - if (url.pathname.startsWith("/assets/") || url.pathname.startsWith("/_next/static/")) { - event.respondWith( - caches.open(CACHE).then((cache) => - cache.match(req).then( - (hit) => - hit || - fetch(req).then((res) => { - if (res.ok) cache.put(req, res.clone()); - return res; - }), - ), - ), - ); - return; - } + // Cache-first for immutable static assets. + if ( + url.pathname.startsWith("/assets/") || + url.pathname.startsWith("/_next/static/") + ) { + event.respondWith( + caches.open(CACHE).then((cache) => + cache.match(req).then( + (hit) => + hit || + fetch(req).then((res) => { + if (res.ok) cache.put(req, res.clone()); + return res; + }), + ), + ), + ); + return; + } - // Network-first for page navigations; fall back to cache, then the shell. - if (req.mode === "navigate") { - event.respondWith( - fetch(req) - .then((res) => { - const copy = res.clone(); - caches - .open(CACHE) - .then((c) => c.put(req, copy)) - .catch(() => {}); - return res; - }) - .catch(() => caches.match(req).then((hit) => hit || caches.match("/"))), - ); - } + // Network-first for page navigations; fall back to cache, then the shell. + if (req.mode === "navigate") { + event.respondWith( + fetch(req) + .then((res) => { + const copy = res.clone(); + caches + .open(CACHE) + .then((c) => c.put(req, copy)) + .catch(() => {}); + return res; + }) + .catch(() => caches.match(req).then((hit) => hit || caches.match("/"))), + ); + } }); diff --git a/scripts/apply-migrations.ts b/scripts/apply-migrations.ts index f4849ea613..5027f60f3e 100644 --- a/scripts/apply-migrations.ts +++ b/scripts/apply-migrations.ts @@ -1,133 +1,141 @@ import "dotenv/config"; -import { readFileSync, readdirSync } from "node:fs"; -import { resolve, dirname } from "node:path"; +import { readdirSync, readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; -import { splitSqlStatements } from "./sql-statements"; import { mysqlConnectionUrl } from "./db-url"; +import { splitSqlStatements } from "./sql-statements"; const __dirname = dirname(fileURLToPath(import.meta.url)); const MIGRATIONS_DIR = resolve(__dirname, "../prisma/migrations"); const TRACKING_TABLE = "cms_migrations"; interface MigrationFile { - id: string; - name: string; - sql: string; + id: string; + name: string; + sql: string; } function getDbConfig(): { url: string; database: string } { - const url = process.env.DATABASE_URL; - if (!url) throw new Error("DATABASE_URL is required"); - const parsed = new URL(url); - const dbName = decodeURIComponent(parsed.pathname.replace(/^\//, "")) || "atomcms"; - return { url: mysqlConnectionUrl(url), database: dbName }; + const url = process.env.DATABASE_URL; + if (!url) throw new Error("DATABASE_URL is required"); + const parsed = new URL(url); + const dbName = + decodeURIComponent(parsed.pathname.replace(/^\//, "")) || "atomcms"; + return { url: mysqlConnectionUrl(url), database: dbName }; } async function ensureConnection(): Promise { - const { url } = getDbConfig(); - const mysql = await import("mysql2/promise"); - const conn = await mysql.createConnection(url); - try { - await conn.execute( - `CREATE TABLE IF NOT EXISTS \`${TRACKING_TABLE}\` ( + const { url } = getDbConfig(); + const mysql = await import("mysql2/promise"); + const conn = await mysql.createConnection(url); + try { + await conn.execute( + `CREATE TABLE IF NOT EXISTS \`${TRACKING_TABLE}\` ( id INT AUTO_INCREMENT PRIMARY KEY, migration VARCHAR(255) NOT NULL UNIQUE, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`, - ); - } finally { - await conn.end(); - } + ); + } finally { + await conn.end(); + } } async function getApplied(): Promise> { - const { url } = getDbConfig(); - const mysql = await import("mysql2/promise"); - const conn = await mysql.createConnection(url); - try { - const [rows] = await conn.execute(`SELECT migration FROM \`${TRACKING_TABLE}\` ORDER BY id`); - return new Set((rows as { migration: string }[]).map((r) => r.migration)); - } catch { - return new Set(); - } finally { - await conn.end(); - } + const { url } = getDbConfig(); + const mysql = await import("mysql2/promise"); + const conn = await mysql.createConnection(url); + try { + const [rows] = await conn.execute( + `SELECT migration FROM \`${TRACKING_TABLE}\` ORDER BY id`, + ); + return new Set((rows as { migration: string }[]).map((r) => r.migration)); + } catch { + return new Set(); + } finally { + await conn.end(); + } } function loadMigrations(): MigrationFile[] { - const entries = readdirSync(MIGRATIONS_DIR, { withFileTypes: true }); - const files = entries - .filter((e) => e.isFile() && e.name.endsWith(".sql")) - .sort((a, b) => a.name.localeCompare(b.name)); + const entries = readdirSync(MIGRATIONS_DIR, { withFileTypes: true }); + const files = entries + .filter((e) => e.isFile() && e.name.endsWith(".sql")) + .sort((a, b) => a.name.localeCompare(b.name)); - return files.map((f) => { - const id = f.name.replace(/\.sql$/, ""); - const sql = readFileSync(resolve(MIGRATIONS_DIR, f.name), "utf-8"); - return { id, name: f.name, sql }; - }); + return files.map((f) => { + const id = f.name.replace(/\.sql$/, ""); + const sql = readFileSync(resolve(MIGRATIONS_DIR, f.name), "utf-8"); + return { id, name: f.name, sql }; + }); } async function apply(migration: MigrationFile): Promise { - const { url } = getDbConfig(); - const mysql = await import("mysql2/promise"); - const conn = await mysql.createConnection(url); - try { - const statements = splitSqlStatements(migration.sql); + const { url } = getDbConfig(); + const mysql = await import("mysql2/promise"); + const conn = await mysql.createConnection(url); + try { + const statements = splitSqlStatements(migration.sql); - for (const stmt of statements) { - await conn.execute(stmt); - } + for (const stmt of statements) { + await conn.execute(stmt); + } - await conn.execute(`INSERT INTO \`${TRACKING_TABLE}\` (migration) VALUES (?)`, [migration.id]); - console.log(`[migrate] Applied: ${migration.name}`); - } finally { - await conn.end(); - } + await conn.execute( + `INSERT INTO \`${TRACKING_TABLE}\` (migration) VALUES (?)`, + [migration.id], + ); + console.log(`[migrate] Applied: ${migration.name}`); + } finally { + await conn.end(); + } } async function main() { - const flag = process.argv[2]; + const flag = process.argv[2]; - if (flag === "--status") { - await ensureConnection(); - const applied = await getApplied(); - const all = loadMigrations(); + if (flag === "--status") { + await ensureConnection(); + const applied = await getApplied(); + const all = loadMigrations(); - console.log("\nMigration status:\n"); - for (const m of all) { - const done = applied.has(m.id); - console.log(` ${done ? "✓" : " "} ${m.name}${done ? "" : " [PENDING]"}`); - } + console.log("\nMigration status:\n"); + for (const m of all) { + const done = applied.has(m.id); + console.log( + ` ${done ? "✓" : " "} ${m.name}${done ? "" : " [PENDING]"}`, + ); + } - const pending = all.filter((m) => !applied.has(m.id)); - const total = all.length; - const done = total - pending.length; - console.log(`\n${done}/${total} applied, ${pending.length} pending\n`); - return; - } + const pending = all.filter((m) => !applied.has(m.id)); + const total = all.length; + const done = total - pending.length; + console.log(`\n${done}/${total} applied, ${pending.length} pending\n`); + return; + } - await ensureConnection(); - const applied = await getApplied(); - const pending = loadMigrations().filter((m) => !applied.has(m.id)); + await ensureConnection(); + const applied = await getApplied(); + const pending = loadMigrations().filter((m) => !applied.has(m.id)); - if (pending.length === 0) { - console.log("[migrate] All migrations already applied."); - return; - } + if (pending.length === 0) { + console.log("[migrate] All migrations already applied."); + return; + } - console.log(`[migrate] Applying ${pending.length} migration(s)...\n`); - for (const m of pending) { - try { - await apply(m); - } catch (err) { - console.error(`[migrate] FAILED: ${m.name}`, err); - process.exit(1); - } - } - console.log("\n[migrate] Done."); + console.log(`[migrate] Applying ${pending.length} migration(s)...\n`); + for (const m of pending) { + try { + await apply(m); + } catch (err) { + console.error(`[migrate] FAILED: ${m.name}`, err); + process.exit(1); + } + } + console.log("\n[migrate] Done."); } main().catch((err) => { - console.error("[migrate] Fatal:", err); - process.exit(1); + console.error("[migrate] Fatal:", err); + process.exit(1); }); diff --git a/scripts/check-local-imports.ts b/scripts/check-local-imports.ts index 5597d7be2c..655356e416 100644 --- a/scripts/check-local-imports.ts +++ b/scripts/check-local-imports.ts @@ -3,10 +3,11 @@ import { findMissingLocalImports } from "../src/lib/local-imports"; const missing = await findMissingLocalImports(process.cwd()); if (missing.length === 0) { - console.log("No unresolved local imports."); - process.exit(0); + console.log("No unresolved local imports."); + process.exit(0); } -for (const item of missing) console.error(`${item.importer}: ${item.specifier}`); +for (const item of missing) + console.error(`${item.importer}: ${item.specifier}`); console.error(`${missing.length} unresolved local import(s).`); process.exitCode = 1; diff --git a/scripts/db-url.test.ts b/scripts/db-url.test.ts index 92e4a64afe..dc69f4e337 100644 --- a/scripts/db-url.test.ts +++ b/scripts/db-url.test.ts @@ -2,12 +2,12 @@ import { describe, expect, it } from "vitest"; import { mysqlConnectionUrl } from "./db-url"; describe("mysqlConnectionUrl", () => { - it("removes Prisma-only pool options before passing the URL to MySQL2", () => { - const result = mysqlConnectionUrl( - "mysql://user:pass@localhost:3306/cms?connection_limit=20&pool_timeout=30&charset=utf8mb4", - ); - expect(result).not.toContain("connection_limit"); - expect(result).not.toContain("pool_timeout"); - expect(result).toContain("charset=utf8mb4"); - }); + it("removes Prisma-only pool options before passing the URL to MySQL2", () => { + const result = mysqlConnectionUrl( + "mysql://user:pass@localhost:3306/cms?connection_limit=20&pool_timeout=30&charset=utf8mb4", + ); + expect(result).not.toContain("connection_limit"); + expect(result).not.toContain("pool_timeout"); + expect(result).toContain("charset=utf8mb4"); + }); }); diff --git a/scripts/db-url.ts b/scripts/db-url.ts index a33d59f5d2..faa791c61f 100644 --- a/scripts/db-url.ts +++ b/scripts/db-url.ts @@ -1,7 +1,11 @@ -const MYSQL2_UNSUPPORTED_OPTIONS = ["connection_limit", "pool_timeout"] as const; +const MYSQL2_UNSUPPORTED_OPTIONS = [ + "connection_limit", + "pool_timeout", +] as const; export function mysqlConnectionUrl(value: string): string { - const url = new URL(value); - for (const option of MYSQL2_UNSUPPORTED_OPTIONS) url.searchParams.delete(option); - return url.toString(); + const url = new URL(value); + for (const option of MYSQL2_UNSUPPORTED_OPTIONS) + url.searchParams.delete(option); + return url.toString(); } diff --git a/scripts/jobs-worker.ts b/scripts/jobs-worker.ts index 1bd80092b1..927008a1f8 100644 --- a/scripts/jobs-worker.ts +++ b/scripts/jobs-worker.ts @@ -3,82 +3,96 @@ import { env } from "../src/env"; import { prisma } from "../src/lib/prisma"; async function backupEmulatorJar(): Promise { - if (!env.EMULATOR_JAR_PATH || !env.EMULATOR_BACKUP_DIR) return; + if (!env.EMULATOR_JAR_PATH || !env.EMULATOR_BACKUP_DIR) return; - const { copyFileSync, mkdirSync, readdirSync, unlinkSync, existsSync } = await import("node:fs"); - const { resolve } = await import("node:path"); + const { copyFileSync, mkdirSync, readdirSync, unlinkSync, existsSync } = + await import("node:fs"); + const { resolve } = await import("node:path"); - const timestamp = new Date().toISOString().slice(0, 19).replace(/[T:]/g, "-"); - const backupFile = resolve(env.EMULATOR_BACKUP_DIR, `emulator-${timestamp}.jar`); + const timestamp = new Date().toISOString().slice(0, 19).replace(/[T:]/g, "-"); + const backupFile = resolve( + env.EMULATOR_BACKUP_DIR, + `emulator-${timestamp}.jar`, + ); - if (!existsSync(env.EMULATOR_BACKUP_DIR)) { - mkdirSync(env.EMULATOR_BACKUP_DIR, { recursive: true }); - } + if (!existsSync(env.EMULATOR_BACKUP_DIR)) { + mkdirSync(env.EMULATOR_BACKUP_DIR, { recursive: true }); + } - try { - copyFileSync(env.EMULATOR_JAR_PATH, backupFile); - console.log(`[jobs] Backed up emulator JAR to ${backupFile}`); + try { + copyFileSync(env.EMULATOR_JAR_PATH, backupFile); + console.log(`[jobs] Backed up emulator JAR to ${backupFile}`); - // Rotate: keep only the N newest - const keep = env.EMULATOR_BACKUP_KEEP ?? 7; - const files = readdirSync(env.EMULATOR_BACKUP_DIR) - .filter((f) => f.startsWith("emulator-") && f.endsWith(".jar")) - .sort() - .reverse(); + // Rotate: keep only the N newest + const keep = env.EMULATOR_BACKUP_KEEP ?? 7; + const files = readdirSync(env.EMULATOR_BACKUP_DIR) + .filter((f) => f.startsWith("emulator-") && f.endsWith(".jar")) + .sort() + .reverse(); - for (let i = keep; i < files.length; i++) { - unlinkSync(resolve(env.EMULATOR_BACKUP_DIR, files[i])); - console.log(`[jobs] Rotated out old backup: ${files[i]}`); - } - } catch (err) { - console.error("[jobs] JAR backup failed:", err); - } + for (let i = keep; i < files.length; i++) { + unlinkSync(resolve(env.EMULATOR_BACKUP_DIR, files[i])); + console.log(`[jobs] Rotated out old backup: ${files[i]}`); + } + } catch (err) { + console.error("[jobs] JAR backup failed:", err); + } } async function cleanupOldLogs(): Promise { - try { - const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); - await prisma.websiteLoginLogs.deleteMany({ where: { createdAt: { lt: cutoff } } }); - console.log("[jobs] Cleaned up login logs older than 30 days"); - } catch (err) { - console.error("[jobs] Log cleanup failed:", err); - } + try { + const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); + await prisma.websiteLoginLogs.deleteMany({ + where: { createdAt: { lt: cutoff } }, + }); + console.log("[jobs] Cleaned up login logs older than 30 days"); + } catch (err) { + console.error("[jobs] Log cleanup failed:", err); + } } async function cleanupOldSessions(): Promise { - try { - const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); - await prisma.passwordReset.deleteMany({ where: { createdAt: { lt: cutoff } } }); - console.log("[jobs] Cleaned up expired password reset tokens"); - } catch (err) { - console.error("[jobs] Session cleanup failed:", err); - } + try { + const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); + await prisma.passwordReset.deleteMany({ + where: { createdAt: { lt: cutoff } }, + }); + console.log("[jobs] Cleaned up expired password reset tokens"); + } catch (err) { + console.error("[jobs] Session cleanup failed:", err); + } } async function main() { - console.log("[jobs] Worker started"); + console.log("[jobs] Worker started"); - // JAR backup — daily at 03:00 - if (env.EMULATOR_JAR_PATH && env.EMULATOR_BACKUP_DIR) { - new Cron("0 3 * * *", () => { - backupEmulatorJar().catch((e) => console.error("[jobs] Backup error:", e)); - }); - console.log("[jobs] Scheduled: emulator JAR backup (daily 03:00)"); - } + // JAR backup — daily at 03:00 + if (env.EMULATOR_JAR_PATH && env.EMULATOR_BACKUP_DIR) { + new Cron("0 3 * * *", () => { + backupEmulatorJar().catch((e) => + console.error("[jobs] Backup error:", e), + ); + }); + console.log("[jobs] Scheduled: emulator JAR backup (daily 03:00)"); + } - // Log cleanup — daily at 04:00 - new Cron("0 4 * * *", () => { - Promise.all([cleanupOldLogs(), cleanupOldSessions()]).catch((e) => - console.error("[jobs] Cleanup error:", e), - ); - }); - console.log("[jobs] Scheduled: old data cleanup (daily 04:00)"); + // Log cleanup — daily at 04:00 + new Cron("0 4 * * *", () => { + Promise.all([cleanupOldLogs(), cleanupOldSessions()]).catch((e) => + console.error("[jobs] Cleanup error:", e), + ); + }); + console.log("[jobs] Scheduled: old data cleanup (daily 04:00)"); - // Run once on startup - await Promise.all([backupEmulatorJar(), cleanupOldLogs(), cleanupOldSessions()]); + // Run once on startup + await Promise.all([ + backupEmulatorJar(), + cleanupOldLogs(), + cleanupOldSessions(), + ]); } main().catch((err) => { - console.error("[jobs] Fatal:", err); - process.exit(1); + console.error("[jobs] Fatal:", err); + process.exit(1); }); diff --git a/scripts/migrate-aes-cbc-to-gcm.ts b/scripts/migrate-aes-cbc-to-gcm.ts index cb247e7277..f79d41651f 100644 --- a/scripts/migrate-aes-cbc-to-gcm.ts +++ b/scripts/migrate-aes-cbc-to-gcm.ts @@ -9,132 +9,142 @@ * npx tsx scripts/migrate-aes-cbc-to-gcm.ts */ import "dotenv/config"; -import { createCipheriv, createDecipheriv, createHmac, randomBytes, timingSafeEqual } from "node:crypto"; +import { + createCipheriv, + createDecipheriv, + createHmac, + randomBytes, + timingSafeEqual, +} from "node:crypto"; import { prisma } from "../src/lib/prisma"; function getKey(appKey: string): Buffer { - const raw = appKey.startsWith("base64:") - ? Buffer.from(appKey.slice("base64:".length), "base64") - : Buffer.from(appKey, "utf8"); - if (raw.length !== 32) { - throw new Error(`APP_KEY must decode to 32 bytes (got ${raw.length})`); - } - return raw; + const raw = appKey.startsWith("base64:") + ? Buffer.from(appKey.slice("base64:".length), "base64") + : Buffer.from(appKey, "utf8"); + if (raw.length !== 32) { + throw new Error(`APP_KEY must decode to 32 bytes (got ${raw.length})`); + } + return raw; } /** OLD: AES-256-CBC decrypt with HMAC-SHA256 verification. */ function decryptCbc(payload: string, key: Buffer, serialize = true): string { - const json = JSON.parse(Buffer.from(payload, "base64").toString("utf8")) as { - iv: string; - value: string; - mac: string; - }; - const expected = createHmac("sha256", key) - .update(json.iv + json.value) - .digest("hex"); - const a = Buffer.from(expected, "hex"); - const b = Buffer.from(json.mac, "hex"); - if (a.length !== b.length || !timingSafeEqual(a, b)) { - throw new Error("The MAC is invalid (CBC payload)."); - } - const iv = Buffer.from(json.iv, "base64"); - const decipher = createDecipheriv("aes-256-cbc", key, iv); - const plain = decipher.update(json.value, "base64", "utf8") + decipher.final("utf8"); - return serialize ? phpUnserializeString(plain) : plain; + const json = JSON.parse(Buffer.from(payload, "base64").toString("utf8")) as { + iv: string; + value: string; + mac: string; + }; + const expected = createHmac("sha256", key) + .update(json.iv + json.value) + .digest("hex"); + const a = Buffer.from(expected, "hex"); + const b = Buffer.from(json.mac, "hex"); + if (a.length !== b.length || !timingSafeEqual(a, b)) { + throw new Error("The MAC is invalid (CBC payload)."); + } + const iv = Buffer.from(json.iv, "base64"); + const decipher = createDecipheriv("aes-256-cbc", key, iv); + const plain = + decipher.update(json.value, "base64", "utf8") + decipher.final("utf8"); + return serialize ? phpUnserializeString(plain) : plain; } /** NEW: AES-256-GCM encrypt (mirrors current LaravelEncrypter). */ function encryptGcm(plaintext: string, key: Buffer, serialize = true): string { - const iv = randomBytes(12); - const data = serialize ? phpSerializeString(plaintext) : plaintext; - const cipher = createCipheriv("aes-256-gcm", key, iv); - const valueB64 = cipher.update(data, "utf8", "base64") + cipher.final("base64"); - const tag = cipher.getAuthTag(); - const ivB64 = iv.toString("base64"); - const tagB64 = tag.toString("base64"); - const payload = JSON.stringify({ iv: ivB64, value: valueB64, tag: tagB64 }); - return Buffer.from(payload, "utf8").toString("base64"); + const iv = randomBytes(12); + const data = serialize ? phpSerializeString(plaintext) : plaintext; + const cipher = createCipheriv("aes-256-gcm", key, iv); + const valueB64 = + cipher.update(data, "utf8", "base64") + cipher.final("base64"); + const tag = cipher.getAuthTag(); + const ivB64 = iv.toString("base64"); + const tagB64 = tag.toString("base64"); + const payload = JSON.stringify({ iv: ivB64, value: valueB64, tag: tagB64 }); + return Buffer.from(payload, "utf8").toString("base64"); } /** Tries to decrypt a payload with the NEW GCM logic; if it works, skip. */ -function isAlreadyGcm(payload: string, key: Buffer): boolean { - try { - const json = JSON.parse(Buffer.from(payload, "base64").toString("utf8")); - if (!json.tag && !json.mac) return false; // can't determine format - if (json.tag) return true; // has authTag => GCM - return false; // has mac => CBC - } catch { - return false; - } +function isAlreadyGcm(payload: string, _key: Buffer): boolean { + try { + const json = JSON.parse(Buffer.from(payload, "base64").toString("utf8")); + if (!json.tag && !json.mac) return false; // can't determine format + if (json.tag) return true; // has authTag => GCM + return false; // has mac => CBC + } catch { + return false; + } } async function main() { - const appKey = process.env.APP_KEY; - if (!appKey) { - console.error("APP_KEY environment variable is required."); - process.exit(1); - } - const key = getKey(appKey); + const appKey = process.env.APP_KEY; + if (!appKey) { + console.error("APP_KEY environment variable is required."); + process.exit(1); + } + const key = getKey(appKey); - const users = await prisma.user.findMany({ - where: { twoFactorSecret: { not: null } }, - select: { id: true, twoFactorSecret: true }, - }); + const users = await prisma.user.findMany({ + where: { twoFactorSecret: { not: null } }, + select: { id: true, twoFactorSecret: true }, + }); - console.log(`Found ${users.length} user(s) with a twoFactorSecret.`); + console.log(`Found ${users.length} user(s) with a twoFactorSecret.`); - let migrated = 0; - let skipped = 0; - let errors = 0; + let migrated = 0; + let skipped = 0; + let errors = 0; - for (const user of users) { - if (!user.twoFactorSecret) continue; + for (const user of users) { + if (!user.twoFactorSecret) continue; - if (isAlreadyGcm(user.twoFactorSecret, key)) { - console.log(` [SKIP] User ${user.id} — already GCM`); - skipped++; - continue; - } + if (isAlreadyGcm(user.twoFactorSecret, key)) { + console.log(` [SKIP] User ${user.id} — already GCM`); + skipped++; + continue; + } - try { - const plaintext = decryptCbc(user.twoFactorSecret, key); - const reEncrypted = encryptGcm(plaintext, key); - await prisma.user.update({ - where: { id: user.id }, - data: { twoFactorSecret: reEncrypted }, - }); - console.log(` [OK] User ${user.id} — migrated`); - migrated++; - } catch (err) { - console.error(` [FAIL] User ${user.id} — ${err}`); - errors++; - } - } + try { + const plaintext = decryptCbc(user.twoFactorSecret, key); + const reEncrypted = encryptGcm(plaintext, key); + await prisma.user.update({ + where: { id: user.id }, + data: { twoFactorSecret: reEncrypted }, + }); + console.log(` [OK] User ${user.id} — migrated`); + migrated++; + } catch (err) { + console.error(` [FAIL] User ${user.id} — ${err}`); + errors++; + } + } - console.log(`\nDone: ${migrated} migrated, ${skipped} skipped, ${errors} errors.`); - if (errors > 0) process.exit(1); + console.log( + `\nDone: ${migrated} migrated, ${skipped} skipped, ${errors} errors.`, + ); + if (errors > 0) process.exit(1); } main() - .catch((err) => { - console.error(err); - process.exit(1); - }) - .finally(() => prisma.$disconnect()); + .catch((err) => { + console.error(err); + process.exit(1); + }) + .finally(() => prisma.$disconnect()); /* ---- helpers (mirrored from laravel-encrypter.ts) ---- */ function phpSerializeString(value: string): string { - return `s:${Buffer.byteLength(value, "utf8")}:"${value}";`; + return `s:${Buffer.byteLength(value, "utf8")}:"${value}";`; } function phpUnserializeString(serialized: string): string { - const m = /^s:(\d+):"/.exec(serialized); - if (!m) throw new Error("Not a serialized PHP string"); - const byteLen = Number(m[1]); - const start = m[0].length; - const bytes = Buffer.from(serialized, "utf8").subarray( - Buffer.byteLength(serialized.slice(0, start), "utf8"), - ); - return bytes.subarray(0, byteLen).toString("utf8"); + const m = /^s:(\d+):"/.exec(serialized); + if (!m) throw new Error("Not a serialized PHP string"); + const byteLen = Number(m[1]); + const start = m[0].length; + const bytes = Buffer.from(serialized, "utf8").subarray( + Buffer.byteLength(serialized.slice(0, start), "utf8"), + ); + return bytes.subarray(0, byteLen).toString("utf8"); } diff --git a/scripts/migration-idempotency.test.ts b/scripts/migration-idempotency.test.ts index 452a761845..5102f2a001 100644 --- a/scripts/migration-idempotency.test.ts +++ b/scripts/migration-idempotency.test.ts @@ -3,10 +3,13 @@ import { resolve } from "node:path"; import { describe, expect, it } from "vitest"; describe("radio columns migration", () => { - it("adds every column idempotently for partially migrated databases", () => { - const sql = readFileSync(resolve("prisma/migrations/0009_radio_contests_giveaways_columns.sql"), "utf8"); - const additions = sql.match(/ADD COLUMN(?! IF NOT EXISTS)/gi) ?? []; - expect(additions).toEqual([]); - expect(sql).toContain("ADD COLUMN IF NOT EXISTS `title`"); - }); + it("adds every column idempotently for partially migrated databases", () => { + const sql = readFileSync( + resolve("prisma/migrations/0009_radio_contests_giveaways_columns.sql"), + "utf8", + ); + const additions = sql.match(/ADD COLUMN(?! IF NOT EXISTS)/gi) ?? []; + expect(additions).toEqual([]); + expect(sql).toContain("ADD COLUMN IF NOT EXISTS `title`"); + }); }); diff --git a/scripts/sql-statements.test.ts b/scripts/sql-statements.test.ts index a9e5543d65..6b2f32293e 100644 --- a/scripts/sql-statements.test.ts +++ b/scripts/sql-statements.test.ts @@ -2,17 +2,17 @@ import { describe, expect, it } from "vitest"; import { splitSqlStatements } from "./sql-statements"; describe("splitSqlStatements", () => { - it("ignores semicolons inside line comments", () => { - const sql = [ - "-- Existing installs have this; new installs need it.", - "ALTER TABLE users ADD COLUMN IF NOT EXISTS example TEXT NULL;", - "-- next statement", - "CREATE TABLE IF NOT EXISTS example_table (id INT PRIMARY KEY);", - ].join("\n"); + it("ignores semicolons inside line comments", () => { + const sql = [ + "-- Existing installs have this; new installs need it.", + "ALTER TABLE users ADD COLUMN IF NOT EXISTS example TEXT NULL;", + "-- next statement", + "CREATE TABLE IF NOT EXISTS example_table (id INT PRIMARY KEY);", + ].join("\n"); - expect(splitSqlStatements(sql)).toEqual([ - "ALTER TABLE users ADD COLUMN IF NOT EXISTS example TEXT NULL", - "CREATE TABLE IF NOT EXISTS example_table (id INT PRIMARY KEY)", - ]); - }); + expect(splitSqlStatements(sql)).toEqual([ + "ALTER TABLE users ADD COLUMN IF NOT EXISTS example TEXT NULL", + "CREATE TABLE IF NOT EXISTS example_table (id INT PRIMARY KEY)", + ]); + }); }); diff --git a/scripts/sql-statements.ts b/scripts/sql-statements.ts index 98ca199173..10a6143e0b 100644 --- a/scripts/sql-statements.ts +++ b/scripts/sql-statements.ts @@ -1,8 +1,10 @@ export function splitSqlStatements(sql: string): string[] { - const withoutComments = sql.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*--.*$/gm, ""); + const withoutComments = sql + .replace(/\/\*[\s\S]*?\*\//g, "") + .replace(/^\s*--.*$/gm, ""); - return withoutComments - .split(";") - .map((statement) => statement.trim()) - .filter(Boolean); + return withoutComments + .split(";") + .map((statement) => statement.trim()) + .filter(Boolean); } diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 0ca54c0c57..657cf37bfd 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -1,9 +1,9 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "module": "esnext", - "moduleResolution": "bundler", - "noEmit": true - }, - "include": ["./**/*.ts"] + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler", + "noEmit": true + }, + "include": ["./**/*.ts"] } diff --git a/src/actions/admin-ads.ts b/src/actions/admin-ads.ts index 952f25cd5c..0e380313fc 100644 --- a/src/actions/admin-ads.ts +++ b/src/actions/admin-ads.ts @@ -3,88 +3,88 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; +import { formPositiveBigInt } from "@/lib/form-data"; import { prisma } from "@/lib/prisma"; import { logStaffActivity } from "@/lib/services/staff-activity"; -import { formPositiveBigInt } from "@/lib/form-data"; // CRUD for website advertisements (website_ads). Emulator does not own this // table; it only stores an image URL rendered in the site layout/widgets. export async function createAd(formData: FormData): Promise { - const staff = await requireStaff(); - const image = String(formData.get("image") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!image) return; + const staff = await requireStaff(); + const image = String(formData.get("image") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!image) return; - const now = new Date(); - try { - const ad = await prisma.websiteAds.create({ - data: { image, createdAt: now, updatedAt: now }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "ad_create", - description: `Created advertisement #${ad.id} (${image})`, - targetType: "website_ad", - targetId: Number(ad.id), - }); - } catch { - // DB error — page re-renders unchanged. - revalidatePath("/admin/ads"); - return; - } - redirect("/admin/ads"); + const now = new Date(); + try { + const ad = await prisma.websiteAds.create({ + data: { image, createdAt: now, updatedAt: now }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "ad_create", + description: `Created advertisement #${ad.id} (${image})`, + targetType: "website_ad", + targetId: Number(ad.id), + }); + } catch { + // DB error — page re-renders unchanged. + revalidatePath("/admin/ads"); + return; + } + redirect("/admin/ads"); } export async function updateAd(formData: FormData): Promise { - const staff = await requireStaff(); - const raw = String(formData.get("id") ?? "").normalize("NFC"); - if (!/^\d+$/.test(raw)) return; - const id = BigInt(raw); - const image = String(formData.get("image") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!image) return; + const staff = await requireStaff(); + const raw = String(formData.get("id") ?? "").normalize("NFC"); + if (!/^\d+$/.test(raw)) return; + const id = BigInt(raw); + const image = String(formData.get("image") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!image) return; - try { - await prisma.websiteAds.update({ - where: { id }, - data: { image, updatedAt: new Date() }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "ad_update", - description: `Updated advertisement #${id} (${image})`, - targetType: "website_ad", - targetId: Number(id), - }); - } catch { - // Not found or DB error — ignore. - revalidatePath(`/admin/ads/${id}`); - return; - } - redirect("/admin/ads"); + try { + await prisma.websiteAds.update({ + where: { id }, + data: { image, updatedAt: new Date() }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "ad_update", + description: `Updated advertisement #${id} (${image})`, + targetType: "website_ad", + targetId: Number(id), + }); + } catch { + // Not found or DB error — ignore. + revalidatePath(`/admin/ads/${id}`); + return; + } + redirect("/admin/ads"); } export async function deleteAd(formData: FormData): Promise { - const staff = await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const staff = await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - await prisma.websiteAds.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "ad_delete", - description: `Deleted advertisement #${id}`, - targetType: "website_ad", - targetId: Number(id), - }); - } catch { - // Not found or DB error — ignore. - } - redirect("/admin/ads"); + try { + await prisma.websiteAds.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "ad_delete", + description: `Deleted advertisement #${id}`, + targetType: "website_ad", + targetId: Number(id), + }); + } catch { + // Not found or DB error — ignore. + } + redirect("/admin/ads"); } diff --git a/src/actions/admin-alerts.ts b/src/actions/admin-alerts.ts index 8abe845391..787e3e40bd 100644 --- a/src/actions/admin-alerts.ts +++ b/src/actions/admin-alerts.ts @@ -11,20 +11,20 @@ import { rcon } from "@/lib/services/rcon"; * `message` payload. Staff-gated; the message is trimmed/bounded before send. */ export async function sendHotelAlert(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const message = String(formData.get("message") ?? "") - .normalize("NFC") - .trim() - .slice(0, 1000); - if (!message) return; + const message = String(formData.get("message") ?? "") + .normalize("NFC") + .trim() + .slice(0, 1000); + if (!message) return; - try { - await rcon.send("hotelalert", { message }); - } catch { - // Best-effort delivery (dead socket / emulator offline) — never 500 the - // admin page. The emulator writes its own alert_logs row on receipt. - } + try { + await rcon.send("hotelalert", { message }); + } catch { + // Best-effort delivery (dead socket / emulator offline) — never 500 the + // admin page. The emulator writes its own alert_logs row on receipt. + } - revalidatePath("/admin/alerts"); + revalidatePath("/admin/alerts"); } diff --git a/src/actions/admin-applications.ts b/src/actions/admin-applications.ts index 0bd9a9cd1b..ad56089021 100644 --- a/src/actions/admin-applications.ts +++ b/src/actions/admin-applications.ts @@ -2,19 +2,19 @@ import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; -import { prisma } from "@/lib/prisma"; import { formPositiveBigInt } from "@/lib/form-data"; +import { prisma } from "@/lib/prisma"; export async function dismissApplication(formData: FormData): Promise { - await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - await prisma.websiteStaffApplications.delete({ where: { id } }); - } catch { - // already gone / no DB — nothing to do - } + try { + await prisma.websiteStaffApplications.delete({ where: { id } }); + } catch { + // already gone / no DB — nothing to do + } - revalidatePath("/admin/applications"); + revalidatePath("/admin/applications"); } diff --git a/src/actions/admin-articles.ts b/src/actions/admin-articles.ts index 04f1aec362..9db014e21c 100644 --- a/src/actions/admin-articles.ts +++ b/src/actions/admin-articles.ts @@ -2,101 +2,107 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; -import { slugify } from "@/lib/format"; import { requireStaff } from "@/lib/admin/guard"; +import { slugify } from "@/lib/format"; import { prisma } from "@/lib/prisma"; -import { formPositiveBigInt } from "@/lib/form-data"; async function uniqueSlug(title: string): Promise { - const base = slugify(title); - let slug = base; - let n = 2; - while (await prisma.websiteArticles.findUnique({ where: { slug }, select: { id: true } })) { - slug = `${base}-${n++}`; - } - return slug; + const base = slugify(title); + let slug = base; + let n = 2; + while ( + await prisma.websiteArticles.findUnique({ + where: { slug }, + select: { id: true }, + }) + ) { + slug = `${base}-${n++}`; + } + return slug; } export async function createArticle(formData: FormData): Promise { - const staff = await requireStaff(); - const title = String(formData.get("title") ?? "") - .normalize("NFC") - .trim(); - const shortStory = String(formData.get("shortStory") ?? "") - .normalize("NFC") - .trim(); - const fullStory = String(formData.get("fullStory") ?? "") - .normalize("NFC") - .trim(); - const image = String(formData.get("image") ?? "") - .normalize("NFC") - .trim(); - if (!title) return; + const staff = await requireStaff(); + const title = String(formData.get("title") ?? "") + .normalize("NFC") + .trim(); + const shortStory = String(formData.get("shortStory") ?? "") + .normalize("NFC") + .trim(); + const fullStory = String(formData.get("fullStory") ?? "") + .normalize("NFC") + .trim(); + const image = String(formData.get("image") ?? "") + .normalize("NFC") + .trim(); + if (!title) return; - try { - const now = new Date(); - await prisma.websiteArticles.create({ - data: { - slug: await uniqueSlug(title), - title: title.slice(0, 255), - shortStory: shortStory.slice(0, 255), - fullStory, - image: image.slice(0, 255), - userId: staff.id, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // Database error — re-render unchanged with error. - redirect("/admin/articles/new?error=Database error while creating article. Please try again."); - } - redirect("/admin/articles"); + try { + const now = new Date(); + await prisma.websiteArticles.create({ + data: { + slug: await uniqueSlug(title), + title: title.slice(0, 255), + shortStory: shortStory.slice(0, 255), + fullStory, + image: image.slice(0, 255), + userId: staff.id, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // Database error — re-render unchanged with error. + redirect( + "/admin/articles/new?error=Database error while creating article. Please try again.", + ); + } + redirect("/admin/articles"); } export async function updateArticle(formData: FormData): Promise { - await requireStaff(); - const id = BigInt(String(formData.get("id"))); - try { - await prisma.websiteArticles.update({ - where: { id }, - data: { - title: String(formData.get("title") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - shortStory: String(formData.get("shortStory") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - fullStory: String(formData.get("fullStory") ?? "") - .normalize("NFC") - .trim(), - image: String(formData.get("image") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - updatedAt: new Date(), - }, - }); - } catch { - redirect("/admin/articles?error=Update failed"); - } - revalidatePath(`/admin/articles/${id}`); - redirect("/admin/articles"); + await requireStaff(); + const id = BigInt(String(formData.get("id"))); + try { + await prisma.websiteArticles.update({ + where: { id }, + data: { + title: String(formData.get("title") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + shortStory: String(formData.get("shortStory") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + fullStory: String(formData.get("fullStory") ?? "") + .normalize("NFC") + .trim(), + image: String(formData.get("image") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + updatedAt: new Date(), + }, + }); + } catch { + redirect("/admin/articles?error=Update failed"); + } + revalidatePath(`/admin/articles/${id}`); + redirect("/admin/articles"); } export async function deleteArticle(formData: FormData): Promise { - await requireStaff(); - const id = BigInt(String(formData.get("id"))); - try { - await prisma.$transaction([ - prisma.websiteArticleReactions.deleteMany({ where: { articleId: id } }), - prisma.websiteArticleComments.deleteMany({ where: { articleId: id } }), - prisma.websiteArticles.delete({ where: { id } }), - ]); - } catch { - redirect("/admin/articles?error=Delete failed"); - } - redirect("/admin/articles"); + await requireStaff(); + const id = BigInt(String(formData.get("id"))); + try { + await prisma.$transaction([ + prisma.websiteArticleReactions.deleteMany({ where: { articleId: id } }), + prisma.websiteArticleComments.deleteMany({ where: { articleId: id } }), + prisma.websiteArticles.delete({ where: { id } }), + ]); + } catch { + redirect("/admin/articles?error=Delete failed"); + } + redirect("/admin/articles"); } diff --git a/src/actions/admin-badge-upload.ts b/src/actions/admin-badge-upload.ts index d96ef207c3..f8d48d0226 100644 --- a/src/actions/admin-badge-upload.ts +++ b/src/actions/admin-badge-upload.ts @@ -1,7 +1,7 @@ "use server"; -import path from "node:path"; import { writeFile } from "node:fs/promises"; +import path from "node:path"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; import { logStaffActivity } from "@/lib/services/staff-activity"; @@ -16,58 +16,58 @@ const MAX_BYTES = 1024 * 1024; // 1MB const ALLOWED_TYPES = new Set(["image/gif", "image/png"]); function back(param: string, value: string): never { - redirect(`/admin/badges?${param}=${encodeURIComponent(value)}`); + redirect(`/admin/badges?${param}=${encodeURIComponent(value)}`); } export async function uploadBadge(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const dir = process.env.BADGE_UPLOAD_DIR; - if (!dir) { - back("error", "Badge upload directory not configured"); - } + const dir = process.env.BADGE_UPLOAD_DIR; + if (!dir) { + back("error", "Badge upload directory not configured"); + } - const code = String(formData.get("code") ?? "") - .normalize("NFC") - .trim(); - if (!CODE_RE.test(code)) { - back("error", "Invalid badge code (use A-Z, 0-9, _ or -, max 64 chars)"); - } + const code = String(formData.get("code") ?? "") + .normalize("NFC") + .trim(); + if (!CODE_RE.test(code)) { + back("error", "Invalid badge code (use A-Z, 0-9, _ or -, max 64 chars)"); + } - const file = formData.get("file"); - if (!(file instanceof File)) { - back("error", "No file uploaded"); - } + const file = formData.get("file"); + if (!(file instanceof File)) { + back("error", "No file uploaded"); + } - if (file.size === 0) { - back("error", "Uploaded file is empty"); - } - if (file.size > MAX_BYTES) { - back("error", "File too large (max 1MB)"); - } - if (!ALLOWED_TYPES.has(file.type)) { - back("error", "File must be a GIF or PNG image"); - } + if (file.size === 0) { + back("error", "Uploaded file is empty"); + } + if (file.size > MAX_BYTES) { + back("error", "File too large (max 1MB)"); + } + if (!ALLOWED_TYPES.has(file.type)) { + back("error", "File must be a GIF or PNG image"); + } - try { - const buffer = Buffer.from(await file.arrayBuffer()); - const baseDir = path.resolve(dir); - const target = path.resolve(baseDir, `${code}.gif`); - if (!target.startsWith(baseDir + path.sep)) { - back("error", "Invalid path"); - } - // eslint-disable-next-line security/detect-non-literal-fs-filename - await writeFile(target, buffer); - } catch { - back("error", "Could not write the badge file to disk"); - } + try { + const buffer = Buffer.from(await file.arrayBuffer()); + const baseDir = path.resolve(dir); + const target = path.resolve(baseDir, `${code}.gif`); + if (!target.startsWith(baseDir + path.sep)) { + back("error", "Invalid path"); + } + // eslint-disable-next-line security/detect-non-literal-fs-filename + await writeFile(target, buffer); + } catch { + back("error", "Could not write the badge file to disk"); + } - await logStaffActivity({ - staffId: staff.id, - action: "badge_upload", - description: `Uploaded badge image "${code}.gif"`, - targetType: "badge", - }); + await logStaffActivity({ + staffId: staff.id, + action: "badge_upload", + description: `Uploaded badge image "${code}.gif"`, + targetType: "badge", + }); - redirect(`/admin/badges?uploaded=${encodeURIComponent(code)}`); + redirect(`/admin/badges?uploaded=${encodeURIComponent(code)}`); } diff --git a/src/actions/admin-badges.ts b/src/actions/admin-badges.ts index 45934fb61b..608d1b0e5a 100644 --- a/src/actions/admin-badges.ts +++ b/src/actions/admin-badges.ts @@ -6,39 +6,39 @@ import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; export async function giveBadge(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const userId = Number(formData.get("userId")); - const code = String(formData.get("code") ?? "") - .normalize("NFC") - .trim() - .slice(0, 32); - if (!(userId > 0) || code.length === 0) return; + const userId = Number(formData.get("userId")); + const code = String(formData.get("code") ?? "") + .normalize("NFC") + .trim() + .slice(0, 32); + if (!(userId > 0) || code.length === 0) return; - // Fire the emulator command so the badge appears live for online users. - await rcon.giveBadge(userId, code); + // Fire the emulator command so the badge appears live for online users. + await rcon.giveBadge(userId, code); - // Persist the badge directly so it survives a relog / offline grant. - // users_badges has no unique (user_id, badge_code) constraint, so guard - // against duplicates and compute the next free slot ourselves. - try { - const existing = await prisma.usersBadges.findFirst({ - where: { userId, badgeCode: code }, - select: { id: true }, - }); - if (!existing) { - const max = await prisma.usersBadges.aggregate({ - where: { userId }, - _max: { slotId: true }, - }); - const slotId = (max._max.slotId ?? 0) + 1; - await prisma.usersBadges.create({ - data: { userId, slotId, badgeCode: code }, - }); - } - } catch { - // Best-effort: the RCON grant already succeeded for online users. - } + // Persist the badge directly so it survives a relog / offline grant. + // users_badges has no unique (user_id, badge_code) constraint, so guard + // against duplicates and compute the next free slot ourselves. + try { + const existing = await prisma.usersBadges.findFirst({ + where: { userId, badgeCode: code }, + select: { id: true }, + }); + if (!existing) { + const max = await prisma.usersBadges.aggregate({ + where: { userId }, + _max: { slotId: true }, + }); + const slotId = (max._max.slotId ?? 0) + 1; + await prisma.usersBadges.create({ + data: { userId, slotId, badgeCode: code }, + }); + } + } catch { + // Best-effort: the RCON grant already succeeded for online users. + } - revalidatePath("/admin/badges"); + revalidatePath("/admin/badges"); } diff --git a/src/actions/admin-bans.ts b/src/actions/admin-bans.ts index dff8751e70..d1d637f507 100644 --- a/src/actions/admin-bans.ts +++ b/src/actions/admin-bans.ts @@ -1,71 +1,77 @@ "use server"; import { revalidatePath } from "next/cache"; +import type { $Enums } from "@/generated/prisma/client"; import { requireStaffRateLimited as requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; -import type { $Enums } from "@/generated/prisma/client"; type BanType = $Enums.bans_type; -const BAN_TYPES: ReadonlySet = new Set(["account", "ip", "machine", "super"]); +const BAN_TYPES: ReadonlySet = new Set([ + "account", + "ip", + "machine", + "super", +]); const PERMANENT_SECONDS = 100 * 365 * 24 * 3600; export async function createBan(formData: FormData): Promise { - const staff = await requireStaff(); - const userId = Number(formData.get("userId")); - const reason = - String(formData.get("reason") ?? "") - .normalize("NFC") - .trim() - .slice(0, 200) || "Banned"; - const hours = Number(formData.get("hours")); - const type = String(formData.get("type")); - if (!(userId > 0) || !BAN_TYPES.has(type)) return; + const staff = await requireStaff(); + const userId = Number(formData.get("userId")); + const reason = + String(formData.get("reason") ?? "") + .normalize("NFC") + .trim() + .slice(0, 200) || "Banned"; + const hours = Number(formData.get("hours")); + const type = String(formData.get("type")); + if (!(userId > 0) || !BAN_TYPES.has(type)) return; - const now = Math.floor(Date.now() / 1000); - const banExpire = hours > 0 ? now + Math.floor(hours) * 3600 : now + PERMANENT_SECONDS; + const now = Math.floor(Date.now() / 1000); + const banExpire = + hours > 0 ? now + Math.floor(hours) * 3600 : now + PERMANENT_SECONDS; - const user = await prisma.user.findUnique({ - where: { id: userId }, - select: { username: true }, - }); + const user = await prisma.user.findUnique({ + where: { id: userId }, + select: { username: true }, + }); - await prisma.ban.create({ - data: { - userId, - ip: "", - machineId: "", - userStaffId: staff.id, - timestamp: now, - banExpire, - banReason: reason, - type: type as BanType, - cfhTopic: -1, - }, - }); + await prisma.ban.create({ + data: { + userId, + ip: "", + machineId: "", + userStaffId: staff.id, + timestamp: now, + banExpire, + banReason: reason, + type: type as BanType, + cfhTopic: -1, + }, + }); - if (user) await rcon.disconnectUser(userId, user.username); - await logStaffActivity({ - staffId: staff.id, - action: "user_ban", - description: `Banned user #${userId} (${type}, ${hours > 0 ? `${hours}h` : "permanent"}): ${reason}`, - targetType: "user", - targetId: userId, - }); - revalidatePath("/admin/bans"); + if (user) await rcon.disconnectUser(userId, user.username); + await logStaffActivity({ + staffId: staff.id, + action: "user_ban", + description: `Banned user #${userId} (${type}, ${hours > 0 ? `${hours}h` : "permanent"}): ${reason}`, + targetType: "user", + targetId: userId, + }); + revalidatePath("/admin/bans"); } export async function liftBan(formData: FormData): Promise { - const staff = await requireStaff(); - const id = Number(formData.get("id")); - if (id > 0) { - await prisma.ban.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "ban_lift", - description: `Lifted ban #${id}`, - }); - } - revalidatePath("/admin/bans"); + const staff = await requireStaff(); + const id = Number(formData.get("id")); + if (id > 0) { + await prisma.ban.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "ban_lift", + description: `Lifted ban #${id}`, + }); + } + revalidatePath("/admin/bans"); } diff --git a/src/actions/admin-email-templates.ts b/src/actions/admin-email-templates.ts index 0a35dae6fa..d73521f754 100644 --- a/src/actions/admin-email-templates.ts +++ b/src/actions/admin-email-templates.ts @@ -2,75 +2,75 @@ import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; -import { prisma } from "@/lib/prisma"; import { formPositiveBigInt } from "@/lib/form-data"; +import { prisma } from "@/lib/prisma"; export async function createEmailTemplate(formData: FormData): Promise { - await requireStaff(); - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const subject = String(formData.get("subject") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const body = String(formData.get("body") ?? "").normalize("NFC"); - const variablesRaw = String(formData.get("variables") ?? "") - .normalize("NFC") - .trim(); - const isActive = formData.get("isActive") != null; - if (!name || !subject || !body) return; + await requireStaff(); + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const subject = String(formData.get("subject") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const body = String(formData.get("body") ?? "").normalize("NFC"); + const variablesRaw = String(formData.get("variables") ?? "") + .normalize("NFC") + .trim(); + const isActive = formData.get("isActive") != null; + if (!name || !subject || !body) return; - await prisma.emailTemplates.create({ - data: { - name, - subject, - body, - variables: variablesRaw || null, - isActive, - }, - }); - revalidatePath("/admin/email-templates"); + await prisma.emailTemplates.create({ + data: { + name, + subject, + body, + variables: variablesRaw || null, + isActive, + }, + }); + revalidatePath("/admin/email-templates"); } export async function updateEmailTemplate(formData: FormData): Promise { - await requireStaff(); - const raw = String(formData.get("id") ?? "").normalize("NFC"); - if (!raw) return; - let id: bigint; - try { - id = BigInt(raw); - } catch { - return; - } - const subject = String(formData.get("subject") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const body = String(formData.get("body") ?? "").normalize("NFC"); - const variablesRaw = String(formData.get("variables") ?? "") - .normalize("NFC") - .trim(); - const isActive = formData.get("isActive") != null; - if (!subject || !body) return; + await requireStaff(); + const raw = String(formData.get("id") ?? "").normalize("NFC"); + if (!raw) return; + let id: bigint; + try { + id = BigInt(raw); + } catch { + return; + } + const subject = String(formData.get("subject") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const body = String(formData.get("body") ?? "").normalize("NFC"); + const variablesRaw = String(formData.get("variables") ?? "") + .normalize("NFC") + .trim(); + const isActive = formData.get("isActive") != null; + if (!subject || !body) return; - await prisma.emailTemplates.update({ - where: { id }, - data: { - subject, - body, - variables: variablesRaw || null, - isActive, - }, - }); - revalidatePath("/admin/email-templates"); + await prisma.emailTemplates.update({ + where: { id }, + data: { + subject, + body, + variables: variablesRaw || null, + isActive, + }, + }); + revalidatePath("/admin/email-templates"); } export async function deleteEmailTemplate(formData: FormData): Promise { - await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; - await prisma.emailTemplates.delete({ where: { id } }); - revalidatePath("/admin/email-templates"); + await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; + await prisma.emailTemplates.delete({ where: { id } }); + revalidatePath("/admin/email-templates"); } diff --git a/src/actions/admin-emulator.ts b/src/actions/admin-emulator.ts index 391a82cccf..711f379900 100644 --- a/src/actions/admin-emulator.ts +++ b/src/actions/admin-emulator.ts @@ -10,37 +10,37 @@ import { prisma } from "@/lib/prisma"; // keys via upsert. We never migrate or drop them. export async function updateEmulatorSetting(formData: FormData): Promise { - await requireStaff(); - const key = String(formData.get("key") ?? "") - .normalize("NFC") - .trim() - .slice(0, 100); - const value = String(formData.get("value") ?? "") - .normalize("NFC") - .slice(0, 512); - if (!key) return; - await prisma.emulatorSettings.upsert({ - where: { key }, - update: { value }, - create: { key, value }, - }); - revalidatePath("/admin/emulator"); + await requireStaff(); + const key = String(formData.get("key") ?? "") + .normalize("NFC") + .trim() + .slice(0, 100); + const value = String(formData.get("value") ?? "") + .normalize("NFC") + .slice(0, 512); + if (!key) return; + await prisma.emulatorSettings.upsert({ + where: { key }, + update: { value }, + create: { key, value }, + }); + revalidatePath("/admin/emulator"); } export async function updateEmulatorText(formData: FormData): Promise { - await requireStaff(); - const key = String(formData.get("key") ?? "") - .normalize("NFC") - .trim() - .slice(0, 100); - const value = String(formData.get("value") ?? "") - .normalize("NFC") - .slice(0, 4096); - if (!key) return; - await prisma.emulatorTexts.upsert({ - where: { key }, - update: { value }, - create: { key, value }, - }); - revalidatePath("/admin/emulator"); + await requireStaff(); + const key = String(formData.get("key") ?? "") + .normalize("NFC") + .trim() + .slice(0, 100); + const value = String(formData.get("value") ?? "") + .normalize("NFC") + .slice(0, 4096); + if (!key) return; + await prisma.emulatorTexts.upsert({ + where: { key }, + update: { value }, + create: { key, value }, + }); + revalidatePath("/admin/emulator"); } diff --git a/src/actions/admin-help.ts b/src/actions/admin-help.ts index 6b4bdb5adf..1cf277a2c4 100644 --- a/src/actions/admin-help.ts +++ b/src/actions/admin-help.ts @@ -3,165 +3,167 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; +import { formPositiveBigInt } from "@/lib/form-data"; import { prisma } from "@/lib/prisma"; import { logStaffActivity } from "@/lib/services/staff-activity"; -import { formPositiveBigInt } from "@/lib/form-data"; // CRUD for help-center FAQ entries (website_help_center_categories). Each entry // is a titled content block with an optional image and call-to-action button. function parsePosition(value: FormDataEntryValue | null): number { - const n = Number(value); - return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1; + const n = Number(value); + return Number.isFinite(n) && n > 0 ? Math.floor(n) : 1; } export async function createHelpQuestion(formData: FormData): Promise { - const staff = await requireStaff(); - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const content = String(formData.get("content") ?? "") - .normalize("NFC") - .trim(); - if (!name || !content) return; + const staff = await requireStaff(); + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const content = String(formData.get("content") ?? "") + .normalize("NFC") + .trim(); + if (!name || !content) return; - const imageUrl = String(formData.get("imageUrl") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const buttonText = String(formData.get("buttonText") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const buttonUrl = String(formData.get("buttonUrl") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const buttonColor = - String(formData.get("buttonColor") ?? "") - .normalize("NFC") - .trim() - .slice(0, 16) || "#eeb425"; - const buttonBorderColor = - String(formData.get("buttonBorderColor") ?? "") - .normalize("NFC") - .trim() - .slice(0, 16) || "#facc15"; + const imageUrl = String(formData.get("imageUrl") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const buttonText = String(formData.get("buttonText") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const buttonUrl = String(formData.get("buttonUrl") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const buttonColor = + String(formData.get("buttonColor") ?? "") + .normalize("NFC") + .trim() + .slice(0, 16) || "#eeb425"; + const buttonBorderColor = + String(formData.get("buttonBorderColor") ?? "") + .normalize("NFC") + .trim() + .slice(0, 16) || "#facc15"; - try { - const entry = await prisma.websiteHelpCenterCategories.create({ - data: { - name, - content, - position: parsePosition(formData.get("position")), - imageUrl: imageUrl || null, - buttonText: buttonText || null, - buttonUrl: buttonUrl || null, - buttonColor, - buttonBorderColor, - smallBox: formData.get("smallBox") != null, - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "help_create", - description: `Created help-center entry #${entry.id} (${name})`, - targetType: "help_center_category", - targetId: Number(entry.id), - }); - } catch { - // Unique name collision or DB error — re-render unchanged with error. - revalidatePath("/admin/help-questions"); - redirect("/admin/help-questions/new?error=Unique name collision or database error. Please try again."); - } - revalidatePath("/admin/help-questions"); - redirect("/admin/help-questions"); + try { + const entry = await prisma.websiteHelpCenterCategories.create({ + data: { + name, + content, + position: parsePosition(formData.get("position")), + imageUrl: imageUrl || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + buttonColor, + buttonBorderColor, + smallBox: formData.get("smallBox") != null, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "help_create", + description: `Created help-center entry #${entry.id} (${name})`, + targetType: "help_center_category", + targetId: Number(entry.id), + }); + } catch { + // Unique name collision or DB error — re-render unchanged with error. + revalidatePath("/admin/help-questions"); + redirect( + "/admin/help-questions/new?error=Unique name collision or database error. Please try again.", + ); + } + revalidatePath("/admin/help-questions"); + redirect("/admin/help-questions"); } export async function updateHelpQuestion(formData: FormData): Promise { - const staff = await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const staff = await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const content = String(formData.get("content") ?? "") - .normalize("NFC") - .trim(); - if (!name || !content) return; + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const content = String(formData.get("content") ?? "") + .normalize("NFC") + .trim(); + if (!name || !content) return; - const imageUrl = String(formData.get("imageUrl") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const buttonText = String(formData.get("buttonText") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const buttonUrl = String(formData.get("buttonUrl") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const buttonColor = - String(formData.get("buttonColor") ?? "") - .normalize("NFC") - .trim() - .slice(0, 16) || "#eeb425"; - const buttonBorderColor = - String(formData.get("buttonBorderColor") ?? "") - .normalize("NFC") - .trim() - .slice(0, 16) || "#facc15"; + const imageUrl = String(formData.get("imageUrl") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const buttonText = String(formData.get("buttonText") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const buttonUrl = String(formData.get("buttonUrl") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const buttonColor = + String(formData.get("buttonColor") ?? "") + .normalize("NFC") + .trim() + .slice(0, 16) || "#eeb425"; + const buttonBorderColor = + String(formData.get("buttonBorderColor") ?? "") + .normalize("NFC") + .trim() + .slice(0, 16) || "#facc15"; - try { - await prisma.websiteHelpCenterCategories.update({ - where: { id }, - data: { - name, - content, - position: parsePosition(formData.get("position")), - imageUrl: imageUrl || null, - buttonText: buttonText || null, - buttonUrl: buttonUrl || null, - buttonColor, - buttonBorderColor, - smallBox: formData.get("smallBox") != null, - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "help_update", - description: `Updated help-center entry #${id} (${name})`, - targetType: "help_center_category", - targetId: Number(id), - }); - } catch { - // Not found, unique collision, or DB error — ignore. - revalidatePath(`/admin/help-questions/${id}`); - return; - } - redirect("/admin/help-questions"); + try { + await prisma.websiteHelpCenterCategories.update({ + where: { id }, + data: { + name, + content, + position: parsePosition(formData.get("position")), + imageUrl: imageUrl || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + buttonColor, + buttonBorderColor, + smallBox: formData.get("smallBox") != null, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "help_update", + description: `Updated help-center entry #${id} (${name})`, + targetType: "help_center_category", + targetId: Number(id), + }); + } catch { + // Not found, unique collision, or DB error — ignore. + revalidatePath(`/admin/help-questions/${id}`); + return; + } + redirect("/admin/help-questions"); } export async function deleteHelpQuestion(formData: FormData): Promise { - const staff = await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const staff = await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - await prisma.websiteHelpCenterCategories.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "help_delete", - description: `Deleted help-center entry #${id}`, - targetType: "help_center_category", - targetId: Number(id), - }); - } catch { - // Not found or DB error — ignore. - } - redirect("/admin/help-questions"); + try { + await prisma.websiteHelpCenterCategories.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "help_delete", + description: `Deleted help-center entry #${id}`, + targetType: "help_center_category", + targetId: Number(id), + }); + } catch { + // Not found or DB error — ignore. + } + redirect("/admin/help-questions"); } diff --git a/src/actions/admin-housekeeping.ts b/src/actions/admin-housekeeping.ts index 6b3c15fc30..3b3068cc1b 100644 --- a/src/actions/admin-housekeeping.ts +++ b/src/actions/admin-housekeeping.ts @@ -9,45 +9,47 @@ import { prisma } from "@/lib/prisma"; * string). Mirrors AtomCMS' housekeeping permission management. */ export async function upsertPermission(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const permission = String(formData.get("permission") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const minRank = Number(formData.get("minRank")); - const descriptionRaw = String(formData.get("description") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const description = descriptionRaw.length > 0 ? descriptionRaw : null; + const permission = String(formData.get("permission") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const minRank = Number(formData.get("minRank")); + const descriptionRaw = String(formData.get("description") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const description = descriptionRaw.length > 0 ? descriptionRaw : null; - if (!permission || !Number.isFinite(minRank) || minRank < 0) return; + if (!permission || !Number.isFinite(minRank) || minRank < 0) return; - try { - await prisma.websiteHousekeepingPermissions.upsert({ - where: { permission }, - update: { minRank, description }, - create: { permission, minRank, description }, - }); - } catch { - // Swallow: duplicate/constraint issues shouldn't crash the action. - } + try { + await prisma.websiteHousekeepingPermissions.upsert({ + where: { permission }, + update: { minRank, description }, + create: { permission, minRank, description }, + }); + } catch { + // Swallow: duplicate/constraint issues shouldn't crash the action. + } - revalidatePath("/admin/housekeeping"); + revalidatePath("/admin/housekeeping"); } export async function deletePermission(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const raw = String(formData.get("id") ?? "").normalize("NFC"); - if (!raw) return; + const raw = String(formData.get("id") ?? "").normalize("NFC"); + if (!raw) return; - try { - await prisma.websiteHousekeepingPermissions.delete({ where: { id: BigInt(raw) } }); - } catch { - // Already gone / invalid id. - } + try { + await prisma.websiteHousekeepingPermissions.delete({ + where: { id: BigInt(raw) }, + }); + } catch { + // Already gone / invalid id. + } - revalidatePath("/admin/housekeeping"); + revalidatePath("/admin/housekeeping"); } diff --git a/src/actions/admin-ip.ts b/src/actions/admin-ip.ts index f7cab4fba6..8900f54ad7 100644 --- a/src/actions/admin-ip.ts +++ b/src/actions/admin-ip.ts @@ -5,58 +5,58 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; function parseIp(formData: FormData): string { - return String(formData.get("ipAddress") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); + return String(formData.get("ipAddress") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); } function parseAsn(formData: FormData): string | null { - const asn = String(formData.get("asn") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - return asn || null; + const asn = String(formData.get("asn") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + return asn || null; } export async function addWhitelist(formData: FormData): Promise { - await requireStaff(); - const ipAddress = parseIp(formData); - if (!ipAddress) return; - const asn = parseAsn(formData); - await prisma.websiteIpWhitelist.create({ - data: { ipAddress, asn, whitelistAsn: asn != null }, - }); - revalidatePath("/admin/ip"); + await requireStaff(); + const ipAddress = parseIp(formData); + if (!ipAddress) return; + const asn = parseAsn(formData); + await prisma.websiteIpWhitelist.create({ + data: { ipAddress, asn, whitelistAsn: asn != null }, + }); + revalidatePath("/admin/ip"); } export async function deleteWhitelist(formData: FormData): Promise { - await requireStaff(); - const raw = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - if (!raw) return; - await prisma.websiteIpWhitelist.delete({ where: { id: BigInt(raw) } }); - revalidatePath("/admin/ip"); + await requireStaff(); + const raw = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + if (!raw) return; + await prisma.websiteIpWhitelist.delete({ where: { id: BigInt(raw) } }); + revalidatePath("/admin/ip"); } export async function addBlacklist(formData: FormData): Promise { - await requireStaff(); - const ipAddress = parseIp(formData); - if (!ipAddress) return; - const asn = parseAsn(formData); - await prisma.websiteIpBlacklist.create({ - data: { ipAddress, asn, blacklistAsn: asn != null }, - }); - revalidatePath("/admin/ip"); + await requireStaff(); + const ipAddress = parseIp(formData); + if (!ipAddress) return; + const asn = parseAsn(formData); + await prisma.websiteIpBlacklist.create({ + data: { ipAddress, asn, blacklistAsn: asn != null }, + }); + revalidatePath("/admin/ip"); } export async function deleteBlacklist(formData: FormData): Promise { - await requireStaff(); - const raw = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - if (!raw) return; - await prisma.websiteIpBlacklist.delete({ where: { id: BigInt(raw) } }); - revalidatePath("/admin/ip"); + await requireStaff(); + const raw = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + if (!raw) return; + await prisma.websiteIpBlacklist.delete({ where: { id: BigInt(raw) } }); + revalidatePath("/admin/ip"); } diff --git a/src/actions/admin-maintenance.ts b/src/actions/admin-maintenance.ts index eaea2c8e98..2c7d6d4bf8 100644 --- a/src/actions/admin-maintenance.ts +++ b/src/actions/admin-maintenance.ts @@ -18,41 +18,44 @@ const KEY_MESSAGE = "maintenance_message"; const KEY_MIN_RANK = "min_maintenance_login_rank"; const COMMENTS: Record = { - [KEY_ENABLED]: "Determines whether maintenance is enabled or not", - [KEY_MESSAGE]: "The maintenance message displayed to users while maintenance is activated", - [KEY_MIN_RANK]: "The minimum rank required to login to the hotel during maintenance", + [KEY_ENABLED]: "Determines whether maintenance is enabled or not", + [KEY_MESSAGE]: + "The maintenance message displayed to users while maintenance is activated", + [KEY_MIN_RANK]: + "The minimum rank required to login to the hotel during maintenance", }; async function upsertSetting(key: string, value: string): Promise { - await prisma.websiteSetting.upsert({ - where: { key }, - update: { value }, - // eslint-disable-next-line security/detect-object-injection -- key is one of 3 known const values - create: { key, value, comment: COMMENTS[key] ?? null }, - }); + await prisma.websiteSetting.upsert({ + where: { key }, + update: { value }, + // eslint-disable-next-line security/detect-object-injection -- key is one of 3 known const values + create: { key, value, comment: COMMENTS[key] ?? null }, + }); } export async function saveMaintenance(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - // Checkbox: present only when ticked. Normalise to the '1'/'0' string the - // emulator/Laravel side expects. - const enabled = formData.get("enabled") != null ? "1" : "0"; + // Checkbox: present only when ticked. Normalise to the '1'/'0' string the + // emulator/Laravel side expects. + const enabled = formData.get("enabled") != null ? "1" : "0"; - const message = String(formData.get("message") ?? "").normalize("NFC"); + const message = String(formData.get("message") ?? "").normalize("NFC"); - // Coerce the rank to a non-negative integer; fall back to AtomCMS's default - // of 5 when the field is blank or garbage. - const rawRank = String(formData.get("min_rank") ?? "") - .normalize("NFC") - .trim(); - const parsedRank = Number.parseInt(rawRank, 10); - const minRank = Number.isFinite(parsedRank) && parsedRank >= 0 ? parsedRank : 5; + // Coerce the rank to a non-negative integer; fall back to AtomCMS's default + // of 5 when the field is blank or garbage. + const rawRank = String(formData.get("min_rank") ?? "") + .normalize("NFC") + .trim(); + const parsedRank = Number.parseInt(rawRank, 10); + const minRank = + Number.isFinite(parsedRank) && parsedRank >= 0 ? parsedRank : 5; - await upsertSetting(KEY_ENABLED, enabled); - await upsertSetting(KEY_MESSAGE, message); - await upsertSetting(KEY_MIN_RANK, String(minRank)); + await upsertSetting(KEY_ENABLED, enabled); + await upsertSetting(KEY_MESSAGE, message); + await upsertSetting(KEY_MIN_RANK, String(minRank)); - siteSettings.reload(); - revalidatePath("/admin/maintenance"); + siteSettings.reload(); + revalidatePath("/admin/maintenance"); } diff --git a/src/actions/admin-media.ts b/src/actions/admin-media.ts index 7c84cc4c1d..6487af5688 100644 --- a/src/actions/admin-media.ts +++ b/src/actions/admin-media.ts @@ -1,8 +1,8 @@ "use server"; +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; import { revalidatePath } from "next/cache"; -import { writeFile, mkdir } from "fs/promises"; -import path from "path"; import { requireStaff } from "@/lib/admin/guard"; const MEDIA_DIR = "public/assets/images/media"; @@ -10,63 +10,65 @@ const MAX_SIZE = 5 * 1024 * 1024; // 5MB const ALLOWED = ["image/png", "image/jpeg", "image/gif", "image/webp"]; export async function uploadMedia(formData: FormData): Promise { - await requireStaff(); - const file = formData.get("file") as File | null; - if (!file || file.size === 0) return; - if (file.size > MAX_SIZE) return; - if (!ALLOWED.includes(file.type)) return; + await requireStaff(); + const file = formData.get("file") as File | null; + if (!file || file.size === 0) return; + if (file.size > MAX_SIZE) return; + if (!ALLOWED.includes(file.type)) return; - const baseDir = path.resolve(process.cwd(), MEDIA_DIR); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await mkdir(baseDir, { recursive: true }); + const baseDir = path.resolve(process.cwd(), MEDIA_DIR); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await mkdir(baseDir, { recursive: true }); - const ext = file.name.split(".").pop() ?? "png"; - const name = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; - const bytes = await file.arrayBuffer(); - const filePath = path.resolve(baseDir, name); - if (!filePath.startsWith(baseDir + path.sep)) throw new Error("Invalid path"); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await writeFile(filePath, Buffer.from(bytes)); + const ext = file.name.split(".").pop() ?? "png"; + const name = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; + const bytes = await file.arrayBuffer(); + const filePath = path.resolve(baseDir, name); + if (!filePath.startsWith(baseDir + path.sep)) throw new Error("Invalid path"); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await writeFile(filePath, Buffer.from(bytes)); - revalidatePath("/api/media"); - revalidatePath("/admin/media"); + revalidatePath("/api/media"); + revalidatePath("/admin/media"); } export async function deleteMedia(name: string): Promise { - await requireStaff(); - const { unlink } = await import("fs/promises"); - const baseDir = path.resolve(process.cwd(), MEDIA_DIR); - const filePath = path.resolve(baseDir, name); - if (!filePath.startsWith(baseDir + path.sep)) return; - try { - await unlink(filePath); - } catch { - // File may not exist - } - revalidatePath("/api/media"); - revalidatePath("/admin/media"); + await requireStaff(); + const { unlink } = await import("node:fs/promises"); + const baseDir = path.resolve(process.cwd(), MEDIA_DIR); + const filePath = path.resolve(baseDir, name); + if (!filePath.startsWith(baseDir + path.sep)) return; + try { + await unlink(filePath); + } catch { + // File may not exist + } + revalidatePath("/api/media"); + revalidatePath("/admin/media"); } -export async function uploadMediaAndReturn(formData: FormData): Promise { - await requireStaff(); - const file = formData.get("file") as File | null; - if (!file || file.size === 0) return ""; - if (file.size > MAX_SIZE) return ""; - if (!ALLOWED.includes(file.type)) return ""; +export async function uploadMediaAndReturn( + formData: FormData, +): Promise { + await requireStaff(); + const file = formData.get("file") as File | null; + if (!file || file.size === 0) return ""; + if (file.size > MAX_SIZE) return ""; + if (!ALLOWED.includes(file.type)) return ""; - const baseDir = path.resolve(process.cwd(), MEDIA_DIR); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await mkdir(baseDir, { recursive: true }); + const baseDir = path.resolve(process.cwd(), MEDIA_DIR); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await mkdir(baseDir, { recursive: true }); - const ext = file.name.split(".").pop() ?? "png"; - const name = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; - const bytes = await file.arrayBuffer(); - const filePath = path.resolve(baseDir, name); - if (!filePath.startsWith(baseDir + path.sep)) return ""; - // eslint-disable-next-line security/detect-non-literal-fs-filename - await writeFile(filePath, Buffer.from(bytes)); + const ext = file.name.split(".").pop() ?? "png"; + const name = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; + const bytes = await file.arrayBuffer(); + const filePath = path.resolve(baseDir, name); + if (!filePath.startsWith(baseDir + path.sep)) return ""; + // eslint-disable-next-line security/detect-non-literal-fs-filename + await writeFile(filePath, Buffer.from(bytes)); - revalidatePath("/api/media"); - revalidatePath("/admin/media"); - return `/api/media/${name}`; + revalidatePath("/api/media"); + revalidatePath("/admin/media"); + return `/api/media/${name}`; } diff --git a/src/actions/admin-permissions.ts b/src/actions/admin-permissions.ts index f82f11e1f1..1a716c72c3 100644 --- a/src/actions/admin-permissions.ts +++ b/src/actions/admin-permissions.ts @@ -3,10 +3,10 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; -import { prisma } from "@/lib/prisma"; -import { logStaffActivity } from "@/lib/services/staff-activity"; import { formPositiveBigInt } from "@/lib/form-data"; +import { prisma } from "@/lib/prisma"; import { logServerError } from "@/lib/server-log"; +import { logStaffActivity } from "@/lib/services/staff-activity"; // website_permissions (model WebsitePermissions) is the CMS-owned permission -> // minimum-rank mapping. Editable columns are exactly: permission (unique name), @@ -14,112 +14,121 @@ import { logServerError } from "@/lib/server-log"; // created_at/updated_at are managed here. function parseMinRank(formData: FormData): number { - const n = Number( - String(formData.get("minRank") ?? "") - .normalize("NFC") - .trim(), - ); - return Number.isInteger(n) && n >= 0 ? n : 1; + const n = Number( + String(formData.get("minRank") ?? "") + .normalize("NFC") + .trim(), + ); + return Number.isInteger(n) && n >= 0 ? n : 1; } export async function createPermission(formData: FormData): Promise { - const staff = await requireStaff(); - const permission = String(formData.get("permission") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const minRank = parseMinRank(formData); - const description = - String(formData.get("description") ?? "") - .normalize("NFC") - .trim() || null; - if (!permission) return; + const staff = await requireStaff(); + const permission = String(formData.get("permission") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const minRank = parseMinRank(formData); + const description = + String(formData.get("description") ?? "") + .normalize("NFC") + .trim() || null; + if (!permission) return; - const now = new Date(); - try { - await prisma.websitePermissions.upsert({ - where: { permission }, - update: { minRank, description, updatedAt: now }, - create: { permission, minRank, description, createdAt: now, updatedAt: now }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "permission_create", - description: `Saved permission "${permission}" (min rank ${minRank})`, - targetType: "permission", - }); - } catch (error) { - logServerError("admin.permission_create_failed", error, { staffId: staff.id, permission }); - redirect("/admin/permissions?error=save"); - // ignore (e.g. constraint failure) — page re-renders current state - } - revalidatePath("/admin/permissions"); - redirect("/admin/permissions?saved=1"); + const now = new Date(); + try { + await prisma.websitePermissions.upsert({ + where: { permission }, + update: { minRank, description, updatedAt: now }, + create: { + permission, + minRank, + description, + createdAt: now, + updatedAt: now, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "permission_create", + description: `Saved permission "${permission}" (min rank ${minRank})`, + targetType: "permission", + }); + } catch (error) { + logServerError("admin.permission_create_failed", error, { + staffId: staff.id, + permission, + }); + redirect("/admin/permissions?error=save"); + // ignore (e.g. constraint failure) — page re-renders current state + } + revalidatePath("/admin/permissions"); + redirect("/admin/permissions?saved=1"); } export async function updatePermission(formData: FormData): Promise { - const staff = await requireStaff(); - const raw = String(formData.get("id") ?? "").normalize("NFC"); - if (!raw) return; - const id = BigInt(raw); - const permission = String(formData.get("permission") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const minRank = parseMinRank(formData); - const description = - String(formData.get("description") ?? "") - .normalize("NFC") - .trim() || null; - if (!permission) return; + const staff = await requireStaff(); + const raw = String(formData.get("id") ?? "").normalize("NFC"); + if (!raw) return; + const id = BigInt(raw); + const permission = String(formData.get("permission") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const minRank = parseMinRank(formData); + const description = + String(formData.get("description") ?? "") + .normalize("NFC") + .trim() || null; + if (!permission) return; - try { - await prisma.websitePermissions.update({ - where: { id }, - data: { permission, minRank, description, updatedAt: new Date() }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "permission_update", - description: `Updated permission #${id} ("${permission}" min rank ${minRank})`, - targetType: "permission", - }); - } catch (error) { - logServerError("admin.permission_update_failed", error, { - staffId: staff.id, - permissionId: String(id), - }); - redirect("/admin/permissions?error=save"); - // ignore (e.g. duplicate) — page re-renders current state - } - revalidatePath("/admin/permissions"); - redirect("/admin/permissions?saved=1"); + try { + await prisma.websitePermissions.update({ + where: { id }, + data: { permission, minRank, description, updatedAt: new Date() }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "permission_update", + description: `Updated permission #${id} ("${permission}" min rank ${minRank})`, + targetType: "permission", + }); + } catch (error) { + logServerError("admin.permission_update_failed", error, { + staffId: staff.id, + permissionId: String(id), + }); + redirect("/admin/permissions?error=save"); + // ignore (e.g. duplicate) — page re-renders current state + } + revalidatePath("/admin/permissions"); + redirect("/admin/permissions?saved=1"); } export async function deletePermission(formData: FormData): Promise { - const staff = await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const staff = await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - const deleted = await prisma.websitePermissions.delete({ - where: { id }, - select: { permission: true }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "permission_delete", - description: `Deleted permission #${id} ("${deleted.permission}")`, - targetType: "permission", - }); - } catch (error) { - logServerError("admin.permission_delete_failed", error, { - staffId: staff.id, - permissionId: String(id), - }); - redirect("/admin/permissions?error=delete"); - // ignore (e.g. already removed) - } - revalidatePath("/admin/permissions"); - redirect("/admin/permissions?saved=1"); + try { + const deleted = await prisma.websitePermissions.delete({ + where: { id }, + select: { permission: true }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "permission_delete", + description: `Deleted permission #${id} ("${deleted.permission}")`, + targetType: "permission", + }); + } catch (error) { + logServerError("admin.permission_delete_failed", error, { + staffId: staff.id, + permissionId: String(id), + }); + redirect("/admin/permissions?error=delete"); + // ignore (e.g. already removed) + } + revalidatePath("/admin/permissions"); + redirect("/admin/permissions?saved=1"); } diff --git a/src/actions/admin-photos.ts b/src/actions/admin-photos.ts index 31735bbde2..ea6ba0e74c 100644 --- a/src/actions/admin-photos.ts +++ b/src/actions/admin-photos.ts @@ -5,15 +5,15 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; export async function deletePhoto(formData: FormData): Promise { - await requireStaff(); - const id = Number(formData.get("id")); - if (!(id > 0)) return; + await requireStaff(); + const id = Number(formData.get("id")); + if (!(id > 0)) return; - try { - await prisma.cameraWeb.delete({ where: { id } }); - } catch { - // Record may have already been removed; ignore. - } + try { + await prisma.cameraWeb.delete({ where: { id } }); + } catch { + // Record may have already been removed; ignore. + } - revalidatePath("/admin/photos"); + revalidatePath("/admin/photos"); } diff --git a/src/actions/admin-radio-api-keys.ts b/src/actions/admin-radio-api-keys.ts index 53b2e30863..5d8bbcc221 100644 --- a/src/actions/admin-radio-api-keys.ts +++ b/src/actions/admin-radio-api-keys.ts @@ -13,118 +13,119 @@ import { logStaffActivity } from "@/lib/services/staff-activity"; // `permissions` JSON column is intentionally left untouched by this CMS slice. function str(raw: FormDataEntryValue | null): string { - return typeof raw === "string" ? raw : ""; + return typeof raw === "string" ? raw : ""; } /** Parse a BigInt id from a form value, or null when blank/invalid. */ function parseId(raw: FormDataEntryValue | null): bigint | null { - const s = str(raw).trim(); - if (!s) return null; - try { - return BigInt(s); - } catch { - return null; - } + const s = str(raw).trim(); + if (!s) return null; + try { + return BigInt(s); + } catch { + return null; + } } /** Clamp a form value to a non-negative integer (defaulting to `fallback`). */ function intOr(raw: FormDataEntryValue | null, fallback: number): number { - const n = Number(str(raw).trim()); - if (!Number.isFinite(n) || n < 0) return fallback; - return Math.floor(n); + const n = Number(str(raw).trim()); + if (!Number.isFinite(n) || n < 0) return fallback; + return Math.floor(n); } export async function createApiKey(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const name = str(formData.get("name")).trim().slice(0, 255); - if (!name) return; + const name = str(formData.get("name")).trim().slice(0, 255); + if (!name) return; - const rateLimit = intOr(formData.get("rateLimit"), 300); - const allowedIps = str(formData.get("allowedIps")).trim().slice(0, 255) || null; + const rateLimit = intOr(formData.get("rateLimit"), 300); + const allowedIps = + str(formData.get("allowedIps")).trim().slice(0, 255) || null; - // Server-side key generation — 24 random bytes → 48 hex chars (fits VarChar(64)). - const key = randomBytes(24).toString("hex"); + // Server-side key generation — 24 random bytes → 48 hex chars (fits VarChar(64)). + const key = randomBytes(24).toString("hex"); - const now = new Date(); - try { - const created = await prisma.radioApiKeys.create({ - data: { - name, - key, - allowedIps, - rateLimit, - isActive: true, - createdAt: now, - updatedAt: now, - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "radio_api_key_create", - description: `Created radio API key "${name}" (#${created.id}, rate limit ${rateLimit})`, - targetType: "radio_api_key", - targetId: Number(created.id), - }); - } catch { - // Unique-key collision (astronomically unlikely) or DB down — fail soft. - return; - } + const now = new Date(); + try { + const created = await prisma.radioApiKeys.create({ + data: { + name, + key, + allowedIps, + rateLimit, + isActive: true, + createdAt: now, + updatedAt: now, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "radio_api_key_create", + description: `Created radio API key "${name}" (#${created.id}, rate limit ${rateLimit})`, + targetType: "radio_api_key", + targetId: Number(created.id), + }); + } catch { + // Unique-key collision (astronomically unlikely) or DB down — fail soft. + return; + } - revalidatePath("/admin/radio/api-keys"); - redirect("/admin/radio/api-keys?created=1"); + revalidatePath("/admin/radio/api-keys"); + redirect("/admin/radio/api-keys?created=1"); } export async function toggleApiKey(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id == null) return; + const id = parseId(formData.get("id")); + if (id == null) return; - try { - const existing = await prisma.radioApiKeys.findUnique({ - where: { id }, - select: { name: true, isActive: true }, - }); - if (!existing) return; + try { + const existing = await prisma.radioApiKeys.findUnique({ + where: { id }, + select: { name: true, isActive: true }, + }); + if (!existing) return; - const next = !existing.isActive; - await prisma.radioApiKeys.update({ - where: { id }, - data: { isActive: next, updatedAt: new Date() }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "radio_api_key_toggle", - description: `${next ? "Activated" : "Deactivated"} radio API key "${existing.name}" (#${id})`, - targetType: "radio_api_key", - targetId: Number(id), - }); - } catch { - return; - } + const next = !existing.isActive; + await prisma.radioApiKeys.update({ + where: { id }, + data: { isActive: next, updatedAt: new Date() }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "radio_api_key_toggle", + description: `${next ? "Activated" : "Deactivated"} radio API key "${existing.name}" (#${id})`, + targetType: "radio_api_key", + targetId: Number(id), + }); + } catch { + return; + } - revalidatePath("/admin/radio/api-keys"); + revalidatePath("/admin/radio/api-keys"); } export async function deleteApiKey(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id == null) return; + const id = parseId(formData.get("id")); + if (id == null) return; - try { - await prisma.radioApiKeys.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "radio_api_key_delete", - description: `Deleted radio API key #${id}`, - targetType: "radio_api_key", - targetId: Number(id), - }); - } catch { - return; - } + try { + await prisma.radioApiKeys.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "radio_api_key_delete", + description: `Deleted radio API key #${id}`, + targetType: "radio_api_key", + targetId: Number(id), + }); + } catch { + return; + } - revalidatePath("/admin/radio/api-keys"); + revalidatePath("/admin/radio/api-keys"); } diff --git a/src/actions/admin-radio-autodj.ts b/src/actions/admin-radio-autodj.ts index 6dc8a1c71f..fbc64b779a 100644 --- a/src/actions/admin-radio-autodj.ts +++ b/src/actions/admin-radio-autodj.ts @@ -13,125 +13,125 @@ import { logStaffActivity } from "@/lib/services/staff-activity"; /** Parse a FormData field into a positive BigInt id, or null when invalid. */ function parseId(raw: FormDataEntryValue | null): bigint | null { - if (typeof raw !== "string" || raw.trim() === "") return null; - try { - const id = BigInt(raw.trim()); - return id > 0n ? id : null; - } catch { - return null; - } + if (typeof raw !== "string" || raw.trim() === "") return null; + try { + const id = BigInt(raw.trim()); + return id > 0n ? id : null; + } catch { + return null; + } } function str(raw: FormDataEntryValue | null): string { - return typeof raw === "string" ? raw : ""; + return typeof raw === "string" ? raw : ""; } /** Checkbox/select truthiness: '1', 'true', 'on' → true. */ function bool(raw: FormDataEntryValue | null): boolean { - const v = str(raw).trim().toLowerCase(); - return v === "1" || v === "true" || v === "on"; + const v = str(raw).trim().toLowerCase(); + return v === "1" || v === "true" || v === "on"; } /** Parse a non-negative UnsignedInt, falling back to 0. */ function reqUInt(raw: FormDataEntryValue | null): number { - const n = Number(str(raw).trim()); - if (!Number.isFinite(n) || n < 0) return 0; - return Math.trunc(n); + const n = Number(str(raw).trim()); + if (!Number.isFinite(n) || n < 0) return 0; + return Math.trunc(n); } /** Parse an optional non-negative UnsignedInt; blank/invalid/negative → null. */ function optUInt(raw: FormDataEntryValue | null): number | null { - const s = str(raw).trim(); - if (s === "") return null; - const n = Number(s); - if (!Number.isFinite(n) || n < 0) return null; - return Math.trunc(n); + const s = str(raw).trim(); + if (s === "") return null; + const n = Number(s); + if (!Number.isFinite(n) || n < 0) return null; + return Math.trunc(n); } // ── AutoDJ playlist CRUD (radio_auto_dj_playlist) ──────────────────────── export async function createTrack(formData: FormData): Promise { - const staff = await requireStaff(); - const title = str(formData.get("title")).trim().slice(0, 255); - if (!title) return; + const staff = await requireStaff(); + const title = str(formData.get("title")).trim().slice(0, 255); + if (!title) return; - const artist = str(formData.get("artist")).trim().slice(0, 255); - const album = str(formData.get("album")).trim().slice(0, 255); - const artworkUrl = str(formData.get("artworkUrl")).trim().slice(0, 255); - const duration = optUInt(formData.get("duration")); - const sortOrder = reqUInt(formData.get("sortOrder")); - const isActive = bool(formData.get("isActive")); - const now = new Date(); + const artist = str(formData.get("artist")).trim().slice(0, 255); + const album = str(formData.get("album")).trim().slice(0, 255); + const artworkUrl = str(formData.get("artworkUrl")).trim().slice(0, 255); + const duration = optUInt(formData.get("duration")); + const sortOrder = reqUInt(formData.get("sortOrder")); + const isActive = bool(formData.get("isActive")); + const now = new Date(); - try { - const created = await prisma.radioAutoDjPlaylist.create({ - data: { - title, - artist: artist || null, - album: album || null, - artworkUrl: artworkUrl || null, - duration, - sortOrder, - isActive, - createdAt: now, - updatedAt: now, - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "radio_autodj_create", - description: `Created AutoDJ track "${title}"${artist ? ` by ${artist}` : ""}`, - targetType: "radio_auto_dj_track", - targetId: Number(created.id), - }); - } catch { - // Fail soft — DB unavailable; re-render without throwing. - } - revalidatePath("/admin/radio/autodj"); + try { + const created = await prisma.radioAutoDjPlaylist.create({ + data: { + title, + artist: artist || null, + album: album || null, + artworkUrl: artworkUrl || null, + duration, + sortOrder, + isActive, + createdAt: now, + updatedAt: now, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "radio_autodj_create", + description: `Created AutoDJ track "${title}"${artist ? ` by ${artist}` : ""}`, + targetType: "radio_auto_dj_track", + targetId: Number(created.id), + }); + } catch { + // Fail soft — DB unavailable; re-render without throwing. + } + revalidatePath("/admin/radio/autodj"); } export async function toggleTrack(formData: FormData): Promise { - const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + const staff = await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - // The form posts the desired next state so the toggle is idempotent. - const isActive = bool(formData.get("isActive")); + // The form posts the desired next state so the toggle is idempotent. + const isActive = bool(formData.get("isActive")); - try { - await prisma.radioAutoDjPlaylist.update({ - where: { id }, - data: { isActive, updatedAt: new Date() }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "radio_autodj_toggle", - description: `${isActive ? "Activated" : "Deactivated"} AutoDJ track #${id}`, - targetType: "radio_auto_dj_track", - targetId: Number(id), - }); - } catch { - // Row may be gone; ignore. - } - revalidatePath("/admin/radio/autodj"); + try { + await prisma.radioAutoDjPlaylist.update({ + where: { id }, + data: { isActive, updatedAt: new Date() }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "radio_autodj_toggle", + description: `${isActive ? "Activated" : "Deactivated"} AutoDJ track #${id}`, + targetType: "radio_auto_dj_track", + targetId: Number(id), + }); + } catch { + // Row may be gone; ignore. + } + revalidatePath("/admin/radio/autodj"); } export async function deleteTrack(formData: FormData): Promise { - const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + const staff = await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - try { - await prisma.radioAutoDjPlaylist.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "radio_autodj_delete", - description: `Deleted AutoDJ track #${id}`, - targetType: "radio_auto_dj_track", - targetId: Number(id), - }); - } catch { - // Already deleted; ignore. - } - revalidatePath("/admin/radio/autodj"); + try { + await prisma.radioAutoDjPlaylist.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "radio_autodj_delete", + description: `Deleted AutoDJ track #${id}`, + targetType: "radio_auto_dj_track", + targetId: Number(id), + }); + } catch { + // Already deleted; ignore. + } + revalidatePath("/admin/radio/autodj"); } diff --git a/src/actions/admin-radio-extra.ts b/src/actions/admin-radio-extra.ts index 503dad167a..31bb6c1ffe 100644 --- a/src/actions/admin-radio-extra.ts +++ b/src/actions/admin-radio-extra.ts @@ -9,23 +9,23 @@ import { siteSettings } from "@/lib/services/site-settings"; /** Parse a FormData field into a positive BigInt id, or null when invalid. */ function parseId(raw: FormDataEntryValue | null): bigint | null { - if (typeof raw !== "string" || raw.trim() === "") return null; - try { - const id = BigInt(raw.trim()); - return id > 0n ? id : null; - } catch { - return null; - } + if (typeof raw !== "string" || raw.trim() === "") return null; + try { + const id = BigInt(raw.trim()); + return id > 0n ? id : null; + } catch { + return null; + } } function str(raw: FormDataEntryValue | null): string { - return typeof raw === "string" ? raw : ""; + return typeof raw === "string" ? raw : ""; } /** Checkbox/select truthiness: '1', 'true', 'on' → true. */ function bool(raw: FormDataEntryValue | null): boolean { - const v = str(raw).trim().toLowerCase(); - return v === "1" || v === "true" || v === "on"; + const v = str(raw).trim().toLowerCase(); + return v === "1" || v === "true" || v === "on"; } // ── Radio settings (website_settings radio_* keys) ───────────────────────── @@ -36,23 +36,23 @@ function bool(raw: FormDataEntryValue | null): boolean { * siteSettings cache so the public radio pages pick the change up immediately. */ export async function saveRadioSetting(formData: FormData): Promise { - await requireStaff(); - const key = str(formData.get("key")).trim().slice(0, 255); - const value = str(formData.get("value")); - const comment = str(formData.get("comment")).trim().slice(0, 255); - if (!key) return; + await requireStaff(); + const key = str(formData.get("key")).trim().slice(0, 255); + const value = str(formData.get("value")); + const comment = str(formData.get("comment")).trim().slice(0, 255); + if (!key) return; - try { - await prisma.websiteSetting.upsert({ - where: { key }, - update: { value }, - create: { key, value, comment: comment || null }, - }); - siteSettings.reload(); - } catch { - // DB unavailable — fail soft so the action does not throw. - } - revalidatePath("/admin/radio/settings"); + try { + await prisma.websiteSetting.upsert({ + where: { key }, + update: { value }, + create: { key, value, comment: comment || null }, + }); + siteSettings.reload(); + } catch { + // DB unavailable — fail soft so the action does not throw. + } + revalidatePath("/admin/radio/settings"); } /** @@ -61,173 +61,177 @@ export async function saveRadioSetting(formData: FormData): Promise { * only touch those (and never wipe unrelated settings). */ export async function saveRadioSettings(formData: FormData): Promise { - await requireStaff(); - const keysRaw = str(formData.get("__keys")); - const keys = keysRaw - .split(",") - .map((k) => k.trim()) - .filter((k) => k.startsWith("radio_") || k.startsWith("auto_dj_")); - if (keys.length === 0) return; + await requireStaff(); + const keysRaw = str(formData.get("__keys")); + const keys = keysRaw + .split(",") + .map((k) => k.trim()) + .filter((k) => k.startsWith("radio_") || k.startsWith("auto_dj_")); + if (keys.length === 0) return; - try { - await prisma.$transaction( - keys.map((key) => { - const value = str(formData.get(key)); - return prisma.websiteSetting.upsert({ - where: { key }, - update: { value }, - create: { key, value, comment: null }, - }); - }), - ); - siteSettings.reload(); - } catch { - // Fail soft. - } - revalidatePath("/admin/radio/settings"); + try { + await prisma.$transaction( + keys.map((key) => { + const value = str(formData.get(key)); + return prisma.websiteSetting.upsert({ + where: { key }, + update: { value }, + create: { key, value, comment: null }, + }); + }), + ); + siteSettings.reload(); + } catch { + // Fail soft. + } + revalidatePath("/admin/radio/settings"); } // ── Radio banners CRUD (radio_banners) ───────────────────────────────────── export async function createRadioBanner(formData: FormData): Promise { - const staff = await requireStaff(); - const imagePath = str(formData.get("imagePath")).trim().slice(0, 255); - if (!imagePath) return; + const staff = await requireStaff(); + const imagePath = str(formData.get("imagePath")).trim().slice(0, 255); + if (!imagePath) return; - const title = str(formData.get("title")).trim().slice(0, 255); - const description = str(formData.get("description")).trim(); - const sortOrderNum = Number(str(formData.get("sortOrder"))); - const sortOrder = Number.isFinite(sortOrderNum) ? Math.trunc(sortOrderNum) : 0; - const isActive = bool(formData.get("isActive")); - const now = new Date(); + const title = str(formData.get("title")).trim().slice(0, 255); + const description = str(formData.get("description")).trim(); + const sortOrderNum = Number(str(formData.get("sortOrder"))); + const sortOrder = Number.isFinite(sortOrderNum) + ? Math.trunc(sortOrderNum) + : 0; + const isActive = bool(formData.get("isActive")); + const now = new Date(); - try { - await prisma.radioBanners.create({ - data: { - userId: BigInt(staff.id), - imagePath, - title: title || null, - description: description || null, - sortOrder, - isActive, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // Fail soft. - } - revalidatePath("/admin/radio/banners"); + try { + await prisma.radioBanners.create({ + data: { + userId: BigInt(staff.id), + imagePath, + title: title || null, + description: description || null, + sortOrder, + isActive, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // Fail soft. + } + revalidatePath("/admin/radio/banners"); } export async function updateRadioBanner(formData: FormData): Promise { - await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - const imagePath = str(formData.get("imagePath")).trim().slice(0, 255); - const title = str(formData.get("title")).trim().slice(0, 255); - const description = str(formData.get("description")).trim(); - const sortOrderNum = Number(str(formData.get("sortOrder"))); - const sortOrder = Number.isFinite(sortOrderNum) ? Math.trunc(sortOrderNum) : 0; - const isActive = bool(formData.get("isActive")); - if (!imagePath) return; + const imagePath = str(formData.get("imagePath")).trim().slice(0, 255); + const title = str(formData.get("title")).trim().slice(0, 255); + const description = str(formData.get("description")).trim(); + const sortOrderNum = Number(str(formData.get("sortOrder"))); + const sortOrder = Number.isFinite(sortOrderNum) + ? Math.trunc(sortOrderNum) + : 0; + const isActive = bool(formData.get("isActive")); + if (!imagePath) return; - try { - await prisma.radioBanners.update({ - where: { id }, - data: { - imagePath, - title: title || null, - description: description || null, - sortOrder, - isActive, - updatedAt: new Date(), - }, - }); - } catch { - // Row may be gone; ignore. - } - revalidatePath("/admin/radio/banners"); + try { + await prisma.radioBanners.update({ + where: { id }, + data: { + imagePath, + title: title || null, + description: description || null, + sortOrder, + isActive, + updatedAt: new Date(), + }, + }); + } catch { + // Row may be gone; ignore. + } + revalidatePath("/admin/radio/banners"); } export async function deleteRadioBanner(formData: FormData): Promise { - await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; - try { - await prisma.radioBanners.delete({ where: { id } }); - } catch { - // Already deleted; ignore. - } - revalidatePath("/admin/radio/banners"); + await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; + try { + await prisma.radioBanners.delete({ where: { id } }); + } catch { + // Already deleted; ignore. + } + revalidatePath("/admin/radio/banners"); } // ── Radio ranks CRUD (radio_ranks) ───────────────────────────────────────── export async function createRadioRank(formData: FormData): Promise { - await requireStaff(); - const name = str(formData.get("name")).trim().slice(0, 255); - if (!name) return; + await requireStaff(); + const name = str(formData.get("name")).trim().slice(0, 255); + if (!name) return; - const description = str(formData.get("description")).trim().slice(0, 255); - const badgeCode = str(formData.get("badgeCode")).trim().slice(0, 255); - const isActive = bool(formData.get("isActive")); - const now = new Date(); + const description = str(formData.get("description")).trim().slice(0, 255); + const badgeCode = str(formData.get("badgeCode")).trim().slice(0, 255); + const isActive = bool(formData.get("isActive")); + const now = new Date(); - try { - await prisma.radioRanks.create({ - data: { - name, - description: description || null, - badgeCode: badgeCode || null, - isActive, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // Fail soft. - } - revalidatePath("/admin/radio/ranks"); + try { + await prisma.radioRanks.create({ + data: { + name, + description: description || null, + badgeCode: badgeCode || null, + isActive, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // Fail soft. + } + revalidatePath("/admin/radio/ranks"); } export async function updateRadioRank(formData: FormData): Promise { - await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - const name = str(formData.get("name")).trim().slice(0, 255); - const description = str(formData.get("description")).trim().slice(0, 255); - const badgeCode = str(formData.get("badgeCode")).trim().slice(0, 255); - const isActive = bool(formData.get("isActive")); - if (!name) return; + const name = str(formData.get("name")).trim().slice(0, 255); + const description = str(formData.get("description")).trim().slice(0, 255); + const badgeCode = str(formData.get("badgeCode")).trim().slice(0, 255); + const isActive = bool(formData.get("isActive")); + if (!name) return; - try { - await prisma.radioRanks.update({ - where: { id }, - data: { - name, - description: description || null, - badgeCode: badgeCode || null, - isActive, - updatedAt: new Date(), - }, - }); - } catch { - // Row may be gone; ignore. - } - revalidatePath("/admin/radio/ranks"); + try { + await prisma.radioRanks.update({ + where: { id }, + data: { + name, + description: description || null, + badgeCode: badgeCode || null, + isActive, + updatedAt: new Date(), + }, + }); + } catch { + // Row may be gone; ignore. + } + revalidatePath("/admin/radio/ranks"); } export async function deleteRadioRank(formData: FormData): Promise { - await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; - try { - await prisma.radioRanks.delete({ where: { id } }); - } catch { - // Already deleted; ignore. - } - revalidatePath("/admin/radio/ranks"); + await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; + try { + await prisma.radioRanks.delete({ where: { id } }); + } catch { + // Already deleted; ignore. + } + revalidatePath("/admin/radio/ranks"); } diff --git a/src/actions/admin-radio-moderation.ts b/src/actions/admin-radio-moderation.ts index d093dc0bcb..b5111b9186 100644 --- a/src/actions/admin-radio-moderation.ts +++ b/src/actions/admin-radio-moderation.ts @@ -2,18 +2,18 @@ import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; -import { logStaffActivity } from "@/lib/services/staff-activity"; import { prisma } from "@/lib/prisma"; +import { logStaffActivity } from "@/lib/services/staff-activity"; /** Parse a FormData field into a positive BigInt id, or null when invalid. */ function parseId(raw: FormDataEntryValue | null): bigint | null { - if (typeof raw !== "string" || raw.trim() === "") return null; - try { - const id = BigInt(raw.trim()); - return id > 0n ? id : null; - } catch { - return null; - } + if (typeof raw !== "string" || raw.trim() === "") return null; + try { + const id = BigInt(raw.trim()); + return id > 0n ? id : null; + } catch { + return null; + } } /** @@ -22,22 +22,22 @@ function parseId(raw: FormDataEntryValue | null): bigint | null { * moderation route. Fails soft if the row is already gone. */ export async function deleteShout(formData: FormData): Promise { - const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + const staff = await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - try { - await prisma.radioShouts.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "radio.shout.delete", - description: `Deleted radio shout #${id}`, - targetType: "radio_shout", - targetId: Number(id), - }); - } catch { - // Row may already be gone; ignore so the action does not throw. - } + try { + await prisma.radioShouts.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "radio.shout.delete", + description: `Deleted radio shout #${id}`, + targetType: "radio_shout", + targetId: Number(id), + }); + } catch { + // Row may already be gone; ignore so the action does not throw. + } - revalidatePath("/admin/radio/moderation"); + revalidatePath("/admin/radio/moderation"); } diff --git a/src/actions/admin-radio-points.ts b/src/actions/admin-radio-points.ts index 4d066215c5..618552437e 100644 --- a/src/actions/admin-radio-points.ts +++ b/src/actions/admin-radio-points.ts @@ -4,8 +4,8 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; -import { logStaffActivity } from "@/lib/services/staff-activity"; import { siteSettings } from "@/lib/services/site-settings"; +import { logStaffActivity } from "@/lib/services/staff-activity"; // Radio listener-points settings (website_settings radio_points_* keys). // Mirrors AtomCMS's RadioPoints Filament page: key/value rows in @@ -14,68 +14,79 @@ import { siteSettings } from "@/lib/services/site-settings"; // pages pick the change up immediately. const POINTS_KEYS = [ - "radio_points_enabled", - "radio_points_per_minute", - "radio_points_currency", - "radio_points_max_per_day", - "radio_points_min_listeners", + "radio_points_enabled", + "radio_points_per_minute", + "radio_points_currency", + "radio_points_max_per_day", + "radio_points_min_listeners", ] as const; -const ALLOWED_CURRENCIES = new Set(["credits", "duckets", "diamonds", "points"]); +const ALLOWED_CURRENCIES = new Set([ + "credits", + "duckets", + "diamonds", + "points", +]); function str(raw: FormDataEntryValue | null): string { - return typeof raw === "string" ? raw : ""; + return typeof raw === "string" ? raw : ""; } /** Checkbox/select truthiness → '1' / '0'. */ function boolStr(raw: FormDataEntryValue | null): "0" | "1" { - const v = str(raw).trim().toLowerCase(); - return v === "1" || v === "true" || v === "on" ? "1" : "0"; + const v = str(raw).trim().toLowerCase(); + return v === "1" || v === "true" || v === "on" ? "1" : "0"; } /** Clamp a form value to a non-negative integer string (defaulting to 0). */ function intStr(raw: FormDataEntryValue | null): string { - const n = Number(str(raw).trim()); - if (!Number.isFinite(n) || n < 0) return "0"; - return String(Math.floor(n)); + const n = Number(str(raw).trim()); + if (!Number.isFinite(n) || n < 0) return "0"; + return String(Math.floor(n)); } export async function savePoints(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const currencyRaw = str(formData.get("radio_points_currency")).trim().toLowerCase(); - const currency = ALLOWED_CURRENCIES.has(currencyRaw) ? currencyRaw : "credits"; + const currencyRaw = str(formData.get("radio_points_currency")) + .trim() + .toLowerCase(); + const currency = ALLOWED_CURRENCIES.has(currencyRaw) + ? currencyRaw + : "credits"; - const values: Record<(typeof POINTS_KEYS)[number], string> = { - radio_points_enabled: boolStr(formData.get("radio_points_enabled")), - radio_points_per_minute: intStr(formData.get("radio_points_per_minute")), - radio_points_currency: currency, - radio_points_max_per_day: intStr(formData.get("radio_points_max_per_day")), - radio_points_min_listeners: intStr(formData.get("radio_points_min_listeners")), - }; + const values: Record<(typeof POINTS_KEYS)[number], string> = { + radio_points_enabled: boolStr(formData.get("radio_points_enabled")), + radio_points_per_minute: intStr(formData.get("radio_points_per_minute")), + radio_points_currency: currency, + radio_points_max_per_day: intStr(formData.get("radio_points_max_per_day")), + radio_points_min_listeners: intStr( + formData.get("radio_points_min_listeners"), + ), + }; - try { - await prisma.$transaction( - POINTS_KEYS.map((key) => - prisma.websiteSetting.upsert({ - where: { key }, - // eslint-disable-next-line security/detect-object-injection -- key from POINTS_KEYS const - update: { value: values[key] }, - // eslint-disable-next-line security/detect-object-injection -- key from POINTS_KEYS const - create: { key, value: values[key], comment: "Radio points" }, - }), - ), - ); - siteSettings.reload(); - await logStaffActivity({ - staffId: staff.id, - action: "radio_points_update", - description: `Updated radio listener-points settings (enabled=${values.radio_points_enabled}, ${values.radio_points_per_minute}/min ${currency})`, - }); - } catch { - // DB unavailable — fail soft so the action does not throw. - } + try { + await prisma.$transaction( + POINTS_KEYS.map((key) => + prisma.websiteSetting.upsert({ + where: { key }, + // eslint-disable-next-line security/detect-object-injection -- key from POINTS_KEYS const + update: { value: values[key] }, + // eslint-disable-next-line security/detect-object-injection -- key from POINTS_KEYS const + create: { key, value: values[key], comment: "Radio points" }, + }), + ), + ); + siteSettings.reload(); + await logStaffActivity({ + staffId: staff.id, + action: "radio_points_update", + description: `Updated radio listener-points settings (enabled=${values.radio_points_enabled}, ${values.radio_points_per_minute}/min ${currency})`, + }); + } catch { + // DB unavailable — fail soft so the action does not throw. + } - revalidatePath("/admin/radio/points"); - redirect("/admin/radio/points?saved=1"); + revalidatePath("/admin/radio/points"); + redirect("/admin/radio/points?saved=1"); } diff --git a/src/actions/admin-rare-values.ts b/src/actions/admin-rare-values.ts index 50aeba9728..b244db5826 100644 --- a/src/actions/admin-rare-values.ts +++ b/src/actions/admin-rare-values.ts @@ -2,106 +2,110 @@ import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; -import { prisma } from "@/lib/prisma"; import { formPositiveBigInt } from "@/lib/form-data"; +import { prisma } from "@/lib/prisma"; export async function createCategory(formData: FormData): Promise { - await requireStaff(); - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const badge = String(formData.get("badge") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const priorityRaw = Number(formData.get("priority")); - const priority = Number.isFinite(priorityRaw) && priorityRaw > 0 ? Math.floor(priorityRaw) : 1; - if (!name || !badge) return; + await requireStaff(); + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const badge = String(formData.get("badge") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const priorityRaw = Number(formData.get("priority")); + const priority = + Number.isFinite(priorityRaw) && priorityRaw > 0 + ? Math.floor(priorityRaw) + : 1; + if (!name || !badge) return; - try { - await prisma.websiteRareValueCategories.create({ - data: { name, badge, priority }, - }); - } catch { - // Unique name collision or DB error — ignore, page will re-render unchanged. - } - revalidatePath("/admin/rare-values"); + try { + await prisma.websiteRareValueCategories.create({ + data: { name, badge, priority }, + }); + } catch { + // Unique name collision or DB error — ignore, page will re-render unchanged. + } + revalidatePath("/admin/rare-values"); } export async function deleteCategory(formData: FormData): Promise { - await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - // Remove the category's values first to avoid orphaned rows. - await prisma.websiteRareValues.deleteMany({ where: { categoryId: id } }); - await prisma.websiteRareValueCategories.delete({ where: { id } }); - } catch { - // Not found or DB error — ignore. - } - revalidatePath("/admin/rare-values"); + try { + // Remove the category's values first to avoid orphaned rows. + await prisma.websiteRareValues.deleteMany({ where: { categoryId: id } }); + await prisma.websiteRareValueCategories.delete({ where: { id } }); + } catch { + // Not found or DB error — ignore. + } + revalidatePath("/admin/rare-values"); } export async function createValue(formData: FormData): Promise { - await requireStaff(); - const categoryId = formPositiveBigInt(formData, "categoryId"); - if (!categoryId) return; + await requireStaff(); + const categoryId = formPositiveBigInt(formData, "categoryId"); + if (!categoryId) return; - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const furnitureIcon = String(formData.get("furnitureIcon") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!name || !furnitureIcon) return; + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const furnitureIcon = String(formData.get("furnitureIcon") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!name || !furnitureIcon) return; - const itemIdRaw = Number(formData.get("itemId")); - const itemId = Number.isFinite(itemIdRaw) && itemIdRaw > 0 ? Math.floor(itemIdRaw) : null; + const itemIdRaw = Number(formData.get("itemId")); + const itemId = + Number.isFinite(itemIdRaw) && itemIdRaw > 0 ? Math.floor(itemIdRaw) : null; - const creditValueRaw = String(formData.get("creditValue") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const currencyValueRaw = String(formData.get("currencyValue") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const currencyType = - String(formData.get("currencyType") ?? "diamonds") - .trim() - .slice(0, 255) || "diamonds"; + const creditValueRaw = String(formData.get("creditValue") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const currencyValueRaw = String(formData.get("currencyValue") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const currencyType = + String(formData.get("currencyType") ?? "diamonds") + .trim() + .slice(0, 255) || "diamonds"; - try { - await prisma.websiteRareValues.create({ - data: { - categoryId, - itemId, - name, - creditValue: creditValueRaw || null, - currencyValue: currencyValueRaw || null, - currencyType, - furnitureIcon, - }, - }); - } catch { - // DB error — ignore. - } - revalidatePath("/admin/rare-values"); + try { + await prisma.websiteRareValues.create({ + data: { + categoryId, + itemId, + name, + creditValue: creditValueRaw || null, + currencyValue: currencyValueRaw || null, + currencyType, + furnitureIcon, + }, + }); + } catch { + // DB error — ignore. + } + revalidatePath("/admin/rare-values"); } export async function deleteValue(formData: FormData): Promise { - await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + await requireStaff(); + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - await prisma.websiteRareValues.delete({ where: { id } }); - } catch { - // Not found or DB error — ignore. - } - revalidatePath("/admin/rare-values"); + try { + await prisma.websiteRareValues.delete({ where: { id } }); + } catch { + // Not found or DB error — ignore. + } + revalidatePath("/admin/rare-values"); } diff --git a/src/actions/admin-settings.ts b/src/actions/admin-settings.ts index 61a3953e0b..2c3d5054be 100644 --- a/src/actions/admin-settings.ts +++ b/src/actions/admin-settings.ts @@ -6,45 +6,45 @@ import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; export async function updateSetting(formData: FormData): Promise { - await requireStaff(); - const key = String(formData.get("key") ?? "") - .normalize("NFC") - .trim(); - const value = String(formData.get("value") ?? "").normalize("NFC"); - if (!key) return; - await prisma.websiteSetting.update({ where: { key }, data: { value } }); - siteSettings.reload(); - revalidatePath("/admin/settings"); + await requireStaff(); + const key = String(formData.get("key") ?? "") + .normalize("NFC") + .trim(); + const value = String(formData.get("value") ?? "").normalize("NFC"); + if (!key) return; + await prisma.websiteSetting.update({ where: { key }, data: { value } }); + siteSettings.reload(); + revalidatePath("/admin/settings"); } export async function createSetting(formData: FormData): Promise { - await requireStaff(); - const key = String(formData.get("key") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const value = String(formData.get("value") ?? "").normalize("NFC"); - const comment = String(formData.get("comment") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!key) return; - await prisma.websiteSetting.upsert({ - where: { key }, - update: { value }, - create: { key, value, comment: comment || null }, - }); - siteSettings.reload(); - revalidatePath("/admin/settings"); + await requireStaff(); + const key = String(formData.get("key") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const value = String(formData.get("value") ?? "").normalize("NFC"); + const comment = String(formData.get("comment") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!key) return; + await prisma.websiteSetting.upsert({ + where: { key }, + update: { value }, + create: { key, value, comment: comment || null }, + }); + siteSettings.reload(); + revalidatePath("/admin/settings"); } export async function deleteSetting(formData: FormData): Promise { - await requireStaff(); - const key = String(formData.get("key") ?? "") - .normalize("NFC") - .trim(); - if (!key) return; - await prisma.websiteSetting.delete({ where: { key } }); - siteSettings.reload(); - revalidatePath("/admin/settings"); + await requireStaff(); + const key = String(formData.get("key") ?? "") + .normalize("NFC") + .trim(); + if (!key) return; + await prisma.websiteSetting.delete({ where: { key } }); + siteSettings.reload(); + revalidatePath("/admin/settings"); } diff --git a/src/actions/admin-shop.ts b/src/actions/admin-shop.ts index 76a922ff7d..32187f616e 100644 --- a/src/actions/admin-shop.ts +++ b/src/actions/admin-shop.ts @@ -3,10 +3,10 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { requireStaff } from "@/lib/admin/guard"; -import { prisma } from "@/lib/prisma"; -import { logStaffActivity } from "@/lib/services/staff-activity"; import { formPositiveBigInt } from "@/lib/form-data"; +import { prisma } from "@/lib/prisma"; import { logServerError } from "@/lib/server-log"; +import { logStaffActivity } from "@/lib/services/staff-activity"; // Website store packages (website_shop_articles). This CMS-owned table backs // the public store; rows here are the buyable packages, not orders. The closest @@ -14,162 +14,165 @@ import { logServerError } from "@/lib/server-log"; /** Parse an UnsignedInt form value, returning null when blank/invalid/negative. */ function optUInt(formData: FormData, key: string): number | null { - const raw = String(formData.get(key) ?? "") - .normalize("NFC") - .trim(); - if (raw === "") return null; - const n = Number(raw); - if (!Number.isFinite(n) || n < 0) return null; - return Math.floor(n); + const raw = String(formData.get(key) ?? "") + .normalize("NFC") + .trim(); + if (raw === "") return null; + const n = Number(raw); + if (!Number.isFinite(n) || n < 0) return null; + return Math.floor(n); } /** Parse a required non-negative UnsignedInt, falling back to 0. */ function reqUInt(formData: FormData, key: string): number { - const n = optUInt(formData, key); - return n ?? 0; + const n = optUInt(formData, key); + return n ?? 0; } export async function createShopArticle(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!name) return; + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!name) return; - const now = new Date(); - try { - const created = await prisma.websiteShopArticles.create({ - data: { - name, - info: String(formData.get("info") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - iconUrl: String(formData.get("icon") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - color: String(formData.get("color") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - costs: reqUInt(formData, "costs"), - giveRank: optUInt(formData, "giveRank"), - credits: optUInt(formData, "credits"), - duckets: optUInt(formData, "duckets"), - diamonds: optUInt(formData, "diamonds"), - badges: - String(formData.get("badges") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255) || null, - position: reqUInt(formData, "position"), - createdAt: now, - updatedAt: now, - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "shop_create", - description: `Created shop package "${name}" (${created.costs} costs)`, - targetType: "shop_article", - targetId: Number(created.id), - }); - } catch (error) { - logServerError("admin.shop_create_failed", error, { staffId: staff.id, name }); - // Unique constraint on `name` (or DB unavailable) — swallow and re-render. - return; - } + const now = new Date(); + try { + const created = await prisma.websiteShopArticles.create({ + data: { + name, + info: String(formData.get("info") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + iconUrl: String(formData.get("icon") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + color: String(formData.get("color") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + costs: reqUInt(formData, "costs"), + giveRank: optUInt(formData, "giveRank"), + credits: optUInt(formData, "credits"), + duckets: optUInt(formData, "duckets"), + diamonds: optUInt(formData, "diamonds"), + badges: + String(formData.get("badges") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255) || null, + position: reqUInt(formData, "position"), + createdAt: now, + updatedAt: now, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "shop_create", + description: `Created shop package "${name}" (${created.costs} costs)`, + targetType: "shop_article", + targetId: Number(created.id), + }); + } catch (error) { + logServerError("admin.shop_create_failed", error, { + staffId: staff.id, + name, + }); + // Unique constraint on `name` (or DB unavailable) — swallow and re-render. + return; + } - redirect("/admin/shop"); + redirect("/admin/shop"); } export async function updateShopArticle(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!name) return; + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!name) return; - try { - await prisma.websiteShopArticles.update({ - where: { id }, - data: { - name, - info: String(formData.get("info") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - iconUrl: String(formData.get("icon") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - color: String(formData.get("color") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - costs: reqUInt(formData, "costs"), - giveRank: optUInt(formData, "giveRank"), - credits: optUInt(formData, "credits"), - duckets: optUInt(formData, "duckets"), - diamonds: optUInt(formData, "diamonds"), - badges: - String(formData.get("badges") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255) || null, - position: reqUInt(formData, "position"), - updatedAt: new Date(), - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "shop_update", - description: `Updated shop package #${id} ("${name}")`, - targetType: "shop_article", - targetId: Number(id), - }); - } catch (error) { - logServerError("admin.shop_update_failed", error, { - staffId: staff.id, - articleId: String(id), - }); - return; - } + try { + await prisma.websiteShopArticles.update({ + where: { id }, + data: { + name, + info: String(formData.get("info") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + iconUrl: String(formData.get("icon") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + color: String(formData.get("color") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + costs: reqUInt(formData, "costs"), + giveRank: optUInt(formData, "giveRank"), + credits: optUInt(formData, "credits"), + duckets: optUInt(formData, "duckets"), + diamonds: optUInt(formData, "diamonds"), + badges: + String(formData.get("badges") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255) || null, + position: reqUInt(formData, "position"), + updatedAt: new Date(), + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "shop_update", + description: `Updated shop package #${id} ("${name}")`, + targetType: "shop_article", + targetId: Number(id), + }); + } catch (error) { + logServerError("admin.shop_update_failed", error, { + staffId: staff.id, + articleId: String(id), + }); + return; + } - revalidatePath(`/admin/shop/${id}`); - redirect("/admin/shop"); + revalidatePath(`/admin/shop/${id}`); + redirect("/admin/shop"); } export async function deleteShopArticle(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - await prisma.websiteShopArticles.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "shop_delete", - description: `Deleted shop package #${id}`, - targetType: "shop_article", - targetId: Number(id), - }); - } catch (error) { - logServerError("admin.shop_delete_failed", error, { - staffId: staff.id, - articleId: String(id), - }); - return; - } + try { + await prisma.websiteShopArticles.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "shop_delete", + description: `Deleted shop package #${id}`, + targetType: "shop_article", + targetId: Number(id), + }); + } catch (error) { + logServerError("admin.shop_delete_failed", error, { + staffId: staff.id, + articleId: String(id), + }); + return; + } - redirect("/admin/shop"); + redirect("/admin/shop"); } diff --git a/src/actions/admin-tags.ts b/src/actions/admin-tags.ts index 004fbb0817..619e0b0ab3 100644 --- a/src/actions/admin-tags.ts +++ b/src/actions/admin-tags.ts @@ -9,99 +9,99 @@ import { logStaffActivity } from "@/lib/services/staff-activity"; /** Parse a FormData field into a positive BigInt id, or null when invalid. */ function parseId(raw: FormDataEntryValue | null): bigint | null { - if (typeof raw !== "string" || raw.trim() === "") return null; - try { - const id = BigInt(raw.trim()); - return id > 0n ? id : null; - } catch { - return null; - } + if (typeof raw !== "string" || raw.trim() === "") return null; + try { + const id = BigInt(raw.trim()); + return id > 0n ? id : null; + } catch { + return null; + } } function str(raw: FormDataEntryValue | null): string { - return typeof raw === "string" ? raw : ""; + return typeof raw === "string" ? raw : ""; } /** Normalise a hex-ish colour into the 10-char background_color column. */ function normaliseColor(raw: string): string { - const v = raw.trim().slice(0, 10); - return v || "#888888"; + const v = raw.trim().slice(0, 10); + return v || "#888888"; } // ── Tags CRUD (tags + taggables, AtomCMS article tags/categories) ────────── export async function createTag(formData: FormData): Promise { - const staff = await requireStaff(); - const name = str(formData.get("name")).trim().slice(0, 255); - if (!name) return; + const staff = await requireStaff(); + const name = str(formData.get("name")).trim().slice(0, 255); + if (!name) return; - const backgroundColor = normaliseColor(str(formData.get("backgroundColor"))); - const now = new Date(); + const backgroundColor = normaliseColor(str(formData.get("backgroundColor"))); + const now = new Date(); - try { - const created = await prisma.tags.create({ - data: { name, backgroundColor, createdAt: now, updatedAt: now }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "tag_create", - description: `Created tag "${name}" (#${created.id})`, - targetType: "tag", - targetId: Number(created.id), - }); - } catch { - // Fail soft — DB unavailable or duplicate. - } - revalidatePath("/admin/tags"); + try { + const created = await prisma.tags.create({ + data: { name, backgroundColor, createdAt: now, updatedAt: now }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "tag_create", + description: `Created tag "${name}" (#${created.id})`, + targetType: "tag", + targetId: Number(created.id), + }); + } catch { + // Fail soft — DB unavailable or duplicate. + } + revalidatePath("/admin/tags"); } export async function updateTag(formData: FormData): Promise { - const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + const staff = await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - const name = str(formData.get("name")).trim().slice(0, 255); - const backgroundColor = normaliseColor(str(formData.get("backgroundColor"))); - if (!name) return; + const name = str(formData.get("name")).trim().slice(0, 255); + const backgroundColor = normaliseColor(str(formData.get("backgroundColor"))); + if (!name) return; - try { - await prisma.tags.update({ - where: { id }, - data: { name, backgroundColor, updatedAt: new Date() }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "tag_update", - description: `Updated tag #${id} → "${name}"`, - targetType: "tag", - targetId: Number(id), - }); - } catch { - // Row may be gone; ignore. - } - revalidatePath("/admin/tags"); + try { + await prisma.tags.update({ + where: { id }, + data: { name, backgroundColor, updatedAt: new Date() }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "tag_update", + description: `Updated tag #${id} → "${name}"`, + targetType: "tag", + targetId: Number(id), + }); + } catch { + // Row may be gone; ignore. + } + revalidatePath("/admin/tags"); } export async function deleteTag(formData: FormData): Promise { - const staff = await requireStaff(); - const id = parseId(formData.get("id")); - if (id === null) return; + const staff = await requireStaff(); + const id = parseId(formData.get("id")); + if (id === null) return; - try { - // Remove the tag and any taggable links pointing at it. - await prisma.$transaction([ - prisma.taggables.deleteMany({ where: { tagId: id } }), - prisma.tags.delete({ where: { id } }), - ]); - await logStaffActivity({ - staffId: staff.id, - action: "tag_delete", - description: `Deleted tag #${id}`, - targetType: "tag", - targetId: Number(id), - }); - } catch { - // Already deleted; ignore. - } - revalidatePath("/admin/tags"); + try { + // Remove the tag and any taggable links pointing at it. + await prisma.$transaction([ + prisma.taggables.deleteMany({ where: { tagId: id } }), + prisma.tags.delete({ where: { id } }), + ]); + await logStaffActivity({ + staffId: staff.id, + action: "tag_delete", + description: `Deleted tag #${id}`, + targetType: "tag", + targetId: Number(id), + }); + } catch { + // Already deleted; ignore. + } + revalidatePath("/admin/tags"); } diff --git a/src/actions/admin-teams.ts b/src/actions/admin-teams.ts index 8aca2ea397..022e03f410 100644 --- a/src/actions/admin-teams.ts +++ b/src/actions/admin-teams.ts @@ -5,46 +5,46 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; export async function createTeam(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const rankName = String(formData.get("rankName") ?? "") - .normalize("NFC") - .trim(); - if (!rankName) return; + const rankName = String(formData.get("rankName") ?? "") + .normalize("NFC") + .trim(); + if (!rankName) return; - const badge = String(formData.get("badge") ?? "") - .normalize("NFC") - .trim(); - const jobDescription = String(formData.get("jobDescription") ?? "") - .normalize("NFC") - .trim(); - const staffColor = - String(formData.get("staffColor") ?? "") - .normalize("NFC") - .trim() || "#327fa8"; - const hiddenRank = formData.get("hiddenRank") === "on"; + const badge = String(formData.get("badge") ?? "") + .normalize("NFC") + .trim(); + const jobDescription = String(formData.get("jobDescription") ?? "") + .normalize("NFC") + .trim(); + const staffColor = + String(formData.get("staffColor") ?? "") + .normalize("NFC") + .trim() || "#327fa8"; + const hiddenRank = formData.get("hiddenRank") === "on"; - const now = new Date(); - await prisma.websiteTeams.create({ - data: { - rankName: rankName.slice(0, 255), - badge: badge ? badge.slice(0, 255) : null, - jobDescription: jobDescription ? jobDescription.slice(0, 255) : null, - staffColor: staffColor.slice(0, 255), - hiddenRank, - createdAt: now, - updatedAt: now, - }, - }); + const now = new Date(); + await prisma.websiteTeams.create({ + data: { + rankName: rankName.slice(0, 255), + badge: badge ? badge.slice(0, 255) : null, + jobDescription: jobDescription ? jobDescription.slice(0, 255) : null, + staffColor: staffColor.slice(0, 255), + hiddenRank, + createdAt: now, + updatedAt: now, + }, + }); - revalidatePath("/admin/teams"); + revalidatePath("/admin/teams"); } export async function deleteTeam(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const id = BigInt(String(formData.get("id"))); - await prisma.websiteTeams.delete({ where: { id } }); + const id = BigInt(String(formData.get("id"))); + await prisma.websiteTeams.delete({ where: { id } }); - revalidatePath("/admin/teams"); + revalidatePath("/admin/teams"); } diff --git a/src/actions/admin-theme.ts b/src/actions/admin-theme.ts index 52d5e40c3f..39baeb1607 100644 --- a/src/actions/admin-theme.ts +++ b/src/actions/admin-theme.ts @@ -6,14 +6,14 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; import { logStaffActivity } from "@/lib/services/staff-activity"; +import { + deleteCustomThemeStore, + getCustomTheme, + snapshotCurrentTheme, + upsertCustomTheme, +} from "@/lib/theme-custom-store"; import { FONTS, PRESETS, THEME_COLOR_KEYS } from "@/lib/theme-presets"; import { presetSettings, settingKey } from "@/lib/theme-settings"; -import { - deleteCustomThemeStore, - getCustomTheme, - snapshotCurrentTheme, - upsertCustomTheme, -} from "@/lib/theme-custom-store"; // Only hex/keyword colour values are accepted (matches ThemeVars' sanitiser). const COLOR_RE = /^[#a-zA-Z0-9(),.\s%-]+$/; @@ -22,179 +22,180 @@ const HEADING_KEYS = ["size_heading_h1", "size_heading_h2", "size_heading_h3"]; const CUSTOM_CSS_MAX = 20000; async function writeSetting(key: string, value: string): Promise { - await prisma.websiteSetting.upsert({ - where: { key }, - update: { value }, - create: { key, value, comment: "Theme (housekeeping)" }, - }); + await prisma.websiteSetting.upsert({ + where: { key }, + update: { value }, + create: { key, value, comment: "Theme (housekeeping)" }, + }); } export async function saveTheme(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - try { - for (const mode of ["light", "dark"] as const) { - for (const key of THEME_COLOR_KEYS) { - const dbKey = settingKey(key, mode); - const raw = String(formData.get(dbKey) ?? "") - .normalize("NFC") - .trim(); - if (raw && COLOR_RE.test(raw)) await writeSetting(dbKey, raw); - } - } - const ADMIN_KEYS = [ - "admin_canvas", - "admin_surface", - "admin_text", - "admin_text_muted", - "admin_border", - "admin_sidebar_bg", - ]; - for (const key of ADMIN_KEYS) { - const raw = String(formData.get(key) ?? "") - .normalize("NFC") - .trim(); - if (raw && COLOR_RE.test(raw)) await writeSetting(key, raw); - } + try { + for (const mode of ["light", "dark"] as const) { + for (const key of THEME_COLOR_KEYS) { + const dbKey = settingKey(key, mode); + const raw = String(formData.get(dbKey) ?? "") + .normalize("NFC") + .trim(); + if (raw && COLOR_RE.test(raw)) await writeSetting(dbKey, raw); + } + } + const ADMIN_KEYS = [ + "admin_canvas", + "admin_surface", + "admin_text", + "admin_text_muted", + "admin_border", + "admin_sidebar_bg", + ]; + for (const key of ADMIN_KEYS) { + const raw = String(formData.get(key) ?? "") + .normalize("NFC") + .trim(); + if (raw && COLOR_RE.test(raw)) await writeSetting(key, raw); + } - const radius = String(formData.get("border_radius") ?? "") - .normalize("NFC") - .trim(); - if (/^\d{1,3}$/.test(radius)) await writeSetting("border_radius", radius); + const radius = String(formData.get("border_radius") ?? "") + .normalize("NFC") + .trim(); + if (/^\d{1,3}$/.test(radius)) await writeSetting("border_radius", radius); - // Typography - const font = String(formData.get("font_family") ?? "") - .normalize("NFC") - .trim(); - if (font in FONTS) await writeSetting("font_family", font); - for (const key of HEADING_KEYS) { - const v = String(formData.get(key) ?? "") - .normalize("NFC") - .trim(); - if (/^\d{1,3}$/.test(v)) await writeSetting(key, v); - } + // Typography + const font = String(formData.get("font_family") ?? "") + .normalize("NFC") + .trim(); + if (font in FONTS) await writeSetting("font_family", font); + for (const key of HEADING_KEYS) { + const v = String(formData.get(key) ?? "") + .normalize("NFC") + .trim(); + if (/^\d{1,3}$/.test(v)) await writeSetting(key, v); + } - // Raw custom CSS (staff-trusted; length-capped, ThemeVars injects it as-is). - if (formData.has("custom_css")) { - const cssRaw = String(formData.get("custom_css") ?? "") - .normalize("NFC") - .slice(0, CUSTOM_CSS_MAX); - await writeSetting("custom_css", cssRaw); - } + // Raw custom CSS (staff-trusted; length-capped, ThemeVars injects it as-is). + if (formData.has("custom_css")) { + const cssRaw = String(formData.get("custom_css") ?? "") + .normalize("NFC") + .slice(0, CUSTOM_CSS_MAX); + await writeSetting("custom_css", cssRaw); + } - siteSettings.reload(); - await logStaffActivity({ - staffId: staff.id, - action: "theme_update", - description: "Updated theme settings", - }); - revalidatePath("/", "layout"); - } catch { - // ignore — page re-renders current state - } - redirect("/admin/theme?saved=1"); + siteSettings.reload(); + await logStaffActivity({ + staffId: staff.id, + action: "theme_update", + description: "Updated theme settings", + }); + revalidatePath("/", "layout"); + } catch { + // ignore — page re-renders current state + } + redirect("/admin/theme?saved=1"); } export async function applyPreset(formData: FormData): Promise { - const staff = await requireStaff(); - const name = String(formData.get("preset") ?? "").normalize("NFC"); - // eslint-disable-next-line security/detect-object-injection -- guarded by null check below - const preset = PRESETS[name]; - if (!preset) redirect("/admin/theme"); + const staff = await requireStaff(); + const name = String(formData.get("preset") ?? "").normalize("NFC"); + // eslint-disable-next-line security/detect-object-injection -- guarded by null check below + const preset = PRESETS[name]; + if (!preset) redirect("/admin/theme"); - try { - for (const [key, value] of presetSettings(preset)) await writeSetting(key, value); - await writeSetting("theme_preset", name); - siteSettings.reload(); - await logStaffActivity({ - staffId: staff.id, - action: "theme_preset", - description: `Applied theme preset "${name}"`, - }); - revalidatePath("/", "layout"); - } catch { - // ignore - } - redirect(`/admin/theme?preset=${encodeURIComponent(name)}`); + try { + for (const [key, value] of presetSettings(preset)) + await writeSetting(key, value); + await writeSetting("theme_preset", name); + siteSettings.reload(); + await logStaffActivity({ + staffId: staff.id, + action: "theme_preset", + description: `Applied theme preset "${name}"`, + }); + revalidatePath("/", "layout"); + } catch { + // ignore + } + redirect(`/admin/theme?preset=${encodeURIComponent(name)}`); } export async function saveCustomTheme(formData: FormData): Promise { - const staff = await requireStaff(); - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim(); - if (!name) redirect("/admin/theme"); - const snapshot = await snapshotCurrentTheme(); - try { - await upsertCustomTheme(name, snapshot); - await logStaffActivity({ - staffId: staff.id, - action: "theme_preset", - description: `Saved custom theme "${name}"`, - }); - revalidatePath("/admin/theme"); - } catch { - // ignore - } - redirect("/admin/theme?savedTheme=1"); + const staff = await requireStaff(); + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim(); + if (!name) redirect("/admin/theme"); + const snapshot = await snapshotCurrentTheme(); + try { + await upsertCustomTheme(name, snapshot); + await logStaffActivity({ + staffId: staff.id, + action: "theme_preset", + description: `Saved custom theme "${name}"`, + }); + revalidatePath("/admin/theme"); + } catch { + // ignore + } + redirect("/admin/theme?savedTheme=1"); } export async function applyCustomTheme(formData: FormData): Promise { - const staff = await requireStaff(); - const id = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - if (!id) redirect("/admin/theme"); - const theme = await getCustomTheme(id); - if (!theme) redirect("/admin/theme"); - try { - for (const [key, value] of Object.entries(theme.settings)) { - if (value) await writeSetting(key, value); - } - await writeSetting("theme_preset", theme.name); - siteSettings.reload(); - await logStaffActivity({ - staffId: staff.id, - action: "theme_preset", - description: `Applied custom theme "${theme.name}"`, - }); - revalidatePath("/", "layout"); - } catch { - // ignore - } - redirect(`/admin/theme?theme=${encodeURIComponent(theme.name)}`); + const staff = await requireStaff(); + const id = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + if (!id) redirect("/admin/theme"); + const theme = await getCustomTheme(id); + if (!theme) redirect("/admin/theme"); + try { + for (const [key, value] of Object.entries(theme.settings)) { + if (value) await writeSetting(key, value); + } + await writeSetting("theme_preset", theme.name); + siteSettings.reload(); + await logStaffActivity({ + staffId: staff.id, + action: "theme_preset", + description: `Applied custom theme "${theme.name}"`, + }); + revalidatePath("/", "layout"); + } catch { + // ignore + } + redirect(`/admin/theme?theme=${encodeURIComponent(theme.name)}`); } export async function renameCustomTheme(formData: FormData): Promise { - await requireStaff(); - const id = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - const name = String(formData.get("name") ?? "") - .normalize("NFC") - .trim(); - if (!id || !name) redirect("/admin/theme"); - const snapshot = await snapshotCurrentTheme(); - try { - await upsertCustomTheme(name, snapshot, id); - revalidatePath("/admin/theme"); - } catch { - // ignore - } - redirect("/admin/theme?renamed=1"); + await requireStaff(); + const id = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + const name = String(formData.get("name") ?? "") + .normalize("NFC") + .trim(); + if (!id || !name) redirect("/admin/theme"); + const snapshot = await snapshotCurrentTheme(); + try { + await upsertCustomTheme(name, snapshot, id); + revalidatePath("/admin/theme"); + } catch { + // ignore + } + redirect("/admin/theme?renamed=1"); } export async function deleteCustomTheme(formData: FormData): Promise { - await requireStaff(); - const id = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - if (!id) redirect("/admin/theme"); - try { - await deleteCustomThemeStore(id); - revalidatePath("/admin/theme"); - } catch { - // ignore - } - redirect("/admin/theme?deletedTheme=1"); + await requireStaff(); + const id = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + if (!id) redirect("/admin/theme"); + try { + await deleteCustomThemeStore(id); + revalidatePath("/admin/theme"); + } catch { + // ignore + } + redirect("/admin/theme?deletedTheme=1"); } diff --git a/src/actions/admin-user-edit.ts b/src/actions/admin-user-edit.ts index 131ffd75e1..3f410f598f 100644 --- a/src/actions/admin-user-edit.ts +++ b/src/actions/admin-user-edit.ts @@ -13,13 +13,13 @@ const DUCKETS_TYPE = 0; const DIAMONDS_TYPE = 5; function toInt(value: FormDataEntryValue | null, min = 0): number | null { - if (value == null) return null; - const raw = String(value).trim(); - if (raw === "") return null; - const n = Number(raw); - if (!Number.isFinite(n)) return null; - const i = Math.trunc(n); - return i < min ? min : i; + if (value == null) return null; + const raw = String(value).trim(); + if (raw === "") return null; + const n = Number(raw); + if (!Number.isFinite(n)) return null; + const i = Math.trunc(n); + return i < min ? min : i; } /** @@ -28,73 +28,73 @@ function toInt(value: FormDataEntryValue | null, min = 0): number | null { * user from the session and logs the action. emulator-owned users.id is Int. */ export async function updateUser(formData: FormData): Promise { - // Never trust the client: re-check staff inside the action. - const staff = await requireStaff(); + // Never trust the client: re-check staff inside the action. + const staff = await requireStaff(); - const userId = Number(formData.get("id")); - if (!Number.isInteger(userId) || userId <= 0) return; + const userId = Number(formData.get("id")); + if (!Number.isInteger(userId) || userId <= 0) return; - const existing = await prisma.user.findUnique({ - where: { id: userId }, - select: { id: true }, - }); - if (!existing) return; + const existing = await prisma.user.findUnique({ + where: { id: userId }, + select: { id: true }, + }); + if (!existing) return; - // users row — only existing, safe columns. - const mailRaw = String(formData.get("mail") ?? "") - .normalize("NFC") - .trim(); - const motto = String(formData.get("motto") ?? "") - .normalize("NFC") - .slice(0, 127); - const look = String(formData.get("look") ?? "") - .normalize("NFC") - .slice(0, 256); - const rank = toInt(formData.get("rank"), 1); - const credits = toInt(formData.get("credits"), 0); - const pixels = toInt(formData.get("pixels"), 0); - const points = toInt(formData.get("points"), 0); + // users row — only existing, safe columns. + const mailRaw = String(formData.get("mail") ?? "") + .normalize("NFC") + .trim(); + const motto = String(formData.get("motto") ?? "") + .normalize("NFC") + .slice(0, 127); + const look = String(formData.get("look") ?? "") + .normalize("NFC") + .slice(0, 256); + const rank = toInt(formData.get("rank"), 1); + const credits = toInt(formData.get("credits"), 0); + const pixels = toInt(formData.get("pixels"), 0); + const points = toInt(formData.get("points"), 0); - await prisma.user.update({ - where: { id: userId }, - data: { - mail: mailRaw === "" ? null : mailRaw.slice(0, 500), - motto, - look, - ...(rank != null ? { rank } : {}), - ...(credits != null ? { credits } : {}), - ...(pixels != null ? { pixels } : {}), - ...(points != null ? { points } : {}), - }, - }); + await prisma.user.update({ + where: { id: userId }, + data: { + mail: mailRaw === "" ? null : mailRaw.slice(0, 500), + motto, + look, + ...(rank != null ? { rank } : {}), + ...(credits != null ? { credits } : {}), + ...(pixels != null ? { pixels } : {}), + ...(points != null ? { points } : {}), + }, + }); - // users_currency — set exact balances for duckets / diamonds. - const duckets = toInt(formData.get("duckets"), 0); - const diamonds = toInt(formData.get("diamonds"), 0); - if (duckets != null) { - await prisma.usersCurrency.upsert({ - where: { userId_type: { userId, type: DUCKETS_TYPE } }, - update: { amount: duckets }, - create: { userId, type: DUCKETS_TYPE, amount: duckets }, - }); - } - if (diamonds != null) { - await prisma.usersCurrency.upsert({ - where: { userId_type: { userId, type: DIAMONDS_TYPE } }, - update: { amount: diamonds }, - create: { userId, type: DIAMONDS_TYPE, amount: diamonds }, - }); - } + // users_currency — set exact balances for duckets / diamonds. + const duckets = toInt(formData.get("duckets"), 0); + const diamonds = toInt(formData.get("diamonds"), 0); + if (duckets != null) { + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId, type: DUCKETS_TYPE } }, + update: { amount: duckets }, + create: { userId, type: DUCKETS_TYPE, amount: duckets }, + }); + } + if (diamonds != null) { + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId, type: DIAMONDS_TYPE } }, + update: { amount: diamonds }, + create: { userId, type: DIAMONDS_TYPE, amount: diamonds }, + }); + } - await logStaffActivity({ - staffId: staff.id, - action: "user_edit", - description: `Edited account fields of user #${userId}`, - targetType: "user", - targetId: userId, - }); + await logStaffActivity({ + staffId: staff.id, + action: "user_edit", + description: `Edited account fields of user #${userId}`, + targetType: "user", + targetId: userId, + }); - revalidatePath(`/admin/users/${userId}`); - revalidatePath(`/admin/users/${userId}/edit`); - redirect(`/admin/users/${userId}`); + revalidatePath(`/admin/users/${userId}`); + revalidatePath(`/admin/users/${userId}/edit`); + redirect(`/admin/users/${userId}`); } diff --git a/src/actions/admin-users.ts b/src/actions/admin-users.ts index 15b5ad79c9..bf3baf60a0 100644 --- a/src/actions/admin-users.ts +++ b/src/actions/admin-users.ts @@ -7,69 +7,79 @@ import { rcon } from "@/lib/services/rcon"; import { type CurrencyName, sendCurrency } from "@/lib/services/send-currency"; import { logStaffActivity } from "@/lib/services/staff-activity"; -const CURRENCIES: ReadonlySet = new Set(["credits", "duckets", "diamonds", "points"]); +const CURRENCIES: ReadonlySet = new Set([ + "credits", + "duckets", + "diamonds", + "points", +]); export async function giveCurrency(formData: FormData): Promise { - const staff = await requireStaffRateLimited(); - const userId = Number(formData.get("userId")); - const type = String(formData.get("type")); - const amount = Number(formData.get("amount")); - if (userId > 0 && amount > 0 && CURRENCIES.has(type)) { - await sendCurrency({ rcon, db: prisma }, userId, type as CurrencyName, amount); - await logStaffActivity({ - staffId: staff.id, - action: "give_currency", - description: `Gave ${amount} ${type} to user #${userId}`, - targetType: "user", - targetId: userId, - }); - } - revalidatePath(`/admin/users/${userId}`); + const staff = await requireStaffRateLimited(); + const userId = Number(formData.get("userId")); + const type = String(formData.get("type")); + const amount = Number(formData.get("amount")); + if (userId > 0 && amount > 0 && CURRENCIES.has(type)) { + await sendCurrency( + { rcon, db: prisma }, + userId, + type as CurrencyName, + amount, + ); + await logStaffActivity({ + staffId: staff.id, + action: "give_currency", + description: `Gave ${amount} ${type} to user #${userId}`, + targetType: "user", + targetId: userId, + }); + } + revalidatePath(`/admin/users/${userId}`); } export async function setMotto(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const motto = String(formData.get("motto") ?? "") - .normalize("NFC") - .slice(0, 127); - if (userId > 0) { - await prisma.user.update({ where: { id: userId }, data: { motto } }); - await rcon.setMotto(userId, motto); - } - revalidatePath(`/admin/users/${userId}`); + await requireStaff(); + const userId = Number(formData.get("userId")); + const motto = String(formData.get("motto") ?? "") + .normalize("NFC") + .slice(0, 127); + if (userId > 0) { + await prisma.user.update({ where: { id: userId }, data: { motto } }); + await rcon.setMotto(userId, motto); + } + revalidatePath(`/admin/users/${userId}`); } export async function setRank(formData: FormData): Promise { - const staff = await requireStaffRateLimited(); - const userId = Number(formData.get("userId")); - const rank = Number(formData.get("rank")); - if (userId > 0 && rank > 0) { - await prisma.user.update({ where: { id: userId }, data: { rank } }); - await rcon.setRank(userId, rank); - await logStaffActivity({ - staffId: staff.id, - action: "rank_change", - description: `Set rank of user #${userId} to ${rank}`, - targetType: "user", - targetId: userId, - }); - } - revalidatePath(`/admin/users/${userId}`); + const staff = await requireStaffRateLimited(); + const userId = Number(formData.get("userId")); + const rank = Number(formData.get("rank")); + if (userId > 0 && rank > 0) { + await prisma.user.update({ where: { id: userId }, data: { rank } }); + await rcon.setRank(userId, rank); + await logStaffActivity({ + staffId: staff.id, + action: "rank_change", + description: `Set rank of user #${userId} to ${rank}`, + targetType: "user", + targetId: userId, + }); + } + revalidatePath(`/admin/users/${userId}`); } export async function alertUser(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const message = String(formData.get("message") ?? "") - .normalize("NFC") - .trim(); - if (userId > 0 && message) await rcon.alertUser(userId, message); + await requireStaff(); + const userId = Number(formData.get("userId")); + const message = String(formData.get("message") ?? "") + .normalize("NFC") + .trim(); + if (userId > 0 && message) await rcon.alertUser(userId, message); } export async function disconnectUser(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const username = String(formData.get("username") ?? "").normalize("NFC"); - if (userId > 0) await rcon.disconnectUser(userId, username); + await requireStaff(); + const userId = Number(formData.get("userId")); + const username = String(formData.get("username") ?? "").normalize("NFC"); + if (userId > 0) await rcon.disconnectUser(userId, username); } diff --git a/src/actions/admin-vouchers.ts b/src/actions/admin-vouchers.ts index 4608aaf5e9..aec1794a08 100644 --- a/src/actions/admin-vouchers.ts +++ b/src/actions/admin-vouchers.ts @@ -2,67 +2,70 @@ import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; -import { prisma } from "@/lib/prisma"; import { formPositiveBigInt } from "@/lib/form-data"; +import { prisma } from "@/lib/prisma"; import { logServerError } from "@/lib/server-log"; export async function createVoucher(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const code = String(formData.get("code") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const amount = Number(formData.get("amount")); - const maxUsesRaw = Number(formData.get("maxUses")); - const maxUses = Number.isFinite(maxUsesRaw) && maxUsesRaw > 0 ? Math.floor(maxUsesRaw) : 1; + const code = String(formData.get("code") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const amount = Number(formData.get("amount")); + const maxUsesRaw = Number(formData.get("maxUses")); + const maxUses = + Number.isFinite(maxUsesRaw) && maxUsesRaw > 0 ? Math.floor(maxUsesRaw) : 1; - if (!code || !(amount > 0)) return; + if (!code || !(amount > 0)) return; - const expiresRaw = String(formData.get("expiresAt") ?? "") - .normalize("NFC") - .trim(); - let expiresAt: Date | null = null; - if (expiresRaw) { - const parsed = new Date(expiresRaw); - if (!Number.isNaN(parsed.getTime())) expiresAt = parsed; - } + const expiresRaw = String(formData.get("expiresAt") ?? "") + .normalize("NFC") + .trim(); + let expiresAt: Date | null = null; + if (expiresRaw) { + const parsed = new Date(expiresRaw); + if (!Number.isNaN(parsed.getTime())) expiresAt = parsed; + } - const now = new Date(); + const now = new Date(); - try { - await prisma.websiteShopVouchers.create({ - data: { - code, - amount: Math.floor(amount), - maxUses, - useCount: 0, - expiresAt, - createdAt: now, - updatedAt: now, - }, - }); - } catch (error) { - logServerError("admin.voucher_create_failed", error); - // Unique constraint on `code` (or DB unavailable) — swallow and re-render. - return; - } + try { + await prisma.websiteShopVouchers.create({ + data: { + code, + amount: Math.floor(amount), + maxUses, + useCount: 0, + expiresAt, + createdAt: now, + updatedAt: now, + }, + }); + } catch (error) { + logServerError("admin.voucher_create_failed", error); + // Unique constraint on `code` (or DB unavailable) — swallow and re-render. + return; + } - revalidatePath("/admin/vouchers"); + revalidatePath("/admin/vouchers"); } export async function deleteVoucher(formData: FormData): Promise { - await requireStaff(); + await requireStaff(); - const id = formPositiveBigInt(formData, "id"); - if (!id) return; + const id = formPositiveBigInt(formData, "id"); + if (!id) return; - try { - await prisma.websiteShopVouchers.delete({ where: { id } }); - } catch (error) { - logServerError("admin.voucher_delete_failed", error, { voucherId: String(id) }); - return; - } + try { + await prisma.websiteShopVouchers.delete({ where: { id } }); + } catch (error) { + logServerError("admin.voucher_delete_failed", error, { + voucherId: String(id), + }); + return; + } - revalidatePath("/admin/vouchers"); + revalidatePath("/admin/vouchers"); } diff --git a/src/actions/admin-vpn.ts b/src/actions/admin-vpn.ts index 1c5111a589..93e4caaeb8 100644 --- a/src/actions/admin-vpn.ts +++ b/src/actions/admin-vpn.ts @@ -15,67 +15,75 @@ import { logStaffActivity } from "@/lib/services/staff-activity"; const ALLOWED_PROVIDERS = new Set(["none", "proxycheck", "ipqualityscore"]); /** Upsert one website_settings key with a stable housekeeping comment. */ -async function writeSetting(key: string, value: string, comment: string): Promise { - await prisma.websiteSetting.upsert({ - where: { key }, - update: { value }, - create: { key, value, comment }, - }); +async function writeSetting( + key: string, + value: string, + comment: string, +): Promise { + await prisma.websiteSetting.upsert({ + where: { key }, + update: { value }, + create: { key, value, comment }, + }); } export async function saveVpn(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - // Toggle: an unchecked checkbox submits nothing, so absence === disabled. - const enabled = - String(formData.get("vpn_block_enabled") ?? "") - .normalize("NFC") - .trim() !== ""; + // Toggle: an unchecked checkbox submits nothing, so absence === disabled. + const enabled = + String(formData.get("vpn_block_enabled") ?? "") + .normalize("NFC") + .trim() !== ""; - const providerRaw = String(formData.get("vpn_provider") ?? "") - .normalize("NFC") - .trim() - .toLowerCase(); - const provider = ALLOWED_PROVIDERS.has(providerRaw) ? providerRaw : "none"; + const providerRaw = String(formData.get("vpn_provider") ?? "") + .normalize("NFC") + .trim() + .toLowerCase(); + const provider = ALLOWED_PROVIDERS.has(providerRaw) ? providerRaw : "none"; - const apiKey = String(formData.get("vpn_api_key") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - const blockMessage = String(formData.get("vpn_block_message") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); + const apiKey = String(formData.get("vpn_api_key") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + const blockMessage = String(formData.get("vpn_block_message") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); - try { - await writeSetting( - "vpn_block_enabled", - enabled ? "1" : "0", - "Block registrations from detected VPN/proxy IPs (0=no, 1=yes)", - ); - await writeSetting( - "vpn_provider", - provider, - "VPN/proxy detection provider (none/proxycheck/ipqualityscore)", - ); - await writeSetting("vpn_api_key", apiKey, "API key for the VPN/proxy detection provider"); - await writeSetting( - "vpn_block_message", - blockMessage, - "Message shown to users blocked for using a VPN/proxy", - ); + try { + await writeSetting( + "vpn_block_enabled", + enabled ? "1" : "0", + "Block registrations from detected VPN/proxy IPs (0=no, 1=yes)", + ); + await writeSetting( + "vpn_provider", + provider, + "VPN/proxy detection provider (none/proxycheck/ipqualityscore)", + ); + await writeSetting( + "vpn_api_key", + apiKey, + "API key for the VPN/proxy detection provider", + ); + await writeSetting( + "vpn_block_message", + blockMessage, + "Message shown to users blocked for using a VPN/proxy", + ); - siteSettings.reload(); - await logStaffActivity({ - staffId: staff.id, - action: "vpn_update", - description: `Updated VPN/proxy detection (block=${enabled ? "on" : "off"}, provider=${provider})`, - }); - revalidatePath("/admin/vpn"); - } catch { - // DB unavailable — fail soft so the action does not throw; the page - // re-renders the current (stored) state. - } + siteSettings.reload(); + await logStaffActivity({ + staffId: staff.id, + action: "vpn_update", + description: `Updated VPN/proxy detection (block=${enabled ? "on" : "off"}, provider=${provider})`, + }); + revalidatePath("/admin/vpn"); + } catch { + // DB unavailable — fail soft so the action does not throw; the page + // re-renders the current (stored) state. + } - redirect("/admin/vpn?saved=1"); + redirect("/admin/vpn?saved=1"); } diff --git a/src/actions/admin-wordfilter.ts b/src/actions/admin-wordfilter.ts index 63eb8973c9..f53bdd6a36 100644 --- a/src/actions/admin-wordfilter.ts +++ b/src/actions/admin-wordfilter.ts @@ -6,32 +6,32 @@ import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; export async function addWord(formData: FormData): Promise { - await requireStaff(); - const word = String(formData.get("word") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!word) return; + await requireStaff(); + const word = String(formData.get("word") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!word) return; - try { - await prisma.websiteWordfilter.create({ data: { word } }); - await rcon.updateWordFilter(); - } catch { - // ignore (e.g. duplicate word) — page re-renders current state - } - revalidatePath("/admin/wordfilter"); + try { + await prisma.websiteWordfilter.create({ data: { word } }); + await rcon.updateWordFilter(); + } catch { + // ignore (e.g. duplicate word) — page re-renders current state + } + revalidatePath("/admin/wordfilter"); } export async function deleteWord(formData: FormData): Promise { - await requireStaff(); - const raw = String(formData.get("id") ?? "").normalize("NFC"); - if (!raw) return; + await requireStaff(); + const raw = String(formData.get("id") ?? "").normalize("NFC"); + if (!raw) return; - try { - await prisma.websiteWordfilter.delete({ where: { id: BigInt(raw) } }); - await rcon.updateWordFilter(); - } catch { - // ignore (e.g. already removed) - } - revalidatePath("/admin/wordfilter"); + try { + await prisma.websiteWordfilter.delete({ where: { id: BigInt(raw) } }); + await rcon.updateWordFilter(); + } catch { + // ignore (e.g. already removed) + } + revalidatePath("/admin/wordfilter"); } diff --git a/src/actions/admin-writeable-boxes.ts b/src/actions/admin-writeable-boxes.ts index 4f3756fe15..178fc77b70 100644 --- a/src/actions/admin-writeable-boxes.ts +++ b/src/actions/admin-writeable-boxes.ts @@ -11,163 +11,165 @@ import { logStaffActivity } from "@/lib/services/staff-activity"; /** Parse a non-negative Int form value, falling back to 0. */ function reqInt(formData: FormData, key: string): number { - const raw = String(formData.get(key) ?? "") - .normalize("NFC") - .trim(); - if (raw === "") return 0; - const n = Number(raw); - if (!Number.isFinite(n) || n < 0) return 0; - return Math.floor(n); + const raw = String(formData.get(key) ?? "") + .normalize("NFC") + .trim(); + if (raw === "") return 0; + const n = Number(raw); + if (!Number.isFinite(n) || n < 0) return 0; + return Math.floor(n); } /** Parse the BigInt `id` form value, returning null when blank/invalid. */ function parseId(formData: FormData): bigint | null { - const raw = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - if (!raw) return null; - try { - return BigInt(raw); - } catch { - return null; - } + const raw = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + if (!raw) return null; + try { + return BigInt(raw); + } catch { + return null; + } } function revalidate(): void { - revalidatePath("/admin/writeable-boxes"); - // Active boxes render on the public home page (root layout). - revalidatePath("/", "layout"); + revalidatePath("/admin/writeable-boxes"); + // Active boxes render on the public home page (root layout). + revalidatePath("/", "layout"); } export async function createBox(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const title = String(formData.get("title") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!title) return; + const title = String(formData.get("title") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!title) return; - const now = new Date(); - try { - const created = await prisma.websiteWriteableBoxes.create({ - data: { - title, - icon: - String(formData.get("icon") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255) || null, - content: String(formData.get("content") ?? "").normalize("NFC"), - position: reqInt(formData, "position"), - isActive: String(formData.get("isActive") ?? "").normalize("NFC") === "1", - createdAt: now, - updatedAt: now, - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "writeable_box_create", - description: `Created writeable box "${title}" (#${created.id})`, - targetType: "writeable_box", - targetId: Number(created.id), - }); - } catch { - // DB unavailable — swallow and re-render. - return; - } + const now = new Date(); + try { + const created = await prisma.websiteWriteableBoxes.create({ + data: { + title, + icon: + String(formData.get("icon") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255) || null, + content: String(formData.get("content") ?? "").normalize("NFC"), + position: reqInt(formData, "position"), + isActive: + String(formData.get("isActive") ?? "").normalize("NFC") === "1", + createdAt: now, + updatedAt: now, + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "writeable_box_create", + description: `Created writeable box "${title}" (#${created.id})`, + targetType: "writeable_box", + targetId: Number(created.id), + }); + } catch { + // DB unavailable — swallow and re-render. + return; + } - revalidate(); + revalidate(); } export async function updateBox(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = parseId(formData); - if (id == null) return; + const id = parseId(formData); + if (id == null) return; - const title = String(formData.get("title") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255); - if (!title) return; + const title = String(formData.get("title") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255); + if (!title) return; - try { - await prisma.websiteWriteableBoxes.update({ - where: { id }, - data: { - title, - icon: - String(formData.get("icon") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255) || null, - content: String(formData.get("content") ?? "").normalize("NFC"), - position: reqInt(formData, "position"), - isActive: String(formData.get("isActive") ?? "").normalize("NFC") === "1", - updatedAt: new Date(), - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "writeable_box_update", - description: `Updated writeable box #${id} ("${title}")`, - targetType: "writeable_box", - targetId: Number(id), - }); - } catch { - return; - } + try { + await prisma.websiteWriteableBoxes.update({ + where: { id }, + data: { + title, + icon: + String(formData.get("icon") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255) || null, + content: String(formData.get("content") ?? "").normalize("NFC"), + position: reqInt(formData, "position"), + isActive: + String(formData.get("isActive") ?? "").normalize("NFC") === "1", + updatedAt: new Date(), + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "writeable_box_update", + description: `Updated writeable box #${id} ("${title}")`, + targetType: "writeable_box", + targetId: Number(id), + }); + } catch { + return; + } - revalidate(); + revalidate(); } export async function deleteBox(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = parseId(formData); - if (id == null) return; + const id = parseId(formData); + if (id == null) return; - try { - await prisma.websiteWriteableBoxes.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "writeable_box_delete", - description: `Deleted writeable box #${id}`, - targetType: "writeable_box", - targetId: Number(id), - }); - } catch { - return; - } + try { + await prisma.websiteWriteableBoxes.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "writeable_box_delete", + description: `Deleted writeable box #${id}`, + targetType: "writeable_box", + targetId: Number(id), + }); + } catch { + return; + } - revalidate(); + revalidate(); } export async function toggleBox(formData: FormData): Promise { - const staff = await requireStaff(); + const staff = await requireStaff(); - const id = parseId(formData); - if (id == null) return; + const id = parseId(formData); + if (id == null) return; - // `next` carries the desired state ("1" to activate, anything else to hide). - const next = String(formData.get("next") ?? "").normalize("NFC") === "1"; + // `next` carries the desired state ("1" to activate, anything else to hide). + const next = String(formData.get("next") ?? "").normalize("NFC") === "1"; - try { - await prisma.websiteWriteableBoxes.update({ - where: { id }, - data: { isActive: next, updatedAt: new Date() }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "writeable_box_toggle", - description: `${next ? "Activated" : "Hid"} writeable box #${id}`, - targetType: "writeable_box", - targetId: Number(id), - }); - } catch { - return; - } + try { + await prisma.websiteWriteableBoxes.update({ + where: { id }, + data: { isActive: next, updatedAt: new Date() }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "writeable_box_toggle", + description: `${next ? "Activated" : "Hid"} writeable box #${id}`, + targetType: "writeable_box", + targetId: Number(id), + }); + } catch { + return; + } - revalidate(); + revalidate(); } diff --git a/src/actions/applications.ts b/src/actions/applications.ts index 277b42fab7..63cd361a03 100644 --- a/src/actions/applications.ts +++ b/src/actions/applications.ts @@ -20,38 +20,38 @@ const CONTENT_MAX = 5000; * - content must be at least 10 characters. */ export async function applyStaff(formData: FormData): Promise { - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - // rank_id comes from the open position's permission_id (an Int in the schema). - const rankId = Number(formData.get("rankId")); - if (!Number.isInteger(rankId) || rankId <= 0) return; + // rank_id comes from the open position's permission_id (an Int in the schema). + const rankId = Number(formData.get("rankId")); + if (!Number.isInteger(rankId) || rankId <= 0) return; - const content = String(formData.get("content") ?? "") - .normalize("NFC") - .trim() - .slice(0, CONTENT_MAX); - if (content.length < CONTENT_MIN) return; + const content = String(formData.get("content") ?? "") + .normalize("NFC") + .trim() + .slice(0, CONTENT_MAX); + if (content.length < CONTENT_MIN) return; - try { - // Block duplicate applications for the same rank (AtomCMS hasAppliedForPosition). - const existing = await prisma.websiteStaffApplications.findFirst({ - where: { userId, rankId }, - select: { id: true }, - }); - if (existing) return; + try { + // Block duplicate applications for the same rank (AtomCMS hasAppliedForPosition). + const existing = await prisma.websiteStaffApplications.findFirst({ + where: { userId, rankId }, + select: { id: true }, + }); + if (existing) return; - const now = new Date(); - await prisma.websiteStaffApplications.create({ - data: { userId, rankId, content, createdAt: now, updatedAt: now }, - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + const now = new Date(); + await prisma.websiteStaffApplications.create({ + data: { userId, rankId, content, createdAt: now, updatedAt: now }, + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - revalidatePath("/apply/staff"); + revalidatePath("/apply/staff"); } /** @@ -64,35 +64,35 @@ export async function applyStaff(formData: FormData): Promise { * may only apply once per team. */ export async function applyTeam(formData: FormData): Promise { - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - // website_teams.id is a BigInt; rank_id on the application is an Int. The team - // id is the application's rank flag. - const rankId = Number(formData.get("teamId")); - if (!Number.isInteger(rankId) || rankId <= 0) return; + // website_teams.id is a BigInt; rank_id on the application is an Int. The team + // id is the application's rank flag. + const rankId = Number(formData.get("teamId")); + if (!Number.isInteger(rankId) || rankId <= 0) return; - const content = String(formData.get("content") ?? "") - .normalize("NFC") - .trim() - .slice(0, CONTENT_MAX); - if (content.length < CONTENT_MIN) return; + const content = String(formData.get("content") ?? "") + .normalize("NFC") + .trim() + .slice(0, CONTENT_MAX); + if (content.length < CONTENT_MIN) return; - try { - const existing = await prisma.websiteStaffApplications.findFirst({ - where: { userId, rankId }, - select: { id: true }, - }); - if (existing) return; + try { + const existing = await prisma.websiteStaffApplications.findFirst({ + where: { userId, rankId }, + select: { id: true }, + }); + if (existing) return; - const now = new Date(); - await prisma.websiteStaffApplications.create({ - data: { userId, rankId, content, createdAt: now, updatedAt: now }, - }); - } catch { - return; - } + const now = new Date(); + await prisma.websiteStaffApplications.create({ + data: { userId, rankId, content, createdAt: now, updatedAt: now }, + }); + } catch { + return; + } - revalidatePath("/apply/team"); + revalidatePath("/apply/team"); } diff --git a/src/actions/article-comments.ts b/src/actions/article-comments.ts index 899159198b..6605e37e6a 100644 --- a/src/actions/article-comments.ts +++ b/src/actions/article-comments.ts @@ -15,57 +15,57 @@ const COMMENT_MAX = 255; * form and is validated as a BigInt (website_articles.id is UNSIGNED BIGINT). */ export async function postComment(formData: FormData): Promise { - const session = await auth(); - if (!session?.user?.id) return; + const session = await auth(); + if (!session?.user?.id) return; - const userId = Number(session.user.id); - if (!Number.isFinite(userId)) return; + const userId = Number(session.user.id); + if (!Number.isFinite(userId)) return; - const comment = String(formData.get("comment") ?? "") - .normalize("NFC") - .trim() - .slice(0, COMMENT_MAX); - if (!comment) return; + const comment = String(formData.get("comment") ?? "") + .normalize("NFC") + .trim() + .slice(0, COMMENT_MAX); + if (!comment) return; - // Block filtered/AI-flagged content before it touches the DB (fail-open). - if (!(await isAllowed(comment)).ok) return; + // Block filtered/AI-flagged content before it touches the DB (fail-open). + if (!(await isAllowed(comment)).ok) return; - const articleIdRaw = String(formData.get("articleId") ?? "") - .normalize("NFC") - .trim(); - if (!/^\d+$/.test(articleIdRaw)) return; + const articleIdRaw = String(formData.get("articleId") ?? "") + .normalize("NFC") + .trim(); + if (!/^\d+$/.test(articleIdRaw)) return; - let articleId: bigint; - try { - articleId = BigInt(articleIdRaw); - } catch { - return; - } + let articleId: bigint; + try { + articleId = BigInt(articleIdRaw); + } catch { + return; + } - let slug: string | null; - try { - // Confirm the article exists (and grab its slug for revalidation). - const article = await prisma.websiteArticles.findUnique({ - where: { id: articleId }, - select: { slug: true }, - }); - if (!article) return; - slug = article.slug; + let slug: string | null; + try { + // Confirm the article exists (and grab its slug for revalidation). + const article = await prisma.websiteArticles.findUnique({ + where: { id: articleId }, + select: { slug: true }, + }); + if (!article) return; + slug = article.slug; - const now = new Date(); - await prisma.websiteArticleComments.create({ - data: { - articleId, - userId, - comment, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + const now = new Date(); + await prisma.websiteArticleComments.create({ + data: { + articleId, + userId, + comment, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - if (slug) revalidatePath(`/news/${slug}`); + if (slug) revalidatePath(`/news/${slug}`); } diff --git a/src/actions/article-reactions.ts b/src/actions/article-reactions.ts index 94fb147a04..9c0e7799a2 100644 --- a/src/actions/article-reactions.ts +++ b/src/actions/article-reactions.ts @@ -25,75 +25,75 @@ const ALLOWED_REACTIONS = new Set(["like", "love", "wow"]); * unique key, so we resolve the existing row with findFirst rather than upsert. */ export async function toggleReaction(formData: FormData): Promise { - const session = await auth(); - if (!session?.user?.id) return; + const session = await auth(); + if (!session?.user?.id) return; - const userId = Number(session.user.id); - if (!Number.isFinite(userId)) return; + const userId = Number(session.user.id); + if (!Number.isFinite(userId)) return; - const reaction = String(formData.get("reaction") ?? "") - .normalize("NFC") - .trim() - .toLowerCase(); - if (!ALLOWED_REACTIONS.has(reaction)) return; + const reaction = String(formData.get("reaction") ?? "") + .normalize("NFC") + .trim() + .toLowerCase(); + if (!ALLOWED_REACTIONS.has(reaction)) return; - const articleIdRaw = String(formData.get("articleId") ?? "") - .normalize("NFC") - .trim(); - if (!/^\d+$/.test(articleIdRaw)) return; + const articleIdRaw = String(formData.get("articleId") ?? "") + .normalize("NFC") + .trim(); + if (!/^\d+$/.test(articleIdRaw)) return; - let articleId: bigint; - try { - articleId = BigInt(articleIdRaw); - } catch { - return; - } + let articleId: bigint; + try { + articleId = BigInt(articleIdRaw); + } catch { + return; + } - let slug: string | null; - try { - // Confirm the article exists (and grab its slug for revalidation). - const article = await prisma.websiteArticles.findUnique({ - where: { id: articleId }, - select: { slug: true }, - }); - if (!article) return; - slug = article.slug; + let slug: string | null; + try { + // Confirm the article exists (and grab its slug for revalidation). + const article = await prisma.websiteArticles.findUnique({ + where: { id: articleId }, + select: { slug: true }, + }); + if (!article) return; + slug = article.slug; - // The user's current row for THIS reaction on THIS article, if any. - const existing = await prisma.websiteArticleReactions.findFirst({ - where: { userId, articleId, reaction }, - select: { id: true, active: true }, - }); + // The user's current row for THIS reaction on THIS article, if any. + const existing = await prisma.websiteArticleReactions.findFirst({ + where: { userId, articleId, reaction }, + select: { id: true, active: true }, + }); - if (existing?.active) { - // Already reacting with this exact reaction -> un-vote (deactivate it). - await prisma.websiteArticleReactions.update({ - where: { id: existing.id }, - data: { active: false }, - }); - } else { - // Switching to (or first-time picking) this reaction: clear any other - // active reaction by this user on this article, then activate this one. - await prisma.websiteArticleReactions.updateMany({ - where: { userId, articleId, active: true }, - data: { active: false }, - }); + if (existing?.active) { + // Already reacting with this exact reaction -> un-vote (deactivate it). + await prisma.websiteArticleReactions.update({ + where: { id: existing.id }, + data: { active: false }, + }); + } else { + // Switching to (or first-time picking) this reaction: clear any other + // active reaction by this user on this article, then activate this one. + await prisma.websiteArticleReactions.updateMany({ + where: { userId, articleId, active: true }, + data: { active: false }, + }); - if (existing) { - await prisma.websiteArticleReactions.update({ - where: { id: existing.id }, - data: { active: true }, - }); - } else { - await prisma.websiteArticleReactions.create({ - data: { userId, articleId, reaction, active: true }, - }); - } - } - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + if (existing) { + await prisma.websiteArticleReactions.update({ + where: { id: existing.id }, + data: { active: true }, + }); + } else { + await prisma.websiteArticleReactions.create({ + data: { userId, articleId, reaction, active: true }, + }); + } + } + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - if (slug) revalidatePath(`/news/${slug}`); + if (slug) revalidatePath(`/news/${slug}`); } diff --git a/src/actions/auth-precheck.ts b/src/actions/auth-precheck.ts index 3440bb3c3f..7ef4154d8c 100644 --- a/src/actions/auth-precheck.ts +++ b/src/actions/auth-precheck.ts @@ -1,9 +1,9 @@ "use server"; +import { env } from "@/env"; import { checkLogin } from "@/lib/auth/password"; import { prisma } from "@/lib/prisma"; import { clientIp, rateLimit } from "@/lib/rate-limit"; -import { env } from "@/env"; export type PrecheckResult = "ok" | "invalid" | "twofactor"; @@ -11,36 +11,44 @@ export type PrecheckResult = "ok" | "invalid" | "twofactor"; * Validates username+password WITHOUT creating a session, and reports whether a * TOTP code is still required. Lets the login form do the two-step 2FA flow. */ -export async function precheckLogin(username: string, password: string): Promise { - const u = String(username ?? "") - .normalize("NFC") - .trim(); - const p = String(password ?? ""); - if (!u || !p) return "invalid"; +export async function precheckLogin( + username: string, + password: string, +): Promise { + const u = String(username ?? "") + .normalize("NFC") + .trim(); + const p = String(password ?? ""); + if (!u || !p) return "invalid"; - if (!(await rateLimit(`precheck:${await clientIp()}`, 10, 5 * 60_000)).ok) return "invalid"; + if (!(await rateLimit(`precheck:${await clientIp()}`, 10, 5 * 60_000)).ok) + return "invalid"; - let user: { password: string; twoFactorConfirmedAt: Date | null } | null; - try { - user = await prisma.user.findUnique({ - where: { username: u }, - select: { password: true, twoFactorConfirmedAt: true }, - }); - } catch { - return "invalid"; - } - if (!user) { - // Prevent timing-based enumeration: always run a dummy hash check. - await checkLogin(p, "$2y$12$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcd", { - convertPasswords: false, - }); - return "invalid"; - } + let user: { password: string; twoFactorConfirmedAt: Date | null } | null; + try { + user = await prisma.user.findUnique({ + where: { username: u }, + select: { password: true, twoFactorConfirmedAt: true }, + }); + } catch { + return "invalid"; + } + if (!user) { + // Prevent timing-based enumeration: always run a dummy hash check. + await checkLogin( + p, + "$2y$12$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcd", + { + convertPasswords: false, + }, + ); + return "invalid"; + } - const res = await checkLogin(p, user.password, { - convertPasswords: env.CONVERT_PASSWORDS, - }); - if (!res.valid) return "invalid"; + const res = await checkLogin(p, user.password, { + convertPasswords: env.CONVERT_PASSWORDS, + }); + if (!res.valid) return "invalid"; - return user.twoFactorConfirmedAt ? "twofactor" : "ok"; + return user.twoFactorConfirmedAt ? "twofactor" : "ok"; } diff --git a/src/actions/badges.ts b/src/actions/badges.ts index 92598924ce..9a6c90dc56 100644 --- a/src/actions/badges.ts +++ b/src/actions/badges.ts @@ -5,27 +5,38 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; export async function getBadgeData({ code }: { code: string }) { - await requireStaff(); - const badge = await prisma.websiteBadges.findUnique({ - where: { badgeKey: code }, - select: { badgeName: true, badgeDescription: true }, - }); - if (!badge) return { ok: false as const, data: null }; - return { ok: true as const, data: { name: badge.badgeName, desc: badge.badgeDescription } }; + await requireStaff(); + const badge = await prisma.websiteBadges.findUnique({ + where: { badgeKey: code }, + select: { badgeName: true, badgeDescription: true }, + }); + if (!badge) return { ok: false as const, data: null }; + return { + ok: true as const, + data: { name: badge.badgeName, desc: badge.badgeDescription }, + }; } -export async function updateBadge({ code, name, desc }: { code: string; name: string; desc: string }) { - await requireStaff(); - await prisma.websiteBadges.upsert({ - where: { badgeKey: code }, - update: { badgeName: name, badgeDescription: desc, updatedAt: new Date() }, - create: { - badgeKey: code, - badgeName: name, - badgeDescription: desc, - createdAt: new Date(), - updatedAt: new Date(), - }, - }); - revalidatePath("/admin/import/badges"); +export async function updateBadge({ + code, + name, + desc, +}: { + code: string; + name: string; + desc: string; +}) { + await requireStaff(); + await prisma.websiteBadges.upsert({ + where: { badgeKey: code }, + update: { badgeName: name, badgeDescription: desc, updatedAt: new Date() }, + create: { + badgeKey: code, + badgeName: name, + badgeDescription: desc, + createdAt: new Date(), + updatedAt: new Date(), + }, + }); + revalidatePath("/admin/import/badges"); } diff --git a/src/actions/banners.ts b/src/actions/banners.ts index b324ba5cfb..252517dc86 100644 --- a/src/actions/banners.ts +++ b/src/actions/banners.ts @@ -8,63 +8,65 @@ import { ActionError, actionOk } from "@/lib/safe-action-shared"; import { logAudit } from "@/lib/services/audit"; const bannerSchema = z.object({ - title: z.string().min(1).max(255), - subtitle: z.string().max(500).optional().default(""), - image: z.string().max(500), - link: z.string().max(500).optional().default(""), - color: z.string().max(20).optional().default(""), - isActive: z.coerce.number().int().min(0).max(1).default(1), - sortOrder: z.coerce.number().int().min(0).default(0), - startDate: z.string().max(50).nullable().optional(), - endDate: z.string().max(50).nullable().optional(), + title: z.string().min(1).max(255), + subtitle: z.string().max(500).optional().default(""), + image: z.string().max(500), + link: z.string().max(500).optional().default(""), + color: z.string().max(20).optional().default(""), + isActive: z.coerce.number().int().min(0).max(1).default(1), + sortOrder: z.coerce.number().int().min(0).default(0), + startDate: z.string().max(50).nullable().optional(), + endDate: z.string().max(50).nullable().optional(), }); export const createBanner = adminAction( - { permission: PERMS.BANNERS_EDIT, schema: bannerSchema }, - async (ctx) => { - const banner = await prisma.websiteBanner.create({ data: ctx.data }); - logAudit({ - userId: ctx.session.user.id, - action: "banner_create", - target: "WebsiteBanner", - targetId: banner.id, - after: { title: banner.title }, - }); - return actionOk({ id: banner.id }); - }, + { permission: PERMS.BANNERS_EDIT, schema: bannerSchema }, + async (ctx) => { + const banner = await prisma.websiteBanner.create({ data: ctx.data }); + logAudit({ + userId: ctx.session.user.id, + action: "banner_create", + target: "WebsiteBanner", + targetId: banner.id, + after: { title: banner.title }, + }); + return actionOk({ id: banner.id }); + }, ); -const updateBannerInput = bannerSchema.partial().extend({ id: z.coerce.number().int().positive() }); +const updateBannerInput = bannerSchema + .partial() + .extend({ id: z.coerce.number().int().positive() }); export const updateBanner = adminAction( - { permission: PERMS.BANNERS_EDIT, schema: updateBannerInput }, - async (ctx) => { - const { id, ...data } = ctx.data; - const existing = await prisma.websiteBanner.findUnique({ where: { id } }); - if (!existing) throw new ActionError("Banner not found"); - await prisma.websiteBanner.update({ where: { id }, data }); - logAudit({ - userId: ctx.session.user.id, - action: "banner_update", - target: "WebsiteBanner", - targetId: id, - }); - return actionOk({ id }); - }, + { permission: PERMS.BANNERS_EDIT, schema: updateBannerInput }, + async (ctx) => { + const { id, ...data } = ctx.data; + const existing = await prisma.websiteBanner.findUnique({ where: { id } }); + if (!existing) throw new ActionError("Banner not found"); + await prisma.websiteBanner.update({ where: { id }, data }); + logAudit({ + userId: ctx.session.user.id, + action: "banner_update", + target: "WebsiteBanner", + targetId: id, + }); + return actionOk({ id }); + }, ); const deleteBannerInput = z.object({ id: z.coerce.number().int().positive() }); export const deleteBanner = adminAction( - { permission: PERMS.BANNERS_EDIT, schema: deleteBannerInput }, - async (ctx) => { - await prisma.websiteBanner.delete({ where: { id: ctx.data.id } }); - logAudit({ - userId: ctx.session.user.id, - action: "banner_delete", - target: "WebsiteBanner", - targetId: ctx.data.id, - }); - return actionOk(); - }, + { permission: PERMS.BANNERS_EDIT, schema: deleteBannerInput }, + async (ctx) => { + await prisma.websiteBanner.delete({ where: { id: ctx.data.id } }); + logAudit({ + userId: ctx.session.user.id, + action: "banner_delete", + target: "WebsiteBanner", + targetId: ctx.data.id, + }); + return actionOk(); + }, ); diff --git a/src/actions/bulk-users.ts b/src/actions/bulk-users.ts index d3402a42d5..92739068f1 100644 --- a/src/actions/bulk-users.ts +++ b/src/actions/bulk-users.ts @@ -1,159 +1,182 @@ "use server"; -import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; +import type { ActionResult } from "@/lib/safe-action-shared"; import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; -import type { ActionResult } from "@/lib/safe-action-shared"; export async function bulkUnban({ - userIds, + userIds, }: { - userIds: number[]; + userIds: number[]; }): Promise> { - const staff = await requireStaff(); - const result = await prisma.ban.deleteMany({ where: { userId: { in: userIds } } }); - await logStaffActivity({ - staffId: staff.id, - action: "bulk_unban", - description: `Unbanned ${result.count} user(s)`, - targetType: "user", - }); - return { ok: true as const, data: { unbanned: result.count, total: userIds.length } }; + const staff = await requireStaff(); + const result = await prisma.ban.deleteMany({ + where: { userId: { in: userIds } }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "bulk_unban", + description: `Unbanned ${result.count} user(s)`, + targetType: "user", + }); + return { + ok: true as const, + data: { unbanned: result.count, total: userIds.length }, + }; } export async function bulkBan({ - userIds, - reason, - duration, + userIds, + reason, + duration, }: { - userIds: number[]; - reason: string; - duration: number; + userIds: number[]; + reason: string; + duration: number; }): Promise> { - const staff = await requireStaff(); - const now = Math.floor(Date.now() / 1000); - let banned = 0; + const staff = await requireStaff(); + const now = Math.floor(Date.now() / 1000); + let banned = 0; - for (const userId of userIds) { - try { - await prisma.ban.create({ - data: { - userId, - ip: "", - machineId: "", - userStaffId: staff.id, - timestamp: now, - banExpire: duration > 0 ? now + duration : 0, - banReason: reason, - type: "account", - }, - }); - banned++; - } catch { - // skip duplicates - } - } + for (const userId of userIds) { + try { + await prisma.ban.create({ + data: { + userId, + ip: "", + machineId: "", + userStaffId: staff.id, + timestamp: now, + banExpire: duration > 0 ? now + duration : 0, + banReason: reason, + type: "account", + }, + }); + banned++; + } catch { + // skip duplicates + } + } - await logStaffActivity({ - staffId: staff.id, - action: "bulk_ban", - description: `Banned ${banned} user(s)`, - targetType: "user", - }); - return { ok: true as const, data: { banned } }; + await logStaffActivity({ + staffId: staff.id, + action: "bulk_ban", + description: `Banned ${banned} user(s)`, + targetType: "user", + }); + return { ok: true as const, data: { banned } }; } export async function bulkGiveCurrency({ - userIds, - amount, - type, + userIds, + amount, + type, }: { - userIds: number[]; - amount: number; - type: "credits" | "pixels" | "points"; + userIds: number[]; + amount: number; + type: "credits" | "pixels" | "points"; }): Promise< - ActionResult<{ given: number; total: number; failedIds: Array<{ userId: number; reason: string }> }> + ActionResult<{ + given: number; + total: number; + failedIds: Array<{ userId: number; reason: string }>; + }> > { - const staff = await requireStaff(); - let given = 0; - const failedIds: Array<{ userId: number; reason: string }> = []; + const staff = await requireStaff(); + let given = 0; + const failedIds: Array<{ userId: number; reason: string }> = []; - for (const userId of userIds) { - try { - if (type === "credits") { - await prisma.user.update({ where: { id: userId }, data: { credits: { increment: amount } } }); - await rcon.giveCredits(userId, amount); - } else if (type === "pixels") { - await prisma.usersCurrency.upsert({ - where: { userId_type: { userId, type: 0 } }, - update: { amount: { increment: amount } }, - create: { userId, type: 0, amount }, - }); - await rcon.giveDuckets(userId, amount); - } else if (type === "points") { - await prisma.usersCurrency.upsert({ - where: { userId_type: { userId, type: 101 } }, - update: { amount: { increment: amount } }, - create: { userId, type: 101, amount }, - }); - await rcon.givePointsGotw(userId, amount); - } - given++; - } catch { - failedIds.push({ userId, reason: "Database error" }); - } - } + for (const userId of userIds) { + try { + if (type === "credits") { + await prisma.user.update({ + where: { id: userId }, + data: { credits: { increment: amount } }, + }); + await rcon.giveCredits(userId, amount); + } else if (type === "pixels") { + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId, type: 0 } }, + update: { amount: { increment: amount } }, + create: { userId, type: 0, amount }, + }); + await rcon.giveDuckets(userId, amount); + } else if (type === "points") { + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId, type: 101 } }, + update: { amount: { increment: amount } }, + create: { userId, type: 101, amount }, + }); + await rcon.givePointsGotw(userId, amount); + } + given++; + } catch { + failedIds.push({ userId, reason: "Database error" }); + } + } - await logStaffActivity({ - staffId: staff.id, - action: "bulk_give_currency", - description: `Gave ${amount} ${type} to ${given} user(s)`, - targetType: "user", - }); - return { ok: true as const, data: { given, total: userIds.length, failedIds } }; + await logStaffActivity({ + staffId: staff.id, + action: "bulk_give_currency", + description: `Gave ${amount} ${type} to ${given} user(s)`, + targetType: "user", + }); + return { + ok: true as const, + data: { given, total: userIds.length, failedIds }, + }; } export async function bulkGiveBadge({ - userIds, - badgeCode, + userIds, + badgeCode, }: { - userIds: number[]; - badgeCode: string; + userIds: number[]; + badgeCode: string; }): Promise< - ActionResult<{ given: number; total: number; failedIds: Array<{ userId: number; reason: string }> }> + ActionResult<{ + given: number; + total: number; + failedIds: Array<{ userId: number; reason: string }>; + }> > { - const staff = await requireStaff(); - let given = 0; - const failedIds: Array<{ userId: number; reason: string }> = []; + const staff = await requireStaff(); + let given = 0; + const failedIds: Array<{ userId: number; reason: string }> = []; - for (const userId of userIds) { - try { - const existing = await prisma.usersBadges.findFirst({ - where: { userId, badgeCode }, - select: { id: true }, - }); - if (!existing) { - const max = await prisma.usersBadges.aggregate({ - where: { userId }, - _max: { slotId: true }, - }); - const slotId = (max._max.slotId ?? 0) + 1; - await prisma.usersBadges.create({ data: { userId, slotId, badgeCode } }); - await rcon.giveBadge(userId, badgeCode); - } - given++; - } catch { - failedIds.push({ userId, reason: "Database error" }); - } - } + for (const userId of userIds) { + try { + const existing = await prisma.usersBadges.findFirst({ + where: { userId, badgeCode }, + select: { id: true }, + }); + if (!existing) { + const max = await prisma.usersBadges.aggregate({ + where: { userId }, + _max: { slotId: true }, + }); + const slotId = (max._max.slotId ?? 0) + 1; + await prisma.usersBadges.create({ + data: { userId, slotId, badgeCode }, + }); + await rcon.giveBadge(userId, badgeCode); + } + given++; + } catch { + failedIds.push({ userId, reason: "Database error" }); + } + } - await logStaffActivity({ - staffId: staff.id, - action: "bulk_give_badge", - description: `Gave badge "${badgeCode}" to ${given} user(s)`, - targetType: "user", - }); - return { ok: true as const, data: { given, total: userIds.length, failedIds } }; + await logStaffActivity({ + staffId: staff.id, + action: "bulk_give_badge", + description: `Gave badge "${badgeCode}" to ${given} user(s)`, + targetType: "user", + }); + return { + ok: true as const, + data: { given, total: userIds.length, failedIds }, + }; } diff --git a/src/actions/catalog-bc.ts b/src/actions/catalog-bc.ts index a458cddc94..18026f810b 100644 --- a/src/actions/catalog-bc.ts +++ b/src/actions/catalog-bc.ts @@ -6,116 +6,129 @@ import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; -export async function updateBcPage({ id, ...fields }: { id: number } & Record) { - const staff = await requireStaff(); - await prisma.catalogPagesBc.update({ where: { id }, data: fields as any }); - await rcon.updateCatalog(); - await logStaffActivity({ - staffId: staff.id, - action: "bc_page_update", - description: `Updated BC catalog page #${id}`, - targetType: "catalog_page_bc", - targetId: id, - }); - revalidatePath("/admin/catalog/builder-club"); +export async function updateBcPage({ + id, + ...fields +}: { id: number } & Record) { + const staff = await requireStaff(); + await prisma.catalogPagesBc.update({ where: { id }, data: fields as any }); + await rcon.updateCatalog(); + await logStaffActivity({ + staffId: staff.id, + action: "bc_page_update", + description: `Updated BC catalog page #${id}`, + targetType: "catalog_page_bc", + targetId: id, + }); + revalidatePath("/admin/catalog/builder-club"); } export async function deleteBcItem({ id }: { id: number }) { - const staff = await requireStaff(); - await prisma.catalogItemsBc.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "bc_item_delete", - description: `Deleted BC catalog item #${id}`, - targetType: "catalog_item_bc", - targetId: id, - }); - revalidatePath("/admin/catalog/builder-club"); + const staff = await requireStaff(); + await prisma.catalogItemsBc.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "bc_item_delete", + description: `Deleted BC catalog item #${id}`, + targetType: "catalog_item_bc", + targetId: id, + }); + revalidatePath("/admin/catalog/builder-club"); } export async function updateBcItem({ - id, - ...data + id, + ...data }: { - id: number; - itemIds?: string; - catalogName?: string; - orderNumber?: number; - extradata?: string; + id: number; + itemIds?: string; + catalogName?: string; + orderNumber?: number; + extradata?: string; }) { - const staff = await requireStaff(); - await prisma.catalogItemsBc.update({ where: { id }, data: data as any }); - await logStaffActivity({ - staffId: staff.id, - action: "bc_item_update", - description: `Updated BC catalog item #${id}`, - targetType: "catalog_item_bc", - targetId: id, - }); - revalidatePath("/admin/catalog/builder-club"); + const staff = await requireStaff(); + await prisma.catalogItemsBc.update({ where: { id }, data: data as any }); + await logStaffActivity({ + staffId: staff.id, + action: "bc_item_update", + description: `Updated BC catalog item #${id}`, + targetType: "catalog_item_bc", + targetId: id, + }); + revalidatePath("/admin/catalog/builder-club"); } export async function createBcItem({ - pageId, - ...data + pageId, + ...data }: { - pageId: number; - itemIds: string; - catalogName: string; - orderNumber: number; - extradata: string; + pageId: number; + itemIds: string; + catalogName: string; + orderNumber: number; + extradata: string; }) { - const staff = await requireStaff(); - const created = await prisma.catalogItemsBc.create({ - data: { pageId, ...data }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "bc_item_create", - description: `Created BC catalog item #${created.id}`, - targetType: "catalog_item_bc", - targetId: created.id, - }); - revalidatePath("/admin/catalog/builder-club"); + const staff = await requireStaff(); + const created = await prisma.catalogItemsBc.create({ + data: { pageId, ...data }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "bc_item_create", + description: `Created BC catalog item #${created.id}`, + targetType: "catalog_item_bc", + targetId: created.id, + }); + revalidatePath("/admin/catalog/builder-club"); } -export async function toggleBcPage({ id, field }: { id: number; field: "enabled" | "visible" }) { - await requireStaff(); - const page = await prisma.catalogPagesBc.findUnique({ - where: { id }, - select: { enabled: true, visible: true }, - }); - if (!page) return; - await prisma.catalogPagesBc.update({ - where: { id }, - data: { [field]: page[field] === "1" ? "0" : "1" }, - }); - revalidatePath("/admin/catalog/builder-club"); +export async function toggleBcPage({ + id, + field, +}: { + id: number; + field: "enabled" | "visible"; +}) { + await requireStaff(); + const page = await prisma.catalogPagesBc.findUnique({ + where: { id }, + select: { enabled: true, visible: true }, + }); + if (!page) return; + await prisma.catalogPagesBc.update({ + where: { id }, + data: { [field]: page[field] === "1" ? "0" : "1" }, + }); + revalidatePath("/admin/catalog/builder-club"); } -export async function createBcPage(input: { caption: string; parentId: number; pageLayout: string }) { - const staff = await requireStaff(); - const created = await prisma.catalogPagesBc.create({ - data: { - caption: input.caption, - parentId: input.parentId, - pageLayout: input.pageLayout, - iconColor: 0, - iconImage: 0, - orderNum: 0, - visible: "1", - enabled: "1", - pageHeadline: "", - pageTeaser: "", - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "bc_page_create", - description: `Created BC catalog page "${input.caption}"`, - targetType: "catalog_page_bc", - targetId: created.id, - }); - revalidatePath("/admin/catalog/builder-club"); - return { ok: true as const, data: { id: created.id } }; +export async function createBcPage(input: { + caption: string; + parentId: number; + pageLayout: string; +}) { + const staff = await requireStaff(); + const created = await prisma.catalogPagesBc.create({ + data: { + caption: input.caption, + parentId: input.parentId, + pageLayout: input.pageLayout, + iconColor: 0, + iconImage: 0, + orderNum: 0, + visible: "1", + enabled: "1", + pageHeadline: "", + pageTeaser: "", + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "bc_page_create", + description: `Created BC catalog page "${input.caption}"`, + targetType: "catalog_page_bc", + targetId: created.id, + }); + revalidatePath("/admin/catalog/builder-club"); + return { ok: true as const, data: { id: created.id } }; } diff --git a/src/actions/catalog-items.ts b/src/actions/catalog-items.ts index 4933414bda..b6d2bfef22 100644 --- a/src/actions/catalog-items.ts +++ b/src/actions/catalog-items.ts @@ -7,153 +7,165 @@ import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; export async function createCatalogItem(data: { - pageId: number; - itemIds: string; - catalogName: string; - costCredits: number; - costPoints: number; - pointsType: number; - amount: number; - orderNumber: number; - offerId: number; - limitedSells: number; - limitedStack: number; - extradata: string; - songId: number; - haveOffer: "0" | "1"; - clubOnly: "0" | "1"; + pageId: number; + itemIds: string; + catalogName: string; + costCredits: number; + costPoints: number; + pointsType: number; + amount: number; + orderNumber: number; + offerId: number; + limitedSells: number; + limitedStack: number; + extradata: string; + songId: number; + haveOffer: "0" | "1"; + clubOnly: "0" | "1"; }) { - const staff = await requireStaff(); - const created = await prisma.catalogItems.create({ data }); - await rcon.updateCatalog(); - await logStaffActivity({ - staffId: staff.id, - action: "catalog_item_create", - description: `Created catalog item #${created.id}`, - targetType: "catalog_item", - targetId: created.id, - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: { id: created.id } }; + const staff = await requireStaff(); + const created = await prisma.catalogItems.create({ data }); + await rcon.updateCatalog(); + await logStaffActivity({ + staffId: staff.id, + action: "catalog_item_create", + description: `Created catalog item #${created.id}`, + targetType: "catalog_item", + targetId: created.id, + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: { id: created.id } }; } export async function deleteCatalogItems({ ids }: { ids: number[] }) { - const staff = await requireStaff(); - await prisma.catalogItems.deleteMany({ where: { id: { in: ids } } }); - await rcon.updateCatalog(); - await logStaffActivity({ - staffId: staff.id, - action: "catalog_items_delete", - description: `Deleted catalog items: ${ids.join(", ")}`, - targetType: "catalog_item", - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; + const staff = await requireStaff(); + await prisma.catalogItems.deleteMany({ where: { id: { in: ids } } }); + await rcon.updateCatalog(); + await logStaffActivity({ + staffId: staff.id, + action: "catalog_items_delete", + description: `Deleted catalog items: ${ids.join(", ")}`, + targetType: "catalog_item", + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } -export async function moveCatalogItems({ ids, targetPageId }: { ids: number[]; targetPageId: number }) { - await requireStaff(); - await prisma.catalogItems.updateMany({ - where: { id: { in: ids } }, - data: { pageId: targetPageId }, - }); - await rcon.updateCatalog(); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; +export async function moveCatalogItems({ + ids, + targetPageId, +}: { + ids: number[]; + targetPageId: number; +}) { + await requireStaff(); + await prisma.catalogItems.updateMany({ + where: { id: { in: ids } }, + data: { pageId: targetPageId }, + }); + await rcon.updateCatalog(); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } export async function reorderCatalogItems({ - orders, + orders, }: { - orders: Array<{ id: number; orderNumber: number }>; + orders: Array<{ id: number; orderNumber: number }>; }) { - await requireStaff(); - for (const { id, orderNumber } of orders) { - await prisma.catalogItems.update({ where: { id }, data: { orderNumber } }); - } - await rcon.updateCatalog(); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; + await requireStaff(); + for (const { id, orderNumber } of orders) { + await prisma.catalogItems.update({ where: { id }, data: { orderNumber } }); + } + await rcon.updateCatalog(); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } export async function updateCatalogItem({ - id, - catalogFields, - baseItem, + id, + catalogFields, + baseItem, }: { - id: number; - catalogFields: Record; - baseItem?: { id: number; fields: Record }; + id: number; + catalogFields: Record; + baseItem?: { id: number; fields: Record }; }) { - const staff = await requireStaff(); - await prisma.catalogItems.update({ where: { id }, data: catalogFields as any }); - if (baseItem) { - await prisma.itemsBase.update({ where: { id: baseItem.id }, data: baseItem.fields as any }); - } - await rcon.updateCatalog(); - await logStaffActivity({ - staffId: staff.id, - action: "catalog_item_update", - description: `Updated catalog item #${id}`, - targetType: "catalog_item", - targetId: id, - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; + const staff = await requireStaff(); + await prisma.catalogItems.update({ + where: { id }, + data: catalogFields as any, + }); + if (baseItem) { + await prisma.itemsBase.update({ + where: { id: baseItem.id }, + data: baseItem.fields as any, + }); + } + await rcon.updateCatalog(); + await logStaffActivity({ + staffId: staff.id, + action: "catalog_item_update", + description: `Updated catalog item #${id}`, + targetType: "catalog_item", + targetId: id, + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } export async function translateCatalogItems({ - items, + items, }: { - items: Array<{ id: number; publicName: string; description: string }>; + items: Array<{ id: number; publicName: string; description: string }>; }) { - await requireStaff(); - let namesUpdated = 0; - let descriptionsUpdated = 0; - let furniDataUpdated = 0; - let furniDataInserted = 0; + await requireStaff(); + let namesUpdated = 0; + let descriptionsUpdated = 0; + let furniDataUpdated = 0; + const _furniDataInserted = 0; - for (const item of items) { - const existing = await prisma.catalogItems.findUnique({ - where: { id: item.id }, - select: { catalogName: true }, - }); - if (!existing) continue; + for (const item of items) { + const existing = await prisma.catalogItems.findUnique({ + where: { id: item.id }, + select: { catalogName: true }, + }); + if (!existing) continue; - if (item.publicName && item.publicName !== existing.catalogName) { - await prisma.catalogItems.update({ - where: { id: item.id }, - data: { catalogName: item.publicName }, - }); - namesUpdated++; - } + if (item.publicName && item.publicName !== existing.catalogName) { + await prisma.catalogItems.update({ + where: { id: item.id }, + data: { catalogName: item.publicName }, + }); + namesUpdated++; + } - if (item.description) { - const baseItem = await prisma.itemsBase.findFirst({ - where: { itemName: existing.catalogName }, - select: { id: true, publicName: true }, - }); - if (baseItem) { - await prisma.itemsBase.update({ - where: { id: baseItem.id }, - data: { publicName: item.publicName || baseItem.publicName }, - }); - furniDataUpdated++; - } - descriptionsUpdated++; - } - } + if (item.description) { + const baseItem = await prisma.itemsBase.findFirst({ + where: { itemName: existing.catalogName }, + select: { id: true, publicName: true }, + }); + if (baseItem) { + await prisma.itemsBase.update({ + where: { id: baseItem.id }, + data: { publicName: item.publicName || baseItem.publicName }, + }); + furniDataUpdated++; + } + descriptionsUpdated++; + } + } - await rcon.updateCatalog(); - revalidatePath("/admin/catalog"); - return { - ok: true as const, - data: { - namesUpdated, - descriptionsUpdated, - furniDataUpdated, - furniDataInserted: 0, - updated: items.length, - }, - }; + await rcon.updateCatalog(); + revalidatePath("/admin/catalog"); + return { + ok: true as const, + data: { + namesUpdated, + descriptionsUpdated, + furniDataUpdated, + furniDataInserted: 0, + updated: items.length, + }, + }; } diff --git a/src/actions/catalog.ts b/src/actions/catalog.ts index 032d229d62..26d62c2340 100644 --- a/src/actions/catalog.ts +++ b/src/actions/catalog.ts @@ -3,150 +3,159 @@ import { revalidatePath } from "next/cache"; import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; +import type { ActionResult } from "@/lib/safe-action-shared"; import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; -import type { ActionResult } from "@/lib/safe-action-shared"; export async function updateCatalogPage({ - id, - ...fields + id, + ...fields }: { id: number } & Record): Promise { - const staff = await requireStaff(); - await prisma.catalogPages.update({ where: { id }, data: fields as any }); - await rcon.updateCatalog(); - await logStaffActivity({ - staffId: staff.id, - action: "catalog_page_update", - description: `Updated catalog page #${id}`, - targetType: "catalog_page", - targetId: id, - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; + const staff = await requireStaff(); + await prisma.catalogPages.update({ where: { id }, data: fields as any }); + await rcon.updateCatalog(); + await logStaffActivity({ + staffId: staff.id, + action: "catalog_page_update", + description: `Updated catalog page #${id}`, + targetType: "catalog_page", + targetId: id, + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } export async function deleteCatalogPage({ id }: { id: number }) { - const staff = await requireStaff(); - await prisma.catalogPages.delete({ where: { id } }); - await rcon.updateCatalog(); - await logStaffActivity({ - staffId: staff.id, - action: "catalog_page_delete", - description: `Deleted catalog page #${id}`, - targetType: "catalog_page", - targetId: id, - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; + const staff = await requireStaff(); + await prisma.catalogPages.delete({ where: { id } }); + await rcon.updateCatalog(); + await logStaffActivity({ + staffId: staff.id, + action: "catalog_page_delete", + description: `Deleted catalog page #${id}`, + targetType: "catalog_page", + targetId: id, + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } export async function toggleCatalogPage({ - id, - action, + id, + action, }: { - id: number; - action: "toggleEnabled" | "toggleVisible"; + id: number; + action: "toggleEnabled" | "toggleVisible"; }) { - await requireStaff(); - const page = await prisma.catalogPages.findUnique({ - where: { id }, - select: { enabled: true, visible: true }, - }); - if (!page) return { ok: false as const, error: "Catalog page not found" }; - const field = action === "toggleEnabled" ? "enabled" : "visible"; - const current = action === "toggleEnabled" ? page.enabled : page.visible; - await prisma.catalogPages.update({ - where: { id }, - data: { [field]: current === "1" ? "0" : "1" }, - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; + await requireStaff(); + const page = await prisma.catalogPages.findUnique({ + where: { id }, + select: { enabled: true, visible: true }, + }); + if (!page) return { ok: false as const, error: "Catalog page not found" }; + const field = action === "toggleEnabled" ? "enabled" : "visible"; + const current = action === "toggleEnabled" ? page.enabled : page.visible; + await prisma.catalogPages.update({ + where: { id }, + data: { [field]: current === "1" ? "0" : "1" }, + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } export async function createCatalogPage(input: { - caption: string; - parentId: number; - pageLayout?: string; - iconImage?: number; - iconColor?: number; - enabled?: "0" | "1"; - visible?: "0" | "1"; - minRank?: number; - orderNum?: number; + caption: string; + parentId: number; + pageLayout?: string; + iconImage?: number; + iconColor?: number; + enabled?: "0" | "1"; + visible?: "0" | "1"; + minRank?: number; + orderNum?: number; }): Promise> { - const staff = await requireStaff(); - const created = await prisma.catalogPages.create({ - data: { - caption: input.caption, - parentId: input.parentId, - pageLayout: input.pageLayout ?? "default_3x3", - captionSave: input.caption.slice(0, 25), - iconColor: input.iconColor ?? 0, - iconImage: input.iconImage ?? 0, - minRank: input.minRank ?? 1, - orderNum: input.orderNum ?? 0, - visible: input.visible ?? "1", - enabled: input.enabled ?? "1", - clubOnly: "0", - vipOnly: "0", - pageHeadline: "", - pageTeaser: "", - includes: "", - }, - }); - await logStaffActivity({ - staffId: staff.id, - action: "catalog_page_create", - description: `Created catalog page "${input.caption}"`, - targetType: "catalog_page", - targetId: created.id, - }); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: { id: created.id } }; + const staff = await requireStaff(); + const created = await prisma.catalogPages.create({ + data: { + caption: input.caption, + parentId: input.parentId, + pageLayout: input.pageLayout ?? "default_3x3", + captionSave: input.caption.slice(0, 25), + iconColor: input.iconColor ?? 0, + iconImage: input.iconImage ?? 0, + minRank: input.minRank ?? 1, + orderNum: input.orderNum ?? 0, + visible: input.visible ?? "1", + enabled: input.enabled ?? "1", + clubOnly: "0", + vipOnly: "0", + pageHeadline: "", + pageTeaser: "", + includes: "", + }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "catalog_page_create", + description: `Created catalog page "${input.caption}"`, + targetType: "catalog_page", + targetId: created.id, + }); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: { id: created.id } }; } -export async function reorderTreePage(input: { pageId: number; newParentId?: number; newOrderNum: number }) { - await requireStaff(); - await prisma.catalogPages.update({ - where: { id: input.pageId }, - data: { - orderNum: input.newOrderNum, - ...(input.newParentId === undefined ? {} : { parentId: input.newParentId }), - }, - }); - await rcon.updateCatalog(); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; +export async function reorderTreePage(input: { + pageId: number; + newParentId?: number; + newOrderNum: number; +}) { + await requireStaff(); + await prisma.catalogPages.update({ + where: { id: input.pageId }, + data: { + orderNum: input.newOrderNum, + ...(input.newParentId === undefined + ? {} + : { parentId: input.newParentId }), + }, + }); + await rcon.updateCatalog(); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } -export async function deleteTreePage(input: { pageId: number; mode: "reparent" | "cascade" }) { - await requireStaff(); - if (input.mode === "cascade") { - const children = await prisma.catalogPages.findMany({ - where: { parentId: input.pageId }, - select: { id: true }, - }); - const pageIds = [input.pageId, ...children.map((child) => child.id)]; - await prisma.$transaction([ - prisma.catalogItems.deleteMany({ where: { pageId: { in: pageIds } } }), - prisma.catalogPages.deleteMany({ where: { id: { in: pageIds } } }), - ]); - } else { - const page = await prisma.catalogPages.findUnique({ - where: { id: input.pageId }, - select: { parentId: true }, - }); - if (!page) return { ok: false as const, error: "Catalog page not found" }; - await prisma.$transaction([ - prisma.catalogPages.updateMany({ - where: { parentId: input.pageId }, - data: { parentId: page.parentId }, - }), - prisma.catalogItems.deleteMany({ where: { pageId: input.pageId } }), - prisma.catalogPages.delete({ where: { id: input.pageId } }), - ]); - } - await rcon.updateCatalog(); - revalidatePath("/admin/catalog"); - return { ok: true as const, data: {} }; +export async function deleteTreePage(input: { + pageId: number; + mode: "reparent" | "cascade"; +}) { + await requireStaff(); + if (input.mode === "cascade") { + const children = await prisma.catalogPages.findMany({ + where: { parentId: input.pageId }, + select: { id: true }, + }); + const pageIds = [input.pageId, ...children.map((child) => child.id)]; + await prisma.$transaction([ + prisma.catalogItems.deleteMany({ where: { pageId: { in: pageIds } } }), + prisma.catalogPages.deleteMany({ where: { id: { in: pageIds } } }), + ]); + } else { + const page = await prisma.catalogPages.findUnique({ + where: { id: input.pageId }, + select: { parentId: true }, + }); + if (!page) return { ok: false as const, error: "Catalog page not found" }; + await prisma.$transaction([ + prisma.catalogPages.updateMany({ + where: { parentId: input.pageId }, + data: { parentId: page.parentId }, + }), + prisma.catalogItems.deleteMany({ where: { pageId: input.pageId } }), + prisma.catalogPages.delete({ where: { id: input.pageId } }), + ]); + } + await rcon.updateCatalog(); + revalidatePath("/admin/catalog"); + return { ok: true as const, data: {} }; } diff --git a/src/actions/commandocentrum.ts b/src/actions/commandocentrum.ts index 46982e35c6..9279e3b402 100644 --- a/src/actions/commandocentrum.ts +++ b/src/actions/commandocentrum.ts @@ -8,218 +8,218 @@ const PATH = "/admin/commandocentrum"; /** Rebuild the in-memory catalog on the emulator (rcon: updatecatalog). */ export async function updateCatalog(): Promise { - await requireStaff(); - try { - await rcon.updateCatalog(); - } catch { - // RCON is best-effort; a dead socket must not 500 the admin page. - } - revalidatePath(PATH); + await requireStaff(); + try { + await rcon.updateCatalog(); + } catch { + // RCON is best-effort; a dead socket must not 500 the admin page. + } + revalidatePath(PATH); } /** Reload the chat word filter on the emulator (rcon: updatewordfilter). */ export async function updateWordFilter(): Promise { - await requireStaff(); - try { - await rcon.updateWordFilter(); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + try { + await rcon.updateWordFilter(); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Reload navigator data on the emulator (rcon: updatenavigator, no payload). */ export async function updateNavigator(): Promise { - await requireStaff(); - try { - await rcon.send("updatenavigator", null); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + try { + await rcon.send("updatenavigator", null); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Broadcast a hotel-wide alert to every connected user (rcon: hotelalert). */ export async function hotelAlert(formData: FormData): Promise { - await requireStaff(); - const message = String(formData.get("message") ?? "") - .normalize("NFC") - .trim() - .slice(0, 512); - if (!message) return; - try { - await rcon.send("hotelalert", { message }); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const message = String(formData.get("message") ?? "") + .normalize("NFC") + .trim() + .slice(0, 512); + if (!message) return; + try { + await rcon.send("hotelalert", { message }); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Disconnect/kick a user from the hotel (rcon: disconnect). */ export async function disconnectUser(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const username = String(formData.get("username") ?? "") - .normalize("NFC") - .trim(); - if (!userId || !username) return; - try { - await rcon.disconnectUser(userId, username); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const username = String(formData.get("username") ?? "") + .normalize("NFC") + .trim(); + if (!userId || !username) return; + try { + await rcon.disconnectUser(userId, username); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Send an alert to a specific user (rcon: alertuser). */ export async function alertUser(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const message = String(formData.get("message") ?? "") - .normalize("NFC") - .trim() - .slice(0, 512); - if (!userId || !message) return; - try { - await rcon.alertUser(userId, message); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const message = String(formData.get("message") ?? "") + .normalize("NFC") + .trim() + .slice(0, 512); + if (!userId || !message) return; + try { + await rcon.alertUser(userId, message); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Forward a user to a specific room (rcon: forwarduser). */ export async function forwardUser(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const roomId = Number(formData.get("roomId")); - if (!userId || !roomId) return; - try { - await rcon.forwardUser(userId, roomId); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const roomId = Number(formData.get("roomId")); + if (!userId || !roomId) return; + try { + await rcon.forwardUser(userId, roomId); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Give credits to a user (rcon: givecredits). */ export async function giveCredits(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const credits = Number(formData.get("credits")); - if (!userId || !credits || credits <= 0) return; - try { - await rcon.giveCredits(userId, credits); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const credits = Number(formData.get("credits")); + if (!userId || !credits || credits <= 0) return; + try { + await rcon.giveCredits(userId, credits); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Give duckets to a user (rcon: givepoints type=duckets). */ export async function giveDuckets(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const amount = Number(formData.get("amount")); - if (!userId || !amount || amount <= 0) return; - try { - await rcon.giveDuckets(userId, amount); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const amount = Number(formData.get("amount")); + if (!userId || !amount || amount <= 0) return; + try { + await rcon.giveDuckets(userId, amount); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Give diamonds to a user (rcon: givepoints type=diamonds). */ export async function giveDiamonds(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const amount = Number(formData.get("amount")); - if (!userId || !amount || amount <= 0) return; - try { - await rcon.giveDiamonds(userId, amount); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const amount = Number(formData.get("amount")); + if (!userId || !amount || amount <= 0) return; + try { + await rcon.giveDiamonds(userId, amount); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Give a badge to a user (rcon: givebadge). */ export async function giveBadge(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const badge = String(formData.get("badge") ?? "") - .normalize("NFC") - .trim(); - if (!userId || !badge) return; - try { - await rcon.giveBadge(userId, badge); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const badge = String(formData.get("badge") ?? "") + .normalize("NFC") + .trim(); + if (!userId || !badge) return; + try { + await rcon.giveBadge(userId, badge); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Set a user's motto (rcon: setmotto). */ export async function setMotto(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const motto = String(formData.get("motto") ?? "") - .normalize("NFC") - .trim() - .slice(0, 127); - if (!userId || !motto) return; - try { - await rcon.setMotto(userId, motto); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const motto = String(formData.get("motto") ?? "") + .normalize("NFC") + .trim() + .slice(0, 127); + if (!userId || !motto) return; + try { + await rcon.setMotto(userId, motto); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Set a user's rank (rcon: setrank). */ export async function setRank(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const rank = Number(formData.get("rank")); - if (!userId || rank < 0 || rank > 10) return; - try { - await rcon.setRank(userId, rank); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const rank = Number(formData.get("rank")); + if (!userId || rank < 0 || rank > 10) return; + try { + await rcon.setRank(userId, rank); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Execute a command as a user (rcon: executecommand). */ export async function executeCommand(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const command = String(formData.get("command") ?? "") - .normalize("NFC") - .trim(); - if (!userId || !command) return; - try { - await rcon.executeCommand(userId, command); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const command = String(formData.get("command") ?? "") + .normalize("NFC") + .trim(); + if (!userId || !command) return; + try { + await rcon.executeCommand(userId, command); + } catch { + // best-effort + } + revalidatePath(PATH); } /** Send a gift to a user (rcon: sendgift). */ export async function sendGift(formData: FormData): Promise { - await requireStaff(); - const userId = Number(formData.get("userId")); - const itemId = Number(formData.get("itemId")); - const message = String(formData.get("message") ?? "Here is a gift.") - .trim() - .slice(0, 255); - if (!userId || !itemId) return; - try { - await rcon.sendGift(userId, itemId, message); - } catch { - // best-effort - } - revalidatePath(PATH); + await requireStaff(); + const userId = Number(formData.get("userId")); + const itemId = Number(formData.get("itemId")); + const message = String(formData.get("message") ?? "Here is a gift.") + .trim() + .slice(0, 255); + if (!userId || !itemId) return; + try { + await rcon.sendGift(userId, itemId, message); + } catch { + // best-effort + } + revalidatePath(PATH); } diff --git a/src/actions/draw-badge.ts b/src/actions/draw-badge.ts index 314b70d3f1..d48c13c58c 100644 --- a/src/actions/draw-badge.ts +++ b/src/actions/draw-badge.ts @@ -2,11 +2,11 @@ import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; +import type { Prisma } from "@/generated/prisma/client"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; import { siteSettings } from "@/lib/services/site-settings"; -import type { Prisma } from "@/generated/prisma/client"; /** * Buy a published community-drawn badge for the SIGNED-IN user. Faithful to @@ -30,100 +30,100 @@ const DEFAULT_PRICE = 50; // The emulator badge code is the badge_path filename without its directory or // extension, restricted to the code charset the client accepts. function badgeCodeFromPath(badgePath: string): string { - const base = badgePath.split(/[\\/]/).pop() ?? badgePath; - const noExt = base.replace(/\.[^.]+$/, ""); - return noExt.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 32); + const base = badgePath.split(/[\\/]/).pop() ?? badgePath; + const noExt = base.replace(/\.[^.]+$/, ""); + return noExt.replace(/[^A-Za-z0-9_-]/g, "").slice(0, 32); } async function resolvePrice(): Promise { - const raw = await siteSettings.get("drawbadge.price", String(DEFAULT_PRICE)); - const n = Number(raw); - return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_PRICE; + const raw = await siteSettings.get("drawbadge.price", String(DEFAULT_PRICE)); + const n = Number(raw); + return Number.isFinite(n) && n >= 0 ? Math.floor(n) : DEFAULT_PRICE; } export async function buyBadge(formData: FormData): Promise { - const session = await auth(); - if (!session?.user?.id) redirect("/login"); + const session = await auth(); + if (!session?.user?.id) redirect("/login"); - const userId = Number(session.user.id); - if (!Number.isFinite(userId)) redirect("/login"); + const userId = Number(session.user.id); + if (!Number.isFinite(userId)) redirect("/login"); - // The form posts the badge row id; everything else (price, code) is resolved - // server-side from trusted data — never from the client. - const rawId = String(formData.get("id") ?? "") - .normalize("NFC") - .trim(); - if (!/^\d+$/.test(rawId)) redirect("/draw-badge?error=invalid"); + // The form posts the badge row id; everything else (price, code) is resolved + // server-side from trusted data — never from the client. + const rawId = String(formData.get("id") ?? "") + .normalize("NFC") + .trim(); + if (!/^\d+$/.test(rawId)) redirect("/draw-badge?error=invalid"); - let outcome: "bought" | "invalid" | "credits" | "fail"; - let boughtCode = ""; + let outcome: "bought" | "invalid" | "credits" | "fail"; + let boughtCode = ""; - try { - const badge = await prisma.websiteDrawbadges.findUnique({ - where: { id: BigInt(rawId) }, - select: { id: true, badgePath: true, published: true }, - }); + try { + const badge = await prisma.websiteDrawbadges.findUnique({ + where: { id: BigInt(rawId) }, + select: { id: true, badgePath: true, published: true }, + }); - if (!badge || !badge.published) { - outcome = "invalid"; - } else { - const code = badgeCodeFromPath(badge.badgePath); - if (code.length === 0) { - outcome = "invalid"; - } else { - const price = await resolvePrice(); + if (!badge?.published) { + outcome = "invalid"; + } else { + const code = badgeCodeFromPath(badge.badgePath); + if (code.length === 0) { + outcome = "invalid"; + } else { + const price = await resolvePrice(); - // Re-read the buyer's live credit balance and verify it covers the cost. - const buyer = await prisma.user.findUnique({ - where: { id: userId }, - select: { credits: true }, - }); - if (!buyer || buyer.credits < price) { - outcome = "credits"; - } else { - // Atomically deduct credits and persist the badge so a failure - // between the two operations cannot orphan the user. - if (price > 0) { - await prisma.$transaction(async (tx: Prisma.TransactionClient) => { - await tx.user.update({ - where: { id: userId }, - data: { credits: { decrement: price } }, - }); + // Re-read the buyer's live credit balance and verify it covers the cost. + const buyer = await prisma.user.findUnique({ + where: { id: userId }, + select: { credits: true }, + }); + if (!buyer || buyer.credits < price) { + outcome = "credits"; + } else { + // Atomically deduct credits and persist the badge so a failure + // between the two operations cannot orphan the user. + if (price > 0) { + await prisma.$transaction(async (tx: Prisma.TransactionClient) => { + await tx.user.update({ + where: { id: userId }, + data: { credits: { decrement: price } }, + }); - const existing = await tx.usersBadges.findFirst({ - where: { userId, badgeCode: code }, - select: { id: true }, - }); - if (!existing) { - const max = await tx.usersBadges.aggregate({ - where: { userId }, - _max: { slotId: true }, - }); - const slotId = (max._max.slotId ?? 0) + 1; - await tx.usersBadges.create({ - data: { userId, slotId, badgeCode: code }, - }); - } - }); - } + const existing = await tx.usersBadges.findFirst({ + where: { userId, badgeCode: code }, + select: { id: true }, + }); + if (!existing) { + const max = await tx.usersBadges.aggregate({ + where: { userId }, + _max: { slotId: true }, + }); + const slotId = (max._max.slotId ?? 0) + 1; + await tx.usersBadges.create({ + data: { userId, slotId, badgeCode: code }, + }); + } + }); + } - // Grant the badge live so it appears immediately for online users. - await rcon.giveBadge(userId, code).catch(() => {}); + // Grant the badge live so it appears immediately for online users. + await rcon.giveBadge(userId, code).catch(() => {}); - outcome = "bought"; - boughtCode = code; - } - } - } - } catch { - outcome = "fail"; - } + outcome = "bought"; + boughtCode = code; + } + } + } + } catch { + outcome = "fail"; + } - revalidatePath("/draw-badge"); + revalidatePath("/draw-badge"); - // redirect() throws — it must live OUTSIDE the try/catch. - if (outcome === "bought") { - redirect(`/draw-badge?bought=${encodeURIComponent(boughtCode)}`); - } - redirect(`/draw-badge?error=${outcome}`); + // redirect() throws — it must live OUTSIDE the try/catch. + if (outcome === "bought") { + redirect(`/draw-badge?bought=${encodeURIComponent(boughtCode)}`); + } + redirect(`/draw-badge?error=${outcome}`); } diff --git a/src/actions/email-verify.ts b/src/actions/email-verify.ts index 141400a687..83c81b41ce 100644 --- a/src/actions/email-verify.ts +++ b/src/actions/email-verify.ts @@ -16,28 +16,36 @@ import { siteSettings } from "@/lib/services/site-settings"; /** Secret mixed into the digest. Requires at least one of APP_KEY or AUTH_SECRET. */ function verifySecret(): string { - const secret = env.APP_KEY || env.AUTH_SECRET; - if (!secret) throw new Error("APP_KEY or AUTH_SECRET must be set for email verification"); - return secret; + const secret = env.APP_KEY || env.AUTH_SECRET; + if (!secret) + throw new Error( + "APP_KEY or AUTH_SECRET must be set for email verification", + ); + return secret; } /** Compute the verification token for an email (lowercased + trimmed). */ export async function verificationToken(email: string): Promise { - const normalised = email.trim().toLowerCase(); - return createHash("sha256").update(`${normalised}|${verifySecret()}`).digest("hex"); + const normalised = email.trim().toLowerCase(); + return createHash("sha256") + .update(`${normalised}|${verifySecret()}`) + .digest("hex"); } /** * Constant-time check that `token` matches the expected digest for `email`. * Returns false on any length/format mismatch rather than throwing. */ -export async function isValidVerificationToken(email: string, token: string): Promise { - if (!email || !token || !/^[a-f0-9]{64}$/i.test(token)) return false; - const expected = await verificationToken(email); - const a = Buffer.from(expected, "utf8"); - const b = Buffer.from(token.toLowerCase(), "utf8"); - if (a.length !== b.length) return false; - return timingSafeEqual(a, b); +export async function isValidVerificationToken( + email: string, + token: string, +): Promise { + if (!email || !token || !/^[a-f0-9]{64}$/i.test(token)) return false; + const expected = await verificationToken(email); + const a = Buffer.from(expected, "utf8"); + const b = Buffer.from(token.toLowerCase(), "utf8"); + if (a.length !== b.length) return false; + return timingSafeEqual(a, b); } /** @@ -45,16 +53,17 @@ export async function isValidVerificationToken(email: string, token: string): Pr * is unconfigured (sendMail returns false). */ export async function sendVerification(email: string): Promise { - const normalised = email.trim().toLowerCase(); - if (!normalised) return false; + const normalised = email.trim().toLowerCase(); + if (!normalised) return false; - const token = await verificationToken(normalised); - const base = env.APP_URL.replace(/\/+$/, ""); - const link = `${base}/verify?token=${encodeURIComponent(token)}&email=${encodeURIComponent(normalised)}`; + const token = await verificationToken(normalised); + const base = env.APP_URL.replace(/\/+$/, ""); + const link = `${base}/verify?token=${encodeURIComponent(token)}&email=${encodeURIComponent(normalised)}`; - const hotelName = (await siteSettings.get("hotel_name", env.HOTEL_NAME)) ?? env.HOTEL_NAME; + const hotelName = + (await siteSettings.get("hotel_name", env.HOTEL_NAME)) ?? env.HOTEL_NAME; - const html = ` + const html = `

Verify your email

Welcome to ${escapeHtml(hotelName)}! Confirm this email address to finish setting up your account.

@@ -69,9 +78,13 @@ export async function sendVerification(email: string): Promise {
`.trim(); - return sendMail(normalised, `Verify your email · ${hotelName}`, html); + return sendMail(normalised, `Verify your email · ${hotelName}`, html); } function escapeHtml(s: string): string { - return s.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); + return s + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); } diff --git a/src/actions/emulator.ts b/src/actions/emulator.ts index 94181da677..22a610ab65 100644 --- a/src/actions/emulator.ts +++ b/src/actions/emulator.ts @@ -9,31 +9,31 @@ import { logAudit } from "@/lib/services/audit"; import { rcon } from "@/lib/services/rcon"; const saveEmulatorSettingsSchema = z.object({ - settings: z.record(z.string(), z.string()), + settings: z.record(z.string(), z.string()), }); export const saveEmulatorSettings = adminAction( - { permission: PERMS.SETTINGS_EDIT, schema: saveEmulatorSettingsSchema }, - async (ctx) => { - const entries = Object.entries(ctx.data.settings); + { permission: PERMS.SETTINGS_EDIT, schema: saveEmulatorSettingsSchema }, + async (ctx) => { + const entries = Object.entries(ctx.data.settings); - for (const [key, value] of entries) { - await prisma.emulatorSettings.upsert({ - where: { key }, - update: { value: String(value) }, - create: { key, value: String(value) }, - }); - } + for (const [key, value] of entries) { + await prisma.emulatorSettings.upsert({ + where: { key }, + update: { value: String(value) }, + create: { key, value: String(value) }, + }); + } - await rcon.updateConfig(); + await rcon.updateConfig(); - logAudit({ - userId: ctx.session.user.id, - action: "emulator_settings_update", - target: "EmulatorSettings", - after: ctx.data.settings, - }); + logAudit({ + userId: ctx.session.user.id, + action: "emulator_settings_update", + target: "EmulatorSettings", + after: ctx.data.settings, + }); - return actionOk(); - }, + return actionOk(); + }, ); diff --git a/src/actions/events.ts b/src/actions/events.ts index 3bcdbcbe4d..3a831c1268 100644 --- a/src/actions/events.ts +++ b/src/actions/events.ts @@ -7,179 +7,189 @@ import { adminAction } from "@/lib/safe-action"; import { ActionError, actionOk } from "@/lib/safe-action-shared"; import { logAudit } from "@/lib/services/audit"; import { - createEventSchema, - eventPrizeSchema, - eventTypeSchema, - eventWinnerSchema, - updateEventSchema, + createEventSchema, + eventPrizeSchema, + eventTypeSchema, + eventWinnerSchema, + updateEventSchema, } from "@/lib/validators/event"; // ── Event Types ───────────────────────────────────────────────────── export const createEventType = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: eventTypeSchema }, - async (ctx) => { - const eventType = await prisma.websiteEventType.create({ - data: ctx.data, - }); - logAudit({ - userId: ctx.session.user.id, - action: "event_type_create", - target: "WebsiteEventType", - targetId: eventType.id, - after: { name: eventType.name }, - }); - return actionOk({ id: eventType.id }); - }, + { permission: PERMS.EVENTS_EDIT, schema: eventTypeSchema }, + async (ctx) => { + const eventType = await prisma.websiteEventType.create({ + data: ctx.data, + }); + logAudit({ + userId: ctx.session.user.id, + action: "event_type_create", + target: "WebsiteEventType", + targetId: eventType.id, + after: { name: eventType.name }, + }); + return actionOk({ id: eventType.id }); + }, ); const updateEventTypeInput = eventTypeSchema.partial().extend({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const updateEventType = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: updateEventTypeInput }, - async (ctx) => { - const { id, ...data } = ctx.data; - const existing = await prisma.websiteEventType.findUnique({ where: { id } }); - if (!existing) throw new ActionError("Event type not found"); + { permission: PERMS.EVENTS_EDIT, schema: updateEventTypeInput }, + async (ctx) => { + const { id, ...data } = ctx.data; + const existing = await prisma.websiteEventType.findUnique({ + where: { id }, + }); + if (!existing) throw new ActionError("Event type not found"); - await prisma.websiteEventType.update({ where: { id }, data }); - logAudit({ - userId: ctx.session.user.id, - action: "event_type_update", - target: "WebsiteEventType", - targetId: id, - before: { name: existing.name }, - after: data, - }); - return actionOk({ id }); - }, + await prisma.websiteEventType.update({ where: { id }, data }); + logAudit({ + userId: ctx.session.user.id, + action: "event_type_update", + target: "WebsiteEventType", + targetId: id, + before: { name: existing.name }, + after: data, + }); + return actionOk({ id }); + }, ); const deleteEventTypeInput = z.object({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const deleteEventType = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: deleteEventTypeInput }, - async (ctx) => { - const existing = await prisma.websiteEventType.findUnique({ where: { id: ctx.data.id } }); - if (!existing) throw new ActionError("Event type not found"); + { permission: PERMS.EVENTS_EDIT, schema: deleteEventTypeInput }, + async (ctx) => { + const existing = await prisma.websiteEventType.findUnique({ + where: { id: ctx.data.id }, + }); + if (!existing) throw new ActionError("Event type not found"); - await prisma.websiteEventType.delete({ where: { id: ctx.data.id } }); - logAudit({ - userId: ctx.session.user.id, - action: "event_type_delete", - target: "WebsiteEventType", - targetId: ctx.data.id, - before: { name: existing.name }, - }); - return actionOk(); - }, + await prisma.websiteEventType.delete({ where: { id: ctx.data.id } }); + logAudit({ + userId: ctx.session.user.id, + action: "event_type_delete", + target: "WebsiteEventType", + targetId: ctx.data.id, + before: { name: existing.name }, + }); + return actionOk(); + }, ); // ── Events ────────────────────────────────────────────────────────── export const createEvent = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: createEventSchema }, - async (ctx) => { - const event = await prisma.websiteEvent.create({ - data: { - ...ctx.data, - hostUserId: Number(ctx.session.user.id), - }, - }); - logAudit({ - userId: ctx.session.user.id, - action: "event_create", - target: "WebsiteEvent", - targetId: event.id, - after: { title: event.title }, - }); - return actionOk({ id: event.id }); - }, + { permission: PERMS.EVENTS_EDIT, schema: createEventSchema }, + async (ctx) => { + const event = await prisma.websiteEvent.create({ + data: { + ...ctx.data, + hostUserId: Number(ctx.session.user.id), + }, + }); + logAudit({ + userId: ctx.session.user.id, + action: "event_create", + target: "WebsiteEvent", + targetId: event.id, + after: { title: event.title }, + }); + return actionOk({ id: event.id }); + }, ); const updateEventInput = updateEventSchema.extend({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const updateEvent = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: updateEventInput }, - async (ctx) => { - const { id, ...data } = ctx.data; - const existing = await prisma.websiteEvent.findUnique({ where: { id } }); - if (!existing) throw new ActionError("Event not found"); + { permission: PERMS.EVENTS_EDIT, schema: updateEventInput }, + async (ctx) => { + const { id, ...data } = ctx.data; + const existing = await prisma.websiteEvent.findUnique({ where: { id } }); + if (!existing) throw new ActionError("Event not found"); - await prisma.websiteEvent.update({ where: { id }, data }); - logAudit({ - userId: ctx.session.user.id, - action: "event_update", - target: "WebsiteEvent", - targetId: id, - before: { title: existing.title, status: existing.status }, - after: data, - }); - return actionOk({ id }); - }, + await prisma.websiteEvent.update({ where: { id }, data }); + logAudit({ + userId: ctx.session.user.id, + action: "event_update", + target: "WebsiteEvent", + targetId: id, + before: { title: existing.title, status: existing.status }, + after: data, + }); + return actionOk({ id }); + }, ); const deleteEventInput = z.object({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const deleteEvent = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: deleteEventInput }, - async (ctx) => { - const existing = await prisma.websiteEvent.findUnique({ where: { id: ctx.data.id } }); - if (!existing) throw new ActionError("Event not found"); + { permission: PERMS.EVENTS_EDIT, schema: deleteEventInput }, + async (ctx) => { + const existing = await prisma.websiteEvent.findUnique({ + where: { id: ctx.data.id }, + }); + if (!existing) throw new ActionError("Event not found"); - await prisma.websiteEvent.delete({ where: { id: ctx.data.id } }); - logAudit({ - userId: ctx.session.user.id, - action: "event_delete", - target: "WebsiteEvent", - targetId: ctx.data.id, - before: { title: existing.title }, - }); - return actionOk(); - }, + await prisma.websiteEvent.delete({ where: { id: ctx.data.id } }); + logAudit({ + userId: ctx.session.user.id, + action: "event_delete", + target: "WebsiteEvent", + targetId: ctx.data.id, + before: { title: existing.title }, + }); + return actionOk(); + }, ); // ── Prizes ────────────────────────────────────────────────────────── export const addEventPrize = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: eventPrizeSchema }, - async (ctx) => { - const prize = await prisma.websiteEventPrize.create({ data: ctx.data }); - return actionOk({ id: prize.id }); - }, + { permission: PERMS.EVENTS_EDIT, schema: eventPrizeSchema }, + async (ctx) => { + const prize = await prisma.websiteEventPrize.create({ data: ctx.data }); + return actionOk({ id: prize.id }); + }, ); const deletePrizeInput = z.object({ id: z.coerce.number().int().positive() }); export const deleteEventPrize = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: deletePrizeInput }, - async (ctx) => { - await prisma.websiteEventPrize.delete({ where: { id: ctx.data.id } }); - return actionOk(); - }, + { permission: PERMS.EVENTS_EDIT, schema: deletePrizeInput }, + async (ctx) => { + await prisma.websiteEventPrize.delete({ where: { id: ctx.data.id } }); + return actionOk(); + }, ); // ── Winners ───────────────────────────────────────────────────────── export const addEventWinner = adminAction( - { permission: PERMS.EVENTS_EDIT, schema: eventWinnerSchema }, - async (ctx) => { - const winner = await prisma.websiteEventWinner.create({ data: ctx.data }); - logAudit({ - userId: ctx.session.user.id, - action: "event_winner_add", - target: "WebsiteEventWinner", - targetId: winner.id, - after: { eventId: ctx.data.eventId, userId: ctx.data.userId, position: ctx.data.position }, - }); - return actionOk({ id: winner.id }); - }, + { permission: PERMS.EVENTS_EDIT, schema: eventWinnerSchema }, + async (ctx) => { + const winner = await prisma.websiteEventWinner.create({ data: ctx.data }); + logAudit({ + userId: ctx.session.user.id, + action: "event_winner_add", + target: "WebsiteEventWinner", + targetId: winner.id, + after: { + eventId: ctx.data.eventId, + userId: ctx.data.userId, + position: ctx.data.position, + }, + }); + return actionOk({ id: winner.id }); + }, ); diff --git a/src/actions/guestbook.ts b/src/actions/guestbook.ts index 9e3c0a87d4..2863699323 100644 --- a/src/actions/guestbook.ts +++ b/src/actions/guestbook.ts @@ -18,42 +18,42 @@ const MESSAGE_MAX = 255; * to revalidate the right page. */ export async function postGuestbook(formData: FormData): Promise { - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - const profileId = Number(formData.get("profileId")); - if (!Number.isInteger(profileId) || profileId <= 0) return; + const profileId = Number(formData.get("profileId")); + if (!Number.isInteger(profileId) || profileId <= 0) return; - const message = String(formData.get("message") ?? "") - .normalize("NFC") - .trim() - .slice(0, MESSAGE_MAX); - if (!message) return; + const message = String(formData.get("message") ?? "") + .normalize("NFC") + .trim() + .slice(0, MESSAGE_MAX); + if (!message) return; - // Block filtered/AI-flagged content before it touches the DB (fail-open). - if (!(await isAllowed(message)).ok) return; + // Block filtered/AI-flagged content before it touches the DB (fail-open). + if (!(await isAllowed(message)).ok) return; - // Optional: used only to revalidate the correct profile route. - const username = String(formData.get("username") ?? "") - .normalize("NFC") - .trim(); + // Optional: used only to revalidate the correct profile route. + const username = String(formData.get("username") ?? "") + .normalize("NFC") + .trim(); - const now = new Date(); - try { - await prisma.websiteUserGuestbooks.create({ - data: { - profileId, - userId, - message, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + const now = new Date(); + try { + await prisma.websiteUserGuestbooks.create({ + data: { + profileId, + userId, + message, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - if (username) revalidatePath(`/u/${username}`); + if (username) revalidatePath(`/u/${username}`); } diff --git a/src/actions/help-tickets.ts b/src/actions/help-tickets.ts index ddb8e87bdb..bcc00d57a4 100644 --- a/src/actions/help-tickets.ts +++ b/src/actions/help-tickets.ts @@ -4,57 +4,57 @@ import { revalidatePath } from "next/cache"; import { z } from "zod"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; -import { moderateOrThrow } from "@/lib/services/moderation"; import { clientIp, rateLimit } from "@/lib/rate-limit"; +import { moderateOrThrow } from "@/lib/services/moderation"; const ticketSchema = z.object({ - title: z.string().min(1, "Title is required").max(255), - content: z.string().min(1, "Content is required").max(5000), + title: z.string().min(1, "Title is required").max(255), + content: z.string().min(1, "Content is required").max(5000), }); export async function createTicket(formData: FormData): Promise { - // Re-read the session user id server-side; never trust a form-supplied id. - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + // Re-read the session user id server-side; never trust a form-supplied id. + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - await clientIp(); - if (!(await rateLimit(`ticket:${userId}`, 3, 60_000)).ok) return; + await clientIp(); + if (!(await rateLimit(`ticket:${userId}`, 3, 60_000)).ok) return; - const raw = { - title: String(formData.get("title") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - content: String(formData.get("content") ?? "") - .normalize("NFC") - .trim() - .slice(0, 5000), - }; + const raw = { + title: String(formData.get("title") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + content: String(formData.get("content") ?? "") + .normalize("NFC") + .trim() + .slice(0, 5000), + }; - const parsed = ticketSchema.safeParse(raw); - if (!parsed.success) return; + const parsed = ticketSchema.safeParse(raw); + if (!parsed.success) return; - const { title, content } = parsed.data; + const { title, content } = parsed.data; - // Moderation check - try { - await moderateOrThrow(`${title} ${content}`); - } catch { - return; - } + // Moderation check + try { + await moderateOrThrow(`${title} ${content}`); + } catch { + return; + } - const now = new Date(); - await prisma.websiteHelpCenterTickets.create({ - data: { - userId, - title, - content, - open: true, - createdAt: now, - updatedAt: now, - }, - }); + const now = new Date(); + await prisma.websiteHelpCenterTickets.create({ + data: { + userId, + title, + content, + open: true, + createdAt: now, + updatedAt: now, + }, + }); - revalidatePath("/help/tickets"); + revalidatePath("/help/tickets"); } diff --git a/src/actions/import-badges.ts b/src/actions/import-badges.ts index 7d9aed5845..40e91de0f9 100644 --- a/src/actions/import-badges.ts +++ b/src/actions/import-badges.ts @@ -4,29 +4,33 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; export async function importBadgeFromRemote({ - code, - name, - description, + code, + name, + description, }: { - code: string; - name: string; - description: string; + code: string; + name: string; + description: string; }) { - await requireStaff(); - try { - await prisma.websiteBadges.upsert({ - where: { badgeKey: code }, - update: { badgeName: name, badgeDescription: description, updatedAt: new Date() }, - create: { - badgeKey: code, - badgeName: name, - badgeDescription: description, - createdAt: new Date(), - updatedAt: new Date(), - }, - }); - return { ok: true as const }; - } catch { - return { ok: false as const, error: "Failed to import badge" }; - } + await requireStaff(); + try { + await prisma.websiteBadges.upsert({ + where: { badgeKey: code }, + update: { + badgeName: name, + badgeDescription: description, + updatedAt: new Date(), + }, + create: { + badgeKey: code, + badgeName: name, + badgeDescription: description, + createdAt: new Date(), + updatedAt: new Date(), + }, + }); + return { ok: true as const }; + } catch { + return { ok: false as const, error: "Failed to import badge" }; + } } diff --git a/src/actions/import-furni.ts b/src/actions/import-furni.ts index 02c911960f..f91fc20de3 100644 --- a/src/actions/import-furni.ts +++ b/src/actions/import-furni.ts @@ -4,17 +4,28 @@ import { z } from "zod"; import { PERMS } from "@/lib/permissions"; import { adminAction } from "@/lib/safe-action"; import { actionOk } from "@/lib/safe-action-shared"; -import { cleanupConvertedSwfs, deleteImportedItem } from "@/lib/services/furni-import"; +import { + cleanupConvertedSwfs, + deleteImportedItem, +} from "@/lib/services/furni-import"; -export const cleanSwfFiles = adminAction({ permission: PERMS.ASSETS_IMPORT }, async () => { - const result = await cleanupConvertedSwfs(); - return actionOk(result as unknown as Record); -}); +export const cleanSwfFiles = adminAction( + { permission: PERMS.ASSETS_IMPORT }, + async () => { + const result = await cleanupConvertedSwfs(); + return actionOk(result as unknown as Record); + }, +); const deleteSchema = z.object({ classname: z.string().trim().min(1) }); export const deleteImportedFurni = adminAction( - { permission: PERMS.ASSETS_IMPORT, schema: deleteSchema }, - async (ctx) => - actionOk((await deleteImportedItem(ctx.data.classname)) as unknown as Record), + { permission: PERMS.ASSETS_IMPORT, schema: deleteSchema }, + async (ctx) => + actionOk( + (await deleteImportedItem(ctx.data.classname)) as unknown as Record< + string, + unknown + >, + ), ); diff --git a/src/actions/link-discord.ts b/src/actions/link-discord.ts index 2a3dc794e5..c66ad28dcf 100644 --- a/src/actions/link-discord.ts +++ b/src/actions/link-discord.ts @@ -1,53 +1,66 @@ "use server"; -import { prisma } from "@/lib/prisma"; import { auth } from "@/lib/auth"; import { logger } from "@/lib/logger"; +import { prisma } from "@/lib/prisma"; export async function linkDiscordId(discordId: string): Promise { - const session = await auth(); - if (!session?.user?.id) return "Not logged in"; + const session = await auth(); + if (!session?.user?.id) return "Not logged in"; - const userId = Number(session.user.id); - if (!discordId || !/^\d{17,20}$/.test(discordId.trim())) { - return "Invalid Discord ID format"; - } + const userId = Number(session.user.id); + if (!discordId || !/^\d{17,20}$/.test(discordId.trim())) { + return "Invalid Discord ID format"; + } - const discordIdClean = discordId.trim(); + const discordIdClean = discordId.trim(); - try { - const existing = await prisma.socialAccounts.findUnique({ - where: { provider_providerId: { provider: "discord", providerId: discordIdClean } }, - select: { userId: true }, - }); - if (existing && Number(existing.userId) !== userId) { - return "This Discord ID is already linked to another account"; - } - } catch { - return "Failed to check Discord ID"; - } + try { + const existing = await prisma.socialAccounts.findUnique({ + where: { + provider_providerId: { + provider: "discord", + providerId: discordIdClean, + }, + }, + select: { userId: true }, + }); + if (existing && Number(existing.userId) !== userId) { + return "This Discord ID is already linked to another account"; + } + } catch { + return "Failed to check Discord ID"; + } - try { - await prisma.socialAccounts.upsert({ - where: { provider_providerId: { provider: "discord", providerId: discordIdClean } }, - create: { - userId: BigInt(userId), - provider: "discord", - providerId: discordIdClean, - createdAt: new Date(), - updatedAt: new Date(), - }, - update: { userId: BigInt(userId), updatedAt: new Date() }, - }); + try { + await prisma.socialAccounts.upsert({ + where: { + provider_providerId: { + provider: "discord", + providerId: discordIdClean, + }, + }, + create: { + userId: BigInt(userId), + provider: "discord", + providerId: discordIdClean, + createdAt: new Date(), + updatedAt: new Date(), + }, + update: { userId: BigInt(userId), updatedAt: new Date() }, + }); - await prisma.user.update({ - where: { id: userId }, - data: { mailVerified: "1" }, - }); + await prisma.user.update({ + where: { id: userId }, + data: { mailVerified: "1" }, + }); - return null; - } catch (e) { - logger.error("Failed to link Discord account", { module: "link-discord", error: (e as Error).message }); - return "Failed to link Discord account"; - } + return null; + } catch (e) { + logger.error("Failed to link Discord account", { + module: "link-discord", + error: (e as Error).message, + }); + return "Failed to link Discord account"; + } } diff --git a/src/actions/messenger.ts b/src/actions/messenger.ts index c0a9f180c1..87b8b7a456 100644 --- a/src/actions/messenger.ts +++ b/src/actions/messenger.ts @@ -18,58 +18,58 @@ import { prisma } from "@/lib/prisma"; * longer shows as pending in the in-game messenger or here. */ export async function acceptFriend(formData: FormData): Promise { - const session = await auth(); - const meId = Number(session?.user?.id); - if (!Number.isInteger(meId) || meId <= 0) return; + const session = await auth(); + const meId = Number(session?.user?.id); + if (!Number.isInteger(meId) || meId <= 0) return; - const requestId = Number(formData.get("requestId")); - if (!Number.isInteger(requestId) || requestId <= 0) return; + const requestId = Number(formData.get("requestId")); + if (!Number.isInteger(requestId) || requestId <= 0) return; - try { - // The request must exist AND be addressed to the session user. - const request = await prisma.messengerFriendrequests.findUnique({ - where: { id: requestId }, - select: { id: true, userFromId: true, userToId: true }, - }); - if (!request || request.userToId !== meId) return; + try { + // The request must exist AND be addressed to the session user. + const request = await prisma.messengerFriendrequests.findUnique({ + where: { id: requestId }, + select: { id: true, userFromId: true, userToId: true }, + }); + if (!request || request.userToId !== meId) return; - const friendId = request.userFromId; - if (!Number.isInteger(friendId) || friendId <= 0 || friendId === meId) { - // Malformed/self request — just clear it. - await prisma.messengerFriendrequests.delete({ where: { id: requestId } }); - return; - } + const friendId = request.userFromId; + if (!Number.isInteger(friendId) || friendId <= 0 || friendId === meId) { + // Malformed/self request — just clear it. + await prisma.messengerFriendrequests.delete({ where: { id: requestId } }); + return; + } - const friendsSince = Math.floor(Date.now() / 1000); + const friendsSince = Math.floor(Date.now() / 1000); - await prisma.$transaction(async (tx) => { - // Don't double-insert if a friendship already exists in either direction. - const existing = await tx.messengerFriendships.findFirst({ - where: { - OR: [ - { userOneId: meId, userTwoId: friendId }, - { userOneId: friendId, userTwoId: meId }, - ], - }, - select: { id: true }, - }); + await prisma.$transaction(async (tx) => { + // Don't double-insert if a friendship already exists in either direction. + const existing = await tx.messengerFriendships.findFirst({ + where: { + OR: [ + { userOneId: meId, userTwoId: friendId }, + { userOneId: friendId, userTwoId: meId }, + ], + }, + select: { id: true }, + }); - if (!existing) { - await tx.messengerFriendships.createMany({ - data: [ - { userOneId: meId, userTwoId: friendId, friendsSince }, - { userOneId: friendId, userTwoId: meId, friendsSince }, - ], - }); - } + if (!existing) { + await tx.messengerFriendships.createMany({ + data: [ + { userOneId: meId, userTwoId: friendId, friendsSince }, + { userOneId: friendId, userTwoId: meId, friendsSince }, + ], + }); + } - await tx.messengerFriendrequests.delete({ where: { id: requestId } }); - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + await tx.messengerFriendrequests.delete({ where: { id: requestId } }); + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - revalidatePath("/messages"); - revalidatePath("/friends"); + revalidatePath("/messages"); + revalidatePath("/friends"); } diff --git a/src/actions/moderation.ts b/src/actions/moderation.ts index 29685447c7..ef2c0b8b5b 100644 --- a/src/actions/moderation.ts +++ b/src/actions/moderation.ts @@ -1,10 +1,10 @@ "use server"; import { z } from "zod"; +import { actionOk, adminAction } from "@/lib/foundation/action"; +import { NotFoundError } from "@/lib/foundation/errors"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; -import { adminAction, actionOk } from "@/lib/foundation/action"; -import { NotFoundError } from "@/lib/foundation/errors"; import { logAudit } from "@/lib/services/audit"; import { rcon } from "@/lib/services/rcon"; @@ -13,73 +13,77 @@ import { rcon } from "@/lib/services/rcon"; const cfhIdSchema = z.object({ ticketId: z.coerce.number().int().positive() }); export const assignCfhTicket = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: cfhIdSchema }, - async (ctx) => { - const ticket = await prisma.supportTickets.findUnique({ where: { id: ctx.data.ticketId } }); - if (!ticket) throw new NotFoundError("SupportTicket", ctx.data.ticketId); + { permission: PERMS.MODERATION_EDIT, schema: cfhIdSchema }, + async (ctx) => { + const ticket = await prisma.supportTickets.findUnique({ + where: { id: ctx.data.ticketId }, + }); + if (!ticket) throw new NotFoundError("SupportTicket", ctx.data.ticketId); - await prisma.supportTickets.update({ - where: { id: ctx.data.ticketId }, - data: { modId: ctx.session.user.id, state: 1 }, - }); + await prisma.supportTickets.update({ + where: { id: ctx.data.ticketId }, + data: { modId: ctx.session.user.id, state: 1 }, + }); - logAudit({ - userId: ctx.session.user.id, - action: "cfh_assign", - target: "support_tickets", - targetId: ctx.data.ticketId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "cfh_assign", + target: "support_tickets", + targetId: ctx.data.ticketId, + }); - return actionOk(); - }, + return actionOk(); + }, ); const cfhStateSchema = z.object({ - ticketId: z.coerce.number().int().positive(), - state: z.coerce.number().int().min(0).max(3), + ticketId: z.coerce.number().int().positive(), + state: z.coerce.number().int().min(0).max(3), }); export const updateCfhState = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: cfhStateSchema }, - async (ctx) => { - const ticket = await prisma.supportTickets.findUnique({ where: { id: ctx.data.ticketId } }); - if (!ticket) throw new NotFoundError("SupportTicket", ctx.data.ticketId); + { permission: PERMS.MODERATION_EDIT, schema: cfhStateSchema }, + async (ctx) => { + const ticket = await prisma.supportTickets.findUnique({ + where: { id: ctx.data.ticketId }, + }); + if (!ticket) throw new NotFoundError("SupportTicket", ctx.data.ticketId); - await prisma.supportTickets.update({ - where: { id: ctx.data.ticketId }, - data: { state: ctx.data.state, modId: ctx.session.user.id }, - }); + await prisma.supportTickets.update({ + where: { id: ctx.data.ticketId }, + data: { state: ctx.data.state, modId: ctx.session.user.id }, + }); - logAudit({ - userId: ctx.session.user.id, - action: "cfh_state_change", - target: "support_tickets", - targetId: ctx.data.ticketId, - before: { state: ticket.state }, - after: { state: ctx.data.state }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "cfh_state_change", + target: "support_tickets", + targetId: ctx.data.ticketId, + before: { state: ticket.state }, + after: { state: ctx.data.state }, + }); - return actionOk(); - }, + return actionOk(); + }, ); export const closeCfhTicket = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: cfhIdSchema }, - async (ctx) => { - await prisma.supportTickets.update({ - where: { id: ctx.data.ticketId }, - data: { state: 2, modId: ctx.session.user.id }, - }); + { permission: PERMS.MODERATION_EDIT, schema: cfhIdSchema }, + async (ctx) => { + await prisma.supportTickets.update({ + where: { id: ctx.data.ticketId }, + data: { state: 2, modId: ctx.session.user.id }, + }); - logAudit({ - userId: ctx.session.user.id, - action: "cfh_close", - target: "support_tickets", - targetId: ctx.data.ticketId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "cfh_close", + target: "support_tickets", + targetId: ctx.data.ticketId, + }); - return actionOk(); - }, + return actionOk(); + }, ); // ── Quick Mod Actions ──────────────────────────────────────────────── @@ -87,120 +91,120 @@ export const closeCfhTicket = adminAction( const userIdSchema = z.object({ userId: z.coerce.number().int().positive() }); export const quickKick = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: userIdSchema }, - async (ctx) => { - await rcon.disconnectUser(ctx.data.userId); + { permission: PERMS.MODERATION_EDIT, schema: userIdSchema }, + async (ctx) => { + await rcon.disconnectUser(ctx.data.userId); - logAudit({ - userId: ctx.session.user.id, - action: "mod_kick", - target: "User", - targetId: ctx.data.userId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "mod_kick", + target: "User", + targetId: ctx.data.userId, + }); - return actionOk(); - }, + return actionOk(); + }, ); const muteSchema = z.object({ - userId: z.coerce.number().int().positive(), - duration: z.coerce.number().int().min(0).max(525600), + userId: z.coerce.number().int().positive(), + duration: z.coerce.number().int().min(0).max(525600), }); export const quickMute = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: muteSchema }, - async (ctx) => { - await rcon.muteUser(ctx.data.userId, ctx.data.duration); + { permission: PERMS.MODERATION_EDIT, schema: muteSchema }, + async (ctx) => { + await rcon.muteUser(ctx.data.userId, ctx.data.duration); - logAudit({ - userId: ctx.session.user.id, - action: "mod_mute", - target: "User", - targetId: ctx.data.userId, - after: { duration: ctx.data.duration }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "mod_mute", + target: "User", + targetId: ctx.data.userId, + after: { duration: ctx.data.duration }, + }); - return actionOk(); - }, + return actionOk(); + }, ); export const quickUnmute = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: userIdSchema }, - async (ctx) => { - await rcon.unmuteUser(ctx.data.userId); + { permission: PERMS.MODERATION_EDIT, schema: userIdSchema }, + async (ctx) => { + await rcon.unmuteUser(ctx.data.userId); - logAudit({ - userId: ctx.session.user.id, - action: "mod_unmute", - target: "User", - targetId: ctx.data.userId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "mod_unmute", + target: "User", + targetId: ctx.data.userId, + }); - return actionOk(); - }, + return actionOk(); + }, ); const alertSchema = z.object({ - userId: z.coerce.number().int().positive(), - message: z.string().min(1).max(500), + userId: z.coerce.number().int().positive(), + message: z.string().min(1).max(500), }); export const quickAlert = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: alertSchema }, - async (ctx) => { - await rcon.alertUser(ctx.data.userId, ctx.data.message); + { permission: PERMS.MODERATION_EDIT, schema: alertSchema }, + async (ctx) => { + await rcon.alertUser(ctx.data.userId, ctx.data.message); - logAudit({ - userId: ctx.session.user.id, - action: "mod_alert", - target: "User", - targetId: ctx.data.userId, - after: { message: ctx.data.message }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "mod_alert", + target: "User", + targetId: ctx.data.userId, + after: { message: ctx.data.message }, + }); - return actionOk(); - }, + return actionOk(); + }, ); const roomIdSchema = z.object({ roomId: z.coerce.number().int().positive() }); export const quickRoomKick = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: roomIdSchema }, - async (ctx) => { - await rcon.kickAll(ctx.data.roomId); + { permission: PERMS.MODERATION_EDIT, schema: roomIdSchema }, + async (ctx) => { + await rcon.kickAll(ctx.data.roomId); - logAudit({ - userId: ctx.session.user.id, - action: "mod_room_kick", - target: "Room", - targetId: ctx.data.roomId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "mod_room_kick", + target: "Room", + targetId: ctx.data.roomId, + }); - return actionOk(); - }, + return actionOk(); + }, ); const broadcastSchema = z.object({ - message: z.string().min(1).max(500), - type: z.enum(["hotel", "staff"]), + message: z.string().min(1).max(500), + type: z.enum(["hotel", "staff"]), }); export const broadcastAlert = adminAction( - { permission: PERMS.MODERATION_EDIT, schema: broadcastSchema }, - async (ctx) => { - if (ctx.data.type === "hotel") { - await rcon.hotelAlert(ctx.data.message); - } else { - await rcon.staffAlert(ctx.data.message); - } + { permission: PERMS.MODERATION_EDIT, schema: broadcastSchema }, + async (ctx) => { + if (ctx.data.type === "hotel") { + await rcon.hotelAlert(ctx.data.message); + } else { + await rcon.staffAlert(ctx.data.message); + } - logAudit({ - userId: ctx.session.user.id, - action: `mod_broadcast_${ctx.data.type}`, - target: "broadcast", - after: { message: ctx.data.message }, - }); + logAudit({ + userId: ctx.session.user.id, + action: `mod_broadcast_${ctx.data.type}`, + target: "broadcast", + after: { message: ctx.data.message }, + }); - return actionOk(); - }, + return actionOk(); + }, ); diff --git a/src/actions/multi-account-detect.ts b/src/actions/multi-account-detect.ts index 056c787cde..6e676d8151 100644 --- a/src/actions/multi-account-detect.ts +++ b/src/actions/multi-account-detect.ts @@ -4,38 +4,54 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; export interface MultiAccountCluster { - key: string; - label: string; - accountCount: number; - accounts: Array<{ id: number; username: string; rank: number; online: string }>; + key: string; + label: string; + accountCount: number; + accounts: Array<{ + id: number; + username: string; + rank: number; + online: string; + }>; } -export async function detectMultiAccounts({ minAccounts, limit }: { minAccounts: number; limit: number }) { - await requireStaff(); - const clusters: MultiAccountCluster[] = []; +export async function detectMultiAccounts({ + minAccounts, + limit, +}: { + minAccounts: number; + limit: number; +}) { + await requireStaff(); + const clusters: MultiAccountCluster[] = []; - const ipGroups = await prisma.user.groupBy({ - by: ["ipCurrent"], - where: { ipCurrent: { not: "" } }, - _count: { id: true }, - having: { id: { _count: { gte: minAccounts } } }, - orderBy: { _count: { id: "desc" } }, - take: limit, - }); + const ipGroups = await prisma.user.groupBy({ + by: ["ipCurrent"], + where: { ipCurrent: { not: "" } }, + _count: { id: true }, + having: { id: { _count: { gte: minAccounts } } }, + orderBy: { _count: { id: "desc" } }, + take: limit, + }); - for (const group of ipGroups) { - const users = await prisma.user.findMany({ - where: { ipCurrent: group.ipCurrent }, - select: { id: true, username: true, rank: true, online: true }, - orderBy: { id: "asc" }, - }); - clusters.push({ - key: group.ipCurrent, - label: `IP: ${group.ipCurrent}`, - accountCount: group._count.id, - accounts: users.map((u) => ({ id: u.id, username: u.username, rank: u.rank, online: u.online })), - }); - } + for (const group of ipGroups) { + const users = await prisma.user.findMany({ + where: { ipCurrent: group.ipCurrent }, + select: { id: true, username: true, rank: true, online: true }, + orderBy: { id: "asc" }, + }); + clusters.push({ + key: group.ipCurrent, + label: `IP: ${group.ipCurrent}`, + accountCount: group._count.id, + accounts: users.map((u) => ({ + id: u.id, + username: u.username, + rank: u.rank, + online: u.online, + })), + }); + } - return { ok: true as const, data: { clusters } }; + return { ok: true as const, data: { clusters } }; } diff --git a/src/actions/password-reset.test.ts b/src/actions/password-reset.test.ts index c31746374c..8b6159ae26 100644 --- a/src/actions/password-reset.test.ts +++ b/src/actions/password-reset.test.ts @@ -1,85 +1,96 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; -const { mockFindFirst, mockUpsert, mockFindUnique, mockUpdate, mockDelete, mockSendMail, mockRedirect } = - vi.hoisted(() => ({ - mockFindFirst: vi.fn(), - mockUpsert: vi.fn(), - mockFindUnique: vi.fn(), - mockUpdate: vi.fn(), - mockDelete: vi.fn(), - mockSendMail: vi.fn(), - mockRedirect: vi.fn(), - })); +const { + mockFindFirst, + mockUpsert, + mockFindUnique, + mockUpdate, + mockDelete, + mockSendMail, + mockRedirect, +} = vi.hoisted(() => ({ + mockFindFirst: vi.fn(), + mockUpsert: vi.fn(), + mockFindUnique: vi.fn(), + mockUpdate: vi.fn(), + mockDelete: vi.fn(), + mockSendMail: vi.fn(), + mockRedirect: vi.fn(), +})); vi.mock("next/navigation", () => ({ - redirect: (...args: unknown[]) => { - mockRedirect(...args); - throw new Error("redirect"); - }, + redirect: (...args: unknown[]) => { + mockRedirect(...args); + throw new Error("redirect"); + }, })); vi.mock("@/lib/prisma", () => ({ - prisma: { - user: { findFirst: mockFindFirst, update: mockUpdate }, - passwordReset: { upsert: mockUpsert, findUnique: mockFindUnique, delete: mockDelete }, - }, + prisma: { + user: { findFirst: mockFindFirst, update: mockUpdate }, + passwordReset: { + upsert: mockUpsert, + findUnique: mockFindUnique, + delete: mockDelete, + }, + }, })); vi.mock("@/lib/services/email", () => ({ - sendMail: mockSendMail, + sendMail: mockSendMail, })); vi.mock("@/lib/rate-limit", () => ({ - rateLimit: vi.fn().mockResolvedValue({ ok: true, retryAfter: 0 }), - clientIp: vi.fn().mockResolvedValue("127.0.0.1"), + rateLimit: vi.fn().mockResolvedValue({ ok: true, retryAfter: 0 }), + clientIp: vi.fn().mockResolvedValue("127.0.0.1"), })); vi.mock("@/env", () => ({ - env: { APP_URL: "http://localhost:3000", HOTEL_NAME: "TestHotel" }, + env: { APP_URL: "http://localhost:3000", HOTEL_NAME: "TestHotel" }, })); import { requestReset } from "./password-reset"; beforeEach(() => { - vi.clearAllMocks(); + vi.clearAllMocks(); }); describe("requestReset", () => { - it("sends a reset email when the user exists", async () => { - mockFindFirst.mockResolvedValue({ id: 1 }); - mockUpsert.mockResolvedValue({}); + it("sends a reset email when the user exists", async () => { + mockFindFirst.mockResolvedValue({ id: 1 }); + mockUpsert.mockResolvedValue({}); - const fd = new FormData(); - fd.set("email", "user@example.com"); + const fd = new FormData(); + fd.set("email", "user@example.com"); - await expect(requestReset(fd)).rejects.toThrow("redirect"); + await expect(requestReset(fd)).rejects.toThrow("redirect"); - expect(mockFindFirst).toHaveBeenCalledWith( - expect.objectContaining({ where: { mail: "user@example.com" } }), - ); - expect(mockUpsert).toHaveBeenCalled(); - expect(mockSendMail).toHaveBeenCalledWith( - "user@example.com", - expect.stringContaining("password reset"), - expect.stringContaining("http://localhost:3000/reset"), - ); - }); + expect(mockFindFirst).toHaveBeenCalledWith( + expect.objectContaining({ where: { mail: "user@example.com" } }), + ); + expect(mockUpsert).toHaveBeenCalled(); + expect(mockSendMail).toHaveBeenCalledWith( + "user@example.com", + expect.stringContaining("password reset"), + expect.stringContaining("http://localhost:3000/reset"), + ); + }); - it("does not send email when user is not found", async () => { - mockFindFirst.mockResolvedValue(null); + it("does not send email when user is not found", async () => { + mockFindFirst.mockResolvedValue(null); - const fd = new FormData(); - fd.set("email", "unknown@example.com"); + const fd = new FormData(); + fd.set("email", "unknown@example.com"); - await expect(requestReset(fd)).rejects.toThrow("redirect"); - expect(mockSendMail).not.toHaveBeenCalled(); - }); + await expect(requestReset(fd)).rejects.toThrow("redirect"); + expect(mockSendMail).not.toHaveBeenCalled(); + }); - it("rate limits and does not throw on email without @", async () => { - const fd = new FormData(); - fd.set("email", "not-an-email"); + it("rate limits and does not throw on email without @", async () => { + const fd = new FormData(); + fd.set("email", "not-an-email"); - await expect(requestReset(fd)).rejects.toThrow("redirect"); - expect(mockFindFirst).not.toHaveBeenCalled(); - }); + await expect(requestReset(fd)).rejects.toThrow("redirect"); + expect(mockFindFirst).not.toHaveBeenCalled(); + }); }); diff --git a/src/actions/password-reset.ts b/src/actions/password-reset.ts index 3381a58ebc..4dbf4db667 100644 --- a/src/actions/password-reset.ts +++ b/src/actions/password-reset.ts @@ -2,104 +2,116 @@ import { createHash, randomBytes, timingSafeEqual } from "node:crypto"; import { redirect } from "next/navigation"; -import { hashPassword } from "@/lib/auth/password"; -import { clientIp, rateLimit } from "@/lib/rate-limit"; -import { prisma } from "@/lib/prisma"; -import { sendMail } from "@/lib/services/email"; import { env } from "@/env"; +import { hashPassword } from "@/lib/auth/password"; +import { prisma } from "@/lib/prisma"; +import { clientIp, rateLimit } from "@/lib/rate-limit"; +import { sendMail } from "@/lib/services/email"; const TOKEN_TTL_MS = 60 * 60 * 1000; // 1 hour function sha256(s: string): string { - return createHash("sha256").update(s).digest("hex"); + return createHash("sha256").update(s).digest("hex"); } export async function requestReset(formData: FormData): Promise { - const email = String(formData.get("email") ?? "") - .normalize("NFC") - .trim() - .toLowerCase(); + const email = String(formData.get("email") ?? "") + .normalize("NFC") + .trim() + .toLowerCase(); - // Throttle reset requests per IP (3 per 15 min) to curb email-bomb abuse. - const allowed = (await rateLimit(`reset:${await clientIp()}`, 3, 15 * 60_000)).ok; + // Throttle reset requests per IP (3 per 15 min) to curb email-bomb abuse. + const allowed = (await rateLimit(`reset:${await clientIp()}`, 3, 15 * 60_000)) + .ok; - // Always respond the same way so we don't reveal which emails exist. - if (allowed && /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { - try { - const user = await prisma.user.findFirst({ where: { mail: email }, select: { id: true } }); - if (user) { - const token = randomBytes(32).toString("hex"); - await prisma.passwordReset.upsert({ - where: { email }, - update: { token: sha256(token), createdAt: new Date() }, - create: { email, token: sha256(token), createdAt: new Date() }, - }); - const link = `${env.APP_URL}/reset?email=${encodeURIComponent(email)}&token=${token}`; - await sendMail( - email, - `${env.HOTEL_NAME} — password reset`, - `

Click to reset your password (valid 1 hour):

${link}

`, - ); - } - } catch { - // swallow — generic response below - } - } + // Always respond the same way so we don't reveal which emails exist. + if (allowed && /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) { + try { + const user = await prisma.user.findFirst({ + where: { mail: email }, + select: { id: true }, + }); + if (user) { + const token = randomBytes(32).toString("hex"); + await prisma.passwordReset.upsert({ + where: { email }, + update: { token: sha256(token), createdAt: new Date() }, + create: { email, token: sha256(token), createdAt: new Date() }, + }); + const link = `${env.APP_URL}/reset?email=${encodeURIComponent(email)}&token=${token}`; + await sendMail( + email, + `${env.HOTEL_NAME} — password reset`, + `

Click to reset your password (valid 1 hour):

${link}

`, + ); + } + } catch { + // swallow — generic response below + } + } - redirect("/forgot?sent=1"); + redirect("/forgot?sent=1"); } export async function resetPassword(formData: FormData): Promise { - const email = String(formData.get("email") ?? "") - .normalize("NFC") - .trim() - .toLowerCase(); - const token = String(formData.get("token") ?? "") - .normalize("NFC") - .trim(); - const password = String(formData.get("password") ?? "").normalize("NFC"); + const email = String(formData.get("email") ?? "") + .normalize("NFC") + .trim() + .toLowerCase(); + const token = String(formData.get("token") ?? "") + .normalize("NFC") + .trim(); + const password = String(formData.get("password") ?? "").normalize("NFC"); - // Throttle reset attempts per IP (5 per 15 min) to prevent token brute-force. - if (!(await rateLimit(`resetpwd:${await clientIp()}`, 5, 15 * 60_000)).ok) { - redirect( - `/reset?email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}&error=${encodeURIComponent("Too many attempts — try again later")}`, - ); - } + // Throttle reset attempts per IP (5 per 15 min) to prevent token brute-force. + if (!(await rateLimit(`resetpwd:${await clientIp()}`, 5, 15 * 60_000)).ok) { + redirect( + `/reset?email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}&error=${encodeURIComponent("Too many attempts — try again later")}`, + ); + } - let error: string | null = null; - if (password.length < 6) error = "Password must be at least 6 characters"; + let error: string | null = null; + if (password.length < 6) error = "Password must be at least 6 characters"; - if (!error) { - try { - const row = await prisma.passwordReset.findUnique({ where: { email } }); - const fresh = row?.createdAt ? Date.now() - row.createdAt.getTime() < TOKEN_TTL_MS : false; - const a = Buffer.from(sha256(token), "hex"); - const b = row ? Buffer.from(row.token, "hex") : Buffer.alloc(a.length); - const match = row != null && a.length === b.length && timingSafeEqual(a, b); + if (!error) { + try { + const row = await prisma.passwordReset.findUnique({ where: { email } }); + const fresh = row?.createdAt + ? Date.now() - row.createdAt.getTime() < TOKEN_TTL_MS + : false; + const a = Buffer.from(sha256(token), "hex"); + const b = row ? Buffer.from(row.token, "hex") : Buffer.alloc(a.length); + const match = + row != null && a.length === b.length && timingSafeEqual(a, b); - if (!row || !fresh || !match) { - error = "This reset link is invalid or has expired"; - } else { - const user = await prisma.user.findFirst({ where: { mail: email }, select: { id: true } }); - if (!user) { - error = "Account not found"; - } else { - await prisma.user.update({ - where: { id: user.id }, - data: { password: await hashPassword(password) }, - }); - await prisma.passwordReset.delete({ where: { email } }).catch(() => {}); - } - } - } catch { - error = "Could not reset the password — try again"; - } - } + if (!row || !fresh || !match) { + error = "This reset link is invalid or has expired"; + } else { + const user = await prisma.user.findFirst({ + where: { mail: email }, + select: { id: true }, + }); + if (!user) { + error = "Account not found"; + } else { + await prisma.user.update({ + where: { id: user.id }, + data: { password: await hashPassword(password) }, + }); + await prisma.passwordReset + .delete({ where: { email } }) + .catch(() => {}); + } + } + } catch { + error = "Could not reset the password — try again"; + } + } - if (error) { - redirect( - `/reset?email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}&error=${encodeURIComponent(error)}`, - ); - } - redirect("/login?reset=1"); + if (error) { + redirect( + `/reset?email=${encodeURIComponent(email)}&token=${encodeURIComponent(token)}&error=${encodeURIComponent(error)}`, + ); + } + redirect("/login?reset=1"); } diff --git a/src/actions/permissions.ts b/src/actions/permissions.ts index c5c4c3c8f6..fe98c15e1d 100644 --- a/src/actions/permissions.ts +++ b/src/actions/permissions.ts @@ -6,135 +6,146 @@ import { PERMS } from "@/lib/permission-slugs"; import { prisma } from "@/lib/prisma"; import { adminAction } from "@/lib/safe-action"; import { ActionError, actionOk } from "@/lib/safe-action-shared"; -import { createEmulatorRank, deleteEmulatorRank, updateEmulatorRank } from "@/lib/services/permission-ranks"; +import { + createEmulatorRank, + deleteEmulatorRank, + updateEmulatorRank, +} from "@/lib/services/permission-ranks"; import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; const createRankSchema = z.object({ - rank_name: z.string().trim().min(1).max(25), - level: z.coerce.number().int().min(1), + rank_name: z.string().trim().min(1).max(25), + level: z.coerce.number().int().min(1), }); export const createRank = adminAction( - { schema: createRankSchema, permission: PERMS.PERMISSIONS_MANAGE }, - async (ctx) => { - const id = await createEmulatorRank(prisma, ctx.data); - await prisma.aclRole.upsert({ - where: { slug: `rank_${id}` }, - create: { - slug: `rank_${id}`, - title: ctx.data.rank_name, - description: "CMS role synchronized from permission_ranks", - }, - update: { title: ctx.data.rank_name }, - }); - await logStaffActivity({ - staffId: ctx.session.user.id, - action: "rank_create", - description: `Created rank #${id}`, - targetType: "rank", - targetId: id, - }); - await rcon.send("updatepermissions"); - revalidateTag("permissions", { expire: 0 }); - return actionOk({ id }); - }, + { schema: createRankSchema, permission: PERMS.PERMISSIONS_MANAGE }, + async (ctx) => { + const id = await createEmulatorRank(prisma, ctx.data); + await prisma.aclRole.upsert({ + where: { slug: `rank_${id}` }, + create: { + slug: `rank_${id}`, + title: ctx.data.rank_name, + description: "CMS role synchronized from permission_ranks", + }, + update: { title: ctx.data.rank_name }, + }); + await logStaffActivity({ + staffId: ctx.session.user.id, + action: "rank_create", + description: `Created rank #${id}`, + targetType: "rank", + targetId: id, + }); + await rcon.send("updatepermissions"); + revalidateTag("permissions", { expire: 0 }); + return actionOk({ id }); + }, ); const deleteRankSchema = z.object({ id: z.coerce.number().int().positive() }); export const deleteRank = adminAction( - { schema: deleteRankSchema, permission: PERMS.PERMISSIONS_MANAGE }, - async (ctx) => { - const users = await prisma.user.count({ where: { rank: ctx.data.id } }); - if (users > 0) throw new ActionError(`Cannot delete: ${users} users have this rank`); - const role = await prisma.aclRole.findFirst({ where: { slug: `rank_${ctx.data.id}` } }); - await deleteEmulatorRank(prisma, ctx.data.id); - if (role) { - await prisma.$transaction([ - prisma.aclModelPermission.deleteMany({ where: { modelId: role.id, modelType: "Role" } }), - prisma.aclModelRole.deleteMany({ where: { roleId: role.id } }), - prisma.aclRole.delete({ where: { id: role.id } }), - ]); - } - await logStaffActivity({ - staffId: ctx.session.user.id, - action: "rank_delete", - description: `Deleted rank #${ctx.data.id}`, - targetType: "rank", - targetId: ctx.data.id, - }); - await rcon.send("updatepermissions"); - revalidateTag("permissions", { expire: 0 }); - return actionOk(); - }, + { schema: deleteRankSchema, permission: PERMS.PERMISSIONS_MANAGE }, + async (ctx) => { + const users = await prisma.user.count({ where: { rank: ctx.data.id } }); + if (users > 0) + throw new ActionError(`Cannot delete: ${users} users have this rank`); + const role = await prisma.aclRole.findFirst({ + where: { slug: `rank_${ctx.data.id}` }, + }); + await deleteEmulatorRank(prisma, ctx.data.id); + if (role) { + await prisma.$transaction([ + prisma.aclModelPermission.deleteMany({ + where: { modelId: role.id, modelType: "Role" }, + }), + prisma.aclModelRole.deleteMany({ where: { roleId: role.id } }), + prisma.aclRole.delete({ where: { id: role.id } }), + ]); + } + await logStaffActivity({ + staffId: ctx.session.user.id, + action: "rank_delete", + description: `Deleted rank #${ctx.data.id}`, + targetType: "rank", + targetId: ctx.data.id, + }); + await rcon.send("updatepermissions"); + revalidateTag("permissions", { expire: 0 }); + return actionOk(); + }, ); const saveRankSchema = z.object({ - id: z.coerce.number().int().positive(), - fields: z.record(z.string(), z.union([z.string(), z.number()])), + id: z.coerce.number().int().positive(), + fields: z.record(z.string(), z.union([z.string(), z.number()])), }); export const saveRank = adminAction( - { schema: saveRankSchema, permission: PERMS.PERMISSIONS_MANAGE }, - async (ctx) => { - await updateEmulatorRank(prisma, ctx.data.id, ctx.data.fields); - if (typeof ctx.data.fields.rank_name === "string") { - await prisma.aclRole.updateMany({ - where: { slug: `rank_${ctx.data.id}` }, - data: { title: ctx.data.fields.rank_name }, - }); - } - await logStaffActivity({ - staffId: ctx.session.user.id, - action: "rank_update", - description: `Updated rank #${ctx.data.id}`, - targetType: "rank", - targetId: ctx.data.id, - }); - await rcon.send("updatepermissions"); - revalidateTag("permissions", { expire: 0 }); - return actionOk(); - }, + { schema: saveRankSchema, permission: PERMS.PERMISSIONS_MANAGE }, + async (ctx) => { + await updateEmulatorRank(prisma, ctx.data.id, ctx.data.fields); + if (typeof ctx.data.fields.rank_name === "string") { + await prisma.aclRole.updateMany({ + where: { slug: `rank_${ctx.data.id}` }, + data: { title: ctx.data.fields.rank_name }, + }); + } + await logStaffActivity({ + staffId: ctx.session.user.id, + action: "rank_update", + description: `Updated rank #${ctx.data.id}`, + targetType: "rank", + targetId: ctx.data.id, + }); + await rcon.send("updatepermissions"); + revalidateTag("permissions", { expire: 0 }); + return actionOk(); + }, ); const setCmsPermsSchema = z.object({ - roleId: z.coerce.number().int().positive(), - permissionSlugs: z.array(z.string().trim().min(1)).max(500), + roleId: z.coerce.number().int().positive(), + permissionSlugs: z.array(z.string().trim().min(1)).max(500), }); export const setCmsPermissions = adminAction( - { schema: setCmsPermsSchema, permission: PERMS.PERMISSIONS_MANAGE }, - async (ctx) => { - const role = await prisma.aclRole.findUnique({ - where: { id: ctx.data.roleId }, - select: { id: true, slug: true }, - }); - if (!role) throw new ActionError("Role not found"); - const permissions = await prisma.aclPermission.findMany({ - where: { slug: { in: ctx.data.permissionSlugs } }, - select: { id: true }, - }); - await prisma.$transaction(async (tx) => { - await tx.aclModelPermission.deleteMany({ where: { modelId: role.id, modelType: "Role" } }); - if (permissions.length) { - await tx.aclModelPermission.createMany({ - data: permissions.map((permission) => ({ - modelId: role.id, - modelType: "Role", - permissionId: permission.id, - })), - }); - } - }); - await logStaffActivity({ - staffId: ctx.session.user.id, - action: "acl_role_permissions_update", - description: `Updated ${permissions.length} permissions for ${role.slug}`, - targetType: "acl_role", - targetId: role.id, - }); - revalidateTag("permissions", { expire: 0 }); - return actionOk(); - }, + { schema: setCmsPermsSchema, permission: PERMS.PERMISSIONS_MANAGE }, + async (ctx) => { + const role = await prisma.aclRole.findUnique({ + where: { id: ctx.data.roleId }, + select: { id: true, slug: true }, + }); + if (!role) throw new ActionError("Role not found"); + const permissions = await prisma.aclPermission.findMany({ + where: { slug: { in: ctx.data.permissionSlugs } }, + select: { id: true }, + }); + await prisma.$transaction(async (tx) => { + await tx.aclModelPermission.deleteMany({ + where: { modelId: role.id, modelType: "Role" }, + }); + if (permissions.length) { + await tx.aclModelPermission.createMany({ + data: permissions.map((permission) => ({ + modelId: role.id, + modelType: "Role", + permissionId: permission.id, + })), + }); + } + }); + await logStaffActivity({ + staffId: ctx.session.user.id, + action: "acl_role_permissions_update", + description: `Updated ${permissions.length} permissions for ${role.slug}`, + targetType: "acl_role", + targetId: role.id, + }); + revalidateTag("permissions", { expire: 0 }); + return actionOk(); + }, ); diff --git a/src/actions/polls.ts b/src/actions/polls.ts index 928da04517..42dafd7388 100644 --- a/src/actions/polls.ts +++ b/src/actions/polls.ts @@ -6,102 +6,110 @@ import { prisma } from "@/lib/prisma"; import { adminAction } from "@/lib/safe-action"; import { ActionError, actionOk } from "@/lib/safe-action-shared"; import { logAudit } from "@/lib/services/audit"; -import { createPollSchema, pollQuestionSchema, updatePollSchema } from "@/lib/validators/poll"; +import { + createPollSchema, + pollQuestionSchema, + updatePollSchema, +} from "@/lib/validators/poll"; // ── Polls ─────────────────────────────────────────────────────────── export const createPoll = adminAction( - { permission: PERMS.POLLS_EDIT, schema: createPollSchema }, - async (ctx) => { - const poll = await prisma.websitePoll.create({ data: ctx.data }); - logAudit({ - userId: ctx.session.user.id, - action: "poll_create", - target: "WebsitePoll", - targetId: poll.id, - after: { title: poll.title }, - }); - return actionOk({ id: poll.id }); - }, + { permission: PERMS.POLLS_EDIT, schema: createPollSchema }, + async (ctx) => { + const poll = await prisma.websitePoll.create({ data: ctx.data }); + logAudit({ + userId: ctx.session.user.id, + action: "poll_create", + target: "WebsitePoll", + targetId: poll.id, + after: { title: poll.title }, + }); + return actionOk({ id: poll.id }); + }, ); const updatePollInput = updatePollSchema.extend({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const updatePoll = adminAction( - { permission: PERMS.POLLS_EDIT, schema: updatePollInput }, - async (ctx) => { - const { id, ...data } = ctx.data; - const existing = await prisma.websitePoll.findUnique({ where: { id } }); - if (!existing) throw new ActionError("Poll not found"); + { permission: PERMS.POLLS_EDIT, schema: updatePollInput }, + async (ctx) => { + const { id, ...data } = ctx.data; + const existing = await prisma.websitePoll.findUnique({ where: { id } }); + if (!existing) throw new ActionError("Poll not found"); - await prisma.websitePoll.update({ where: { id }, data }); - logAudit({ - userId: ctx.session.user.id, - action: "poll_update", - target: "WebsitePoll", - targetId: id, - before: { title: existing.title, status: existing.status }, - after: data, - }); - return actionOk({ id }); - }, + await prisma.websitePoll.update({ where: { id }, data }); + logAudit({ + userId: ctx.session.user.id, + action: "poll_update", + target: "WebsitePoll", + targetId: id, + before: { title: existing.title, status: existing.status }, + after: data, + }); + return actionOk({ id }); + }, ); const deletePollInput = z.object({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const deletePoll = adminAction( - { permission: PERMS.POLLS_EDIT, schema: deletePollInput }, - async (ctx) => { - const existing = await prisma.websitePoll.findUnique({ where: { id: ctx.data.id } }); - if (!existing) throw new ActionError("Poll not found"); + { permission: PERMS.POLLS_EDIT, schema: deletePollInput }, + async (ctx) => { + const existing = await prisma.websitePoll.findUnique({ + where: { id: ctx.data.id }, + }); + if (!existing) throw new ActionError("Poll not found"); - await prisma.websitePoll.delete({ where: { id: ctx.data.id } }); - logAudit({ - userId: ctx.session.user.id, - action: "poll_delete", - target: "WebsitePoll", - targetId: ctx.data.id, - before: { title: existing.title }, - }); - return actionOk(); - }, + await prisma.websitePoll.delete({ where: { id: ctx.data.id } }); + logAudit({ + userId: ctx.session.user.id, + action: "poll_delete", + target: "WebsitePoll", + targetId: ctx.data.id, + before: { title: existing.title }, + }); + return actionOk(); + }, ); // ── Questions ─────────────────────────────────────────────────────── export const addPollQuestion = adminAction( - { permission: PERMS.POLLS_EDIT, schema: pollQuestionSchema }, - async (ctx) => { - const question = await prisma.websitePollQuestion.create({ data: ctx.data }); - return actionOk({ id: question.id }); - }, + { permission: PERMS.POLLS_EDIT, schema: pollQuestionSchema }, + async (ctx) => { + const question = await prisma.websitePollQuestion.create({ + data: ctx.data, + }); + return actionOk({ id: question.id }); + }, ); const updateQuestionInput = pollQuestionSchema.partial().extend({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const updatePollQuestion = adminAction( - { permission: PERMS.POLLS_EDIT, schema: updateQuestionInput }, - async (ctx) => { - const { id, ...data } = ctx.data; - await prisma.websitePollQuestion.update({ where: { id }, data }); - return actionOk({ id }); - }, + { permission: PERMS.POLLS_EDIT, schema: updateQuestionInput }, + async (ctx) => { + const { id, ...data } = ctx.data; + await prisma.websitePollQuestion.update({ where: { id }, data }); + return actionOk({ id }); + }, ); const deleteQuestionInput = z.object({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const deletePollQuestion = adminAction( - { permission: PERMS.POLLS_EDIT, schema: deleteQuestionInput }, - async (ctx) => { - await prisma.websitePollQuestion.delete({ where: { id: ctx.data.id } }); - return actionOk(); - }, + { permission: PERMS.POLLS_EDIT, schema: deleteQuestionInput }, + async (ctx) => { + await prisma.websitePollQuestion.delete({ where: { id: ctx.data.id } }); + return actionOk(); + }, ); diff --git a/src/actions/prefixes.ts b/src/actions/prefixes.ts index 2814740c8b..93ff79a629 100644 --- a/src/actions/prefixes.ts +++ b/src/actions/prefixes.ts @@ -13,130 +13,130 @@ import { ActionError, actionOk } from "@/lib/safe-action-shared"; // ── Create prefix ─────────────────────────────────────────────────── const createPrefixSchema = z.object({ - username: z.string().min(1), - text: z.string().min(1), - color: z.string().min(1), - icon: z.string().optional(), - effect: z.string().optional(), - active: z.coerce.number().int().min(0).max(1).default(1), + username: z.string().min(1), + text: z.string().min(1), + color: z.string().min(1), + icon: z.string().optional(), + effect: z.string().optional(), + active: z.coerce.number().int().min(0).max(1).default(1), }); export const createPrefix = adminAction( - { permission: PERMS.PREFIXES_EDIT, schema: createPrefixSchema }, - async (ctx) => { - const { username, text, color, icon, effect, active } = ctx.data; + { permission: PERMS.PREFIXES_EDIT, schema: createPrefixSchema }, + async (ctx) => { + const { username, text, color, icon, effect, active } = ctx.data; - const users = await prisma.$queryRaw<{ id: number }[]>` + const users = await prisma.$queryRaw<{ id: number }[]>` SELECT id FROM users WHERE username = ${username} LIMIT 1 `; - if (users.length === 0) throw new ActionError("User not found"); + if (users.length === 0) throw new ActionError("User not found"); - await prisma.$executeRaw` + await prisma.$executeRaw` INSERT INTO custom_prefixes (user_id, text, color, icon, effect, active) VALUES (${users[0].id}, ${text}, ${color}, ${icon || ""}, ${effect || ""}, ${active}) `; - return actionOk(); - }, + return actionOk(); + }, ); // ── Update prefix ─────────────────────────────────────────────────── const updatePrefixSchema = z.object({ - id: z.coerce.number().int().positive(), - text: z.string().min(1), - color: z.string().min(1), - icon: z.string().optional(), - effect: z.string().optional(), - active: z.coerce.number().int().min(0).max(1).optional(), + id: z.coerce.number().int().positive(), + text: z.string().min(1), + color: z.string().min(1), + icon: z.string().optional(), + effect: z.string().optional(), + active: z.coerce.number().int().min(0).max(1).optional(), }); export const updatePrefix = adminAction( - { permission: PERMS.PREFIXES_EDIT, schema: updatePrefixSchema }, - async (ctx) => { - const { id, text, color, icon, effect, active } = ctx.data; + { permission: PERMS.PREFIXES_EDIT, schema: updatePrefixSchema }, + async (ctx) => { + const { id, text, color, icon, effect, active } = ctx.data; - await prisma.$executeRaw` + await prisma.$executeRaw` UPDATE custom_prefixes SET text = ${text}, color = ${color}, icon = ${icon || ""}, effect = ${effect || ""}, active = ${active ?? 1} WHERE id = ${id} `; - return actionOk(); - }, + return actionOk(); + }, ); // ── Delete prefix ─────────────────────────────────────────────────── const deletePrefixSchema = z.object({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const deletePrefix = adminAction( - { permission: PERMS.PREFIXES_EDIT, schema: deletePrefixSchema }, - async (ctx) => { - await prisma.$executeRaw`DELETE FROM custom_prefixes WHERE id = ${ctx.data.id}`; - return actionOk(); - }, + { permission: PERMS.PREFIXES_EDIT, schema: deletePrefixSchema }, + async (ctx) => { + await prisma.$executeRaw`DELETE FROM custom_prefixes WHERE id = ${ctx.data.id}`; + return actionOk(); + }, ); // ── Add blacklist word ────────────────────────────────────────────── const addBlacklistWordSchema = z.object({ - word: z.string().min(1).max(100), + word: z.string().min(1).max(100), }); export const addBlacklistWord = adminAction( - { permission: PERMS.PREFIXES_EDIT, schema: addBlacklistWordSchema }, - async (ctx) => { - await prisma.$executeRaw` + { permission: PERMS.PREFIXES_EDIT, schema: addBlacklistWordSchema }, + async (ctx) => { + await prisma.$executeRaw` INSERT INTO custom_prefix_blacklist (word) VALUES (${ctx.data.word.trim()}) `; - return actionOk(); - }, + return actionOk(); + }, ); // ── Remove blacklist word ─────────────────────────────────────────── const removeBlacklistWordSchema = z.object({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const removeBlacklistWord = adminAction( - { permission: PERMS.PREFIXES_EDIT, schema: removeBlacklistWordSchema }, - async (ctx) => { - await prisma.$executeRaw`DELETE FROM custom_prefix_blacklist WHERE id = ${ctx.data.id}`; - return actionOk(); - }, + { permission: PERMS.PREFIXES_EDIT, schema: removeBlacklistWordSchema }, + async (ctx) => { + await prisma.$executeRaw`DELETE FROM custom_prefix_blacklist WHERE id = ${ctx.data.id}`; + return actionOk(); + }, ); // ── Update prefix settings ────────────────────────────────────────── const SETTINGS_WHITELIST = new Set([ - "enabled", - "max_length", - "min_rank", - "allow_colors", - "allow_bold", - "allow_italic", - "default_color", + "enabled", + "max_length", + "min_rank", + "allow_colors", + "allow_bold", + "allow_italic", + "default_color", ]); const updatePrefixSettingsSchema = z.object({ - settings: z.record(z.string(), z.string()), + settings: z.record(z.string(), z.string()), }); export const updatePrefixSettings = adminAction( - { permission: PERMS.PREFIXES_EDIT, schema: updatePrefixSettingsSchema }, - async (ctx) => { - for (const [key, value] of Object.entries(ctx.data.settings)) { - if (!SETTINGS_WHITELIST.has(key)) continue; - await prisma.$executeRaw` + { permission: PERMS.PREFIXES_EDIT, schema: updatePrefixSettingsSchema }, + async (ctx) => { + for (const [key, value] of Object.entries(ctx.data.settings)) { + if (!SETTINGS_WHITELIST.has(key)) continue; + await prisma.$executeRaw` INSERT INTO custom_prefix_settings (\`key\`, \`value\`) VALUES (${key}, ${value}) ON DUPLICATE KEY UPDATE \`value\` = ${value} `; - } - return actionOk(); - }, + } + return actionOk(); + }, ); diff --git a/src/actions/radio-apply.ts b/src/actions/radio-apply.ts index b71600d073..4836d97dd5 100644 --- a/src/actions/radio-apply.ts +++ b/src/actions/radio-apply.ts @@ -11,10 +11,10 @@ const TEXT_MAX = 5000; const STYLE_MAX = 5000; function str(form: FormData, key: string, max: number): string { - return String(form.get(key) ?? "") - .normalize("NFC") - .trim() - .slice(0, max); + return String(form.get(key) ?? "") + .normalize("NFC") + .trim() + .slice(0, max); } /** @@ -26,43 +26,43 @@ function str(form: FormData, key: string, max: number): string { * UnsignedBigInt, hence the BigInt() coercion. */ export async function applyDj(formData: FormData): Promise { - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - const realName = str(formData, "realName", NAME_MAX); - const availability = str(formData, "availability", TEXT_MAX); - const motivation = str(formData, "motivation", TEXT_MAX); - const experience = str(formData, "experience", TEXT_MAX); - const musicStyle = str(formData, "musicStyle", STYLE_MAX); + const realName = str(formData, "realName", NAME_MAX); + const availability = str(formData, "availability", TEXT_MAX); + const motivation = str(formData, "motivation", TEXT_MAX); + const experience = str(formData, "experience", TEXT_MAX); + const musicStyle = str(formData, "musicStyle", STYLE_MAX); - const ageRaw = Number(formData.get("age")); - const age = Number.isInteger(ageRaw) ? ageRaw : 0; + const ageRaw = Number(formData.get("age")); + const age = Number.isInteger(ageRaw) ? ageRaw : 0; - // Required fields per the schema (NOT NULL): real_name, age, availability, - // motivation. experience + music_style are nullable. - if (!realName || !availability || !motivation || age <= 0) return; + // Required fields per the schema (NOT NULL): real_name, age, availability, + // motivation. experience + music_style are nullable. + if (!realName || !availability || !motivation || age <= 0) return; - const now = new Date(); - try { - await prisma.radioApplications.create({ - data: { - userId: BigInt(userId), - realName, - age, - availability, - motivation, - experience: experience || null, - musicStyle: musicStyle || null, - status: "pending", - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // DB unavailable or duplicate — fail soft; nothing to persist. - return; - } + const now = new Date(); + try { + await prisma.radioApplications.create({ + data: { + userId: BigInt(userId), + realName, + age, + availability, + motivation, + experience: experience || null, + musicStyle: musicStyle || null, + status: "pending", + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // DB unavailable or duplicate — fail soft; nothing to persist. + return; + } - revalidatePath("/radio/apply"); + revalidatePath("/radio/apply"); } diff --git a/src/actions/radio-requests.ts b/src/actions/radio-requests.ts index 2a08c79a4a..8a053615b7 100644 --- a/src/actions/radio-requests.ts +++ b/src/actions/radio-requests.ts @@ -8,35 +8,35 @@ const SONG_MAX = 255; const ARTIST_MAX = 255; export async function submitRequest(formData: FormData): Promise { - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - const songTitle = String(formData.get("songTitle") ?? "") - .normalize("NFC") - .trim() - .slice(0, SONG_MAX); - const artist = String(formData.get("artist") ?? "") - .normalize("NFC") - .trim() - .slice(0, ARTIST_MAX); - if (!songTitle && !artist) return; + const songTitle = String(formData.get("songTitle") ?? "") + .normalize("NFC") + .trim() + .slice(0, SONG_MAX); + const artist = String(formData.get("artist") ?? "") + .normalize("NFC") + .trim() + .slice(0, ARTIST_MAX); + if (!songTitle && !artist) return; - const now = new Date(); - try { - await prisma.radioSongRequests.create({ - data: { - userId: BigInt(userId), - songTitle: songTitle || null, - artist: artist || null, - submittedAt: now, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - return; - } + const now = new Date(); + try { + await prisma.radioSongRequests.create({ + data: { + userId: BigInt(userId), + songTitle: songTitle || null, + artist: artist || null, + submittedAt: now, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + return; + } - revalidatePath("/radio/requests"); + revalidatePath("/radio/requests"); } diff --git a/src/actions/radio-shouts.ts b/src/actions/radio-shouts.ts index 4fa4b02996..bde86d558f 100644 --- a/src/actions/radio-shouts.ts +++ b/src/actions/radio-shouts.ts @@ -4,11 +4,11 @@ import { revalidatePath } from "next/cache"; import { z } from "zod"; import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; -import { moderateOrThrow } from "@/lib/services/moderation"; import { clientIp, rateLimit } from "@/lib/rate-limit"; +import { moderateOrThrow } from "@/lib/services/moderation"; const shoutSchema = z.object({ - message: z.string().min(1, "Message is required").max(255), + message: z.string().min(1, "Message is required").max(255), }); /** @@ -20,46 +20,46 @@ const shoutSchema = z.object({ * session id is widened to BigInt for the insert. */ export async function postShout(formData: FormData): Promise { - const session = await auth(); - const userId = Number(session?.user?.id); - if (!Number.isInteger(userId) || userId <= 0) return; + const session = await auth(); + const userId = Number(session?.user?.id); + if (!Number.isInteger(userId) || userId <= 0) return; - await clientIp(); - if (!(await rateLimit(`shout:${userId}`, 5, 30_000)).ok) return; + await clientIp(); + if (!(await rateLimit(`shout:${userId}`, 5, 30_000)).ok) return; - const raw = { - message: String(formData.get("message") ?? "") - .normalize("NFC") - .trim() - .slice(0, 255), - }; + const raw = { + message: String(formData.get("message") ?? "") + .normalize("NFC") + .trim() + .slice(0, 255), + }; - const parsed = shoutSchema.safeParse(raw); - if (!parsed.success) return; + const parsed = shoutSchema.safeParse(raw); + if (!parsed.success) return; - const { message } = parsed.data; + const { message } = parsed.data; - // Moderation check - try { - await moderateOrThrow(message); - } catch { - return; - } + // Moderation check + try { + await moderateOrThrow(message); + } catch { + return; + } - const now = new Date(); - try { - await prisma.radioShouts.create({ - data: { - userId: BigInt(userId), - message, - createdAt: now, - updatedAt: now, - }, - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + const now = new Date(); + try { + await prisma.radioShouts.create({ + data: { + userId: BigInt(userId), + message, + createdAt: now, + updatedAt: now, + }, + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - revalidatePath("/radio/shouts"); + revalidatePath("/radio/shouts"); } diff --git a/src/actions/referral.ts b/src/actions/referral.ts index d5cde7f215..e69a409f2e 100644 --- a/src/actions/referral.ts +++ b/src/actions/referral.ts @@ -27,121 +27,143 @@ import { type CurrencyName, sendCurrency } from "@/lib/services/send-currency"; * redirects with ?claimed=1. redirect() is called OUTSIDE the try/catch so its * internal control-flow throw is never swallowed. */ -const VALID_CURRENCIES = new Set(["credits", "duckets", "diamonds", "points"]); +const VALID_CURRENCIES = new Set([ + "credits", + "duckets", + "diamonds", + "points", +]); export async function claimReferral(_formData: FormData): Promise { - let outcome: "claimed" | "not_enough" | "no_referrals" | "bad_config" | "error" = "error"; + let outcome: + | "claimed" + | "not_enough" + | "no_referrals" + | "bad_config" + | "error" = "error"; - try { - const session = await auth(); - if (!session?.user?.id) { - redirect("/login"); - } + try { + const session = await auth(); + if (!session?.user?.id) { + redirect("/login"); + } - const userId = Number(session.user.id); - if (!Number.isFinite(userId) || userId <= 0) { - redirect("/login"); - } + const userId = Number(session.user.id); + if (!Number.isFinite(userId) || userId <= 0) { + redirect("/login"); + } - // Reward configuration (CMS-owned website_settings). AtomCMS defaults: - // 5 referrals needed, 30 diamonds reward. - const [neededRaw, amountRaw, currencyRaw] = await Promise.all([ - prisma.websiteSetting - .findUnique({ where: { key: "referrals_needed" }, select: { value: true } }) - .catch(() => null), - prisma.websiteSetting - .findUnique({ where: { key: "referral_reward_amount" }, select: { value: true } }) - .catch(() => null), - // The seeded key is referral_reward_currency_type; fall back to the - // shorter referral_reward_currency name if that is what is configured. - prisma.websiteSetting - .findFirst({ - where: { key: { in: ["referral_reward_currency_type", "referral_reward_currency"] } }, - select: { value: true }, - }) - .catch(() => null), - ]); + // Reward configuration (CMS-owned website_settings). AtomCMS defaults: + // 5 referrals needed, 30 diamonds reward. + const [neededRaw, amountRaw, currencyRaw] = await Promise.all([ + prisma.websiteSetting + .findUnique({ + where: { key: "referrals_needed" }, + select: { value: true }, + }) + .catch(() => null), + prisma.websiteSetting + .findUnique({ + where: { key: "referral_reward_amount" }, + select: { value: true }, + }) + .catch(() => null), + // The seeded key is referral_reward_currency_type; fall back to the + // shorter referral_reward_currency name if that is what is configured. + prisma.websiteSetting + .findFirst({ + where: { + key: { + in: ["referral_reward_currency_type", "referral_reward_currency"], + }, + }, + select: { value: true }, + }) + .catch(() => null), + ]); - const needed = Number.parseInt(neededRaw?.value ?? "5", 10) || 5; - const amount = Number.parseInt(amountRaw?.value ?? "30", 10); - const currency = (currencyRaw?.value ?? "diamonds").trim().toLowerCase() as CurrencyName; + const needed = Number.parseInt(neededRaw?.value ?? "5", 10) || 5; + const amount = Number.parseInt(amountRaw?.value ?? "30", 10); + const currency = (currencyRaw?.value ?? "diamonds") + .trim() + .toLowerCase() as CurrencyName; - // The user's referral tally lives in user_referrals (one row per user). - const referrals = await prisma.userReferrals - .findFirst({ - where: { userId }, - select: { id: true, referralsTotal: true }, - orderBy: { id: "desc" }, - }) - .catch(() => null); + // The user's referral tally lives in user_referrals (one row per user). + const referrals = await prisma.userReferrals + .findFirst({ + where: { userId }, + select: { id: true, referralsTotal: true }, + orderBy: { id: "desc" }, + }) + .catch(() => null); - const total = referrals ? Number(referrals.referralsTotal) : 0; + const total = referrals ? Number(referrals.referralsTotal) : 0; - if (!referrals || total <= 0) { - outcome = "no_referrals"; - } else if (total < needed) { - outcome = "not_enough"; - } else if (!VALID_CURRENCIES.has(currency) || !(amount > 0)) { - // Misconfigured reward — keep it conservative and grant nothing. - outcome = "bad_config"; - } else { - // Spend the threshold first so a concurrent double-submit can't claim - // twice off the same balance, then deliver the reward and log it. - await prisma.userReferrals.update({ - where: { id: referrals.id }, - data: { referralsTotal: { decrement: needed } }, - }); + if (!referrals || total <= 0) { + outcome = "no_referrals"; + } else if (total < needed) { + outcome = "not_enough"; + } else if (!VALID_CURRENCIES.has(currency) || !(amount > 0)) { + // Misconfigured reward — keep it conservative and grant nothing. + outcome = "bad_config"; + } else { + // Spend the threshold first so a concurrent double-submit can't claim + // twice off the same balance, then deliver the reward and log it. + await prisma.userReferrals.update({ + where: { id: referrals.id }, + data: { referralsTotal: { decrement: needed } }, + }); - try { - await sendCurrency({ rcon, db: prisma }, userId, currency, amount); - } catch { - // sendCurrency already falls back to a direct DB write; if it still - // throws the spend stands. Roll the threshold back so the user isn't - // charged for an undelivered reward. - await prisma.userReferrals - .update({ - where: { id: referrals.id }, - data: { referralsTotal: { increment: needed } }, - }) - .catch(() => {}); - outcome = "error"; - throw new Error("currency-delivery-failed"); - } + try { + await sendCurrency({ rcon, db: prisma }, userId, currency, amount); + } catch { + // sendCurrency already falls back to a direct DB write; if it still + // throws the spend stands. Roll the threshold back so the user isn't + // charged for an undelivered reward. + await prisma.userReferrals + .update({ + where: { id: referrals.id }, + data: { referralsTotal: { increment: needed } }, + }) + .catch(() => {}); + outcome = "error"; + throw new Error("currency-delivery-failed"); + } - await prisma.claimedReferralLogs - .create({ - data: { - userId, - ipAddress: await clientIp(), - createdAt: new Date(), - updatedAt: new Date(), - }, - }) - .catch(() => { - // Best-effort audit log; the reward already landed. - }); + await prisma.claimedReferralLogs + .create({ + data: { + userId, + ipAddress: await clientIp(), + createdAt: new Date(), + updatedAt: new Date(), + }, + }) + .catch(() => { + // Best-effort audit log; the reward already landed. + }); - outcome = "claimed"; - } - } catch (err) { - // redirect() throws a NEXT_REDIRECT control-flow signal — re-throw it so the - // navigation actually happens instead of being treated as a failure. - if ( - err && - typeof err === "object" && - "digest" in err && - typeof (err as { digest?: unknown }).digest === "string" && - (err as { digest: string }).digest.startsWith("NEXT_REDIRECT") - ) { - throw err; - } - if (outcome === "claimed") outcome = "error"; - } + outcome = "claimed"; + } + } catch (err) { + // redirect() throws a NEXT_REDIRECT control-flow signal — re-throw it so the + // navigation actually happens instead of being treated as a failure. + if ( + err && + typeof err === "object" && + "digest" in err && + typeof (err as { digest?: unknown }).digest === "string" && + (err as { digest: string }).digest.startsWith("NEXT_REDIRECT") + ) { + throw err; + } + if (outcome === "claimed") outcome = "error"; + } - revalidatePath("/me"); + revalidatePath("/me"); - if (outcome === "claimed") { - redirect("/me?claimed=1"); - } - redirect(`/me?error=${outcome}`); + if (outcome === "claimed") { + redirect("/me?claimed=1"); + } + redirect(`/me?error=${outcome}`); } diff --git a/src/actions/register.ts b/src/actions/register.ts index 924e633737..edf176e5e7 100644 --- a/src/actions/register.ts +++ b/src/actions/register.ts @@ -11,118 +11,129 @@ import { checkVpn } from "@/lib/services/ip-lookup"; import { siteSettings } from "@/lib/services/site-settings"; const registerSchema = z.object({ - username: z - .string() - .min(3, "Username must be at least 3 characters") - .max(25, "Username must be at most 25 characters") - .regex(/^[A-Za-z0-9_\-=?!@:.,]+$/, "Username contains invalid characters"), - mail: z.string().email("Enter a valid email address").optional().or(z.literal("")), - password: z - .string() - .min(8, "Password must be at least 8 characters") - .regex(/[A-Z]/, "Password must contain at least one uppercase letter") - .regex(/[a-z]/, "Password must contain at least one lowercase letter") - .regex(/[0-9]/, "Password must contain at least one digit"), - look: z.string().optional(), + username: z + .string() + .min(3, "Username must be at least 3 characters") + .max(25, "Username must be at most 25 characters") + .regex(/^[A-Za-z0-9_\-=?!@:.,]+$/, "Username contains invalid characters"), + mail: z + .string() + .email("Enter a valid email address") + .optional() + .or(z.literal("")), + password: z + .string() + .min(8, "Password must be at least 8 characters") + .regex(/[A-Z]/, "Password must contain at least one uppercase letter") + .regex(/[a-z]/, "Password must contain at least one lowercase letter") + .regex(/[0-9]/, "Password must contain at least one digit"), + look: z.string().optional(), }); // A valid starter Habbo figure so the avatar renders in-client immediately. const DEFAULT_LOOK = "hr-100-.hd-180-1.ch-255-66.lg-280-110.sh-305-62"; -export async function register(prevState: string | null, formData: FormData): Promise { - const raw = { - username: String(formData.get("username") ?? "") - .normalize("NFC") - .trim(), - mail: String(formData.get("mail") ?? "") - .normalize("NFC") - .trim() - .toLowerCase(), - password: String(formData.get("password") ?? "").normalize("NFC"), - look: - String(formData.get("look") ?? "") - .normalize("NFC") - .trim() || DEFAULT_LOOK, - }; +export async function register( + _prevState: string | null, + formData: FormData, +): Promise { + const raw = { + username: String(formData.get("username") ?? "") + .normalize("NFC") + .trim(), + mail: String(formData.get("mail") ?? "") + .normalize("NFC") + .trim() + .toLowerCase(), + password: String(formData.get("password") ?? "").normalize("NFC"), + look: + String(formData.get("look") ?? "") + .normalize("NFC") + .trim() || DEFAULT_LOOK, + }; - const parsed = registerSchema.safeParse(raw); - if (!parsed.success) { - return parsed.error.errors[0]?.message ?? "Invalid input"; - } + const parsed = registerSchema.safeParse(raw); + if (!parsed.success) { + return parsed.error.errors[0]?.message ?? "Invalid input"; + } - const { username, mail, password, look } = parsed.data; - const hasEmail = !!mail; - const ip = await clientIp(); + const { username, mail, password, look } = parsed.data; + const hasEmail = !!mail; + const ip = await clientIp(); - // Throttle sign-ups per IP (5 per 10 minutes) to curb account spam. - if (!(await rateLimit(`register:${ip}`, 5, 10 * 60_000)).ok) { - return "Too many sign-up attempts. Please wait a few minutes and try again."; - } + // Throttle sign-ups per IP (5 per 10 minutes) to curb account spam. + if (!(await rateLimit(`register:${ip}`, 5, 10 * 60_000)).ok) { + return "Too many sign-up attempts. Please wait a few minutes and try again."; + } - // CAPTCHA (Turnstile / reCAPTCHA) — only enforced when configured in settings. - const cfg = await captchaConfig(); - if (cfg.provider !== "none") { - const token = String(formData.get(cfg.field) ?? "").normalize("NFC"); - if (!(await verifyCaptcha(token, ip))) return "Captcha verification failed. Please try again."; - } + // CAPTCHA (Turnstile / reCAPTCHA) — only enforced when configured in settings. + const cfg = await captchaConfig(); + if (cfg.provider !== "none") { + const token = String(formData.get(cfg.field) ?? "").normalize("NFC"); + if (!(await verifyCaptcha(token, ip))) + return "Captcha verification failed. Please try again."; + } - // VPN/proxy block (only when enabled in /admin/vpn). - if ((await checkVpn(ip)).blocked) { - return ( - (await siteSettings.get("vpn_block_message", "")) || - "Registrations from VPN/proxy connections are not allowed." - ); - } + // VPN/proxy block (only when enabled in /admin/vpn). + if ((await checkVpn(ip)).blocked) { + return ( + (await siteSettings.get("vpn_block_message", "")) || + "Registrations from VPN/proxy connections are not allowed." + ); + } - // Max accounts per IP (0 / unset = unlimited), mirrors AtomCMS. - const max = Number(await siteSettings.get("max_accounts_per_ip", "0")) || 0; - if (max > 0) { - const count = await prisma.user.count({ where: { ipRegister: ip } }).catch(() => 0); - if (count >= max) return "You have reached the maximum number of accounts for your connection."; - } + // Max accounts per IP (0 / unset = unlimited), mirrors AtomCMS. + const max = Number(await siteSettings.get("max_accounts_per_ip", "0")) || 0; + if (max > 0) { + const count = await prisma.user + .count({ where: { ipRegister: ip } }) + .catch(() => 0); + if (count >= max) + return "You have reached the maximum number of accounts for your connection."; + } - // Uniqueness check. - try { - const existing = await prisma.user.findUnique({ - where: { username }, - select: { id: true }, - }); - if (existing) return "That username is already taken"; - } catch { - return "Registration is temporarily unavailable"; - } + // Uniqueness check. + try { + const existing = await prisma.user.findUnique({ + where: { username }, + select: { id: true }, + }); + if (existing) return "That username is already taken"; + } catch { + return "Registration is temporarily unavailable"; + } - const now = Math.floor(Date.now() / 1000); - try { - await prisma.user.create({ - data: { - username, - password: await hashPassword(password), - mail: hasEmail ? mail : null, - accountCreated: now, - ipRegister: ip, - ipCurrent: ip, - look, - }, - select: { id: true }, - }); + const now = Math.floor(Date.now() / 1000); + try { + await prisma.user.create({ + data: { + username, + password: await hashPassword(password), + mail: hasEmail ? mail : null, + accountCreated: now, + ipRegister: ip, + ipCurrent: ip, + look, + }, + select: { id: true }, + }); - if (hasEmail) { - try { - await sendVerification(mail); - } catch { - // No-op: account is created; user can request a new link later. - } - } - } catch { - return "Could not create the account (is the username unique?)"; - } + if (hasEmail) { + try { + await sendVerification(mail); + } catch { + // No-op: account is created; user can request a new link later. + } + } + } catch { + return "Could not create the account (is the username unique?)"; + } - if (hasEmail) { - redirect("/login?registered=1"); - } else { - const { signIn } = await import("@/lib/auth"); - await signIn("credentials", { username, password, redirect: false }); - redirect("/verify?method=discord"); - } + if (hasEmail) { + redirect("/login?registered=1"); + } else { + const { signIn } = await import("@/lib/auth"); + await signIn("credentials", { username, password, redirect: false }); + redirect("/verify?method=discord"); + } } diff --git a/src/actions/rooms.ts b/src/actions/rooms.ts index a4fb121b0e..298d6aa72d 100644 --- a/src/actions/rooms.ts +++ b/src/actions/rooms.ts @@ -7,88 +7,110 @@ import { rcon } from "@/lib/services/rcon"; import { logStaffActivity } from "@/lib/services/staff-activity"; export async function updateRoomItem(payload: Record) { - const staff = await requireStaff(); - const { roomId, itemId, ...data } = payload as { roomId: number; itemId: number; [key: string]: unknown }; - await prisma.items.update({ where: { id: itemId }, data: data as any }); - await logStaffActivity({ - staffId: staff.id, - action: "room_item_update", - description: `Updated item #${itemId} in room #${roomId}`, - targetType: "room_item", - targetId: itemId, - }); - revalidatePath(`/admin/rooms/${roomId}/furni`); + const staff = await requireStaff(); + const { roomId, itemId, ...data } = payload as { + roomId: number; + itemId: number; + [key: string]: unknown; + }; + await prisma.items.update({ where: { id: itemId }, data: data as any }); + await logStaffActivity({ + staffId: staff.id, + action: "room_item_update", + description: `Updated item #${itemId} in room #${roomId}`, + targetType: "room_item", + targetId: itemId, + }); + revalidatePath(`/admin/rooms/${roomId}/furni`); } -export async function bulkDeleteRoomItems({ roomId, itemIds }: { roomId: number; itemIds: number[] }) { - const staff = await requireStaff(); - await prisma.items.deleteMany({ where: { id: { in: itemIds }, roomId } }); - await logStaffActivity({ - staffId: staff.id, - action: "room_items_bulk_delete", - description: `Deleted ${itemIds.length} item(s) from room #${roomId}`, - targetType: "room_item", - }); - revalidatePath(`/admin/rooms/${roomId}/furni`); +export async function bulkDeleteRoomItems({ + roomId, + itemIds, +}: { + roomId: number; + itemIds: number[]; +}) { + const staff = await requireStaff(); + await prisma.items.deleteMany({ where: { id: { in: itemIds }, roomId } }); + await logStaffActivity({ + staffId: staff.id, + action: "room_items_bulk_delete", + description: `Deleted ${itemIds.length} item(s) from room #${roomId}`, + targetType: "room_item", + }); + revalidatePath(`/admin/rooms/${roomId}/furni`); } -export async function deleteRoomItem({ roomId, itemId }: { roomId: number; itemId: number }) { - const staff = await requireStaff(); - await prisma.items.delete({ where: { id: itemId } }); - await logStaffActivity({ - staffId: staff.id, - action: "room_item_delete", - description: `Deleted item #${itemId} from room #${roomId}`, - targetType: "room_item", - targetId: itemId, - }); - revalidatePath(`/admin/rooms/${roomId}/furni`); +export async function deleteRoomItem({ + roomId, + itemId, +}: { + roomId: number; + itemId: number; +}) { + const staff = await requireStaff(); + await prisma.items.delete({ where: { id: itemId } }); + await logStaffActivity({ + staffId: staff.id, + action: "room_item_delete", + description: `Deleted item #${itemId} from room #${roomId}`, + targetType: "room_item", + targetId: itemId, + }); + revalidatePath(`/admin/rooms/${roomId}/furni`); } -export async function roomRconAction({ roomId, action }: { roomId: number; action: string }) { - await requireStaff(); - if (action === "reload") { - await rcon.send("reloadroom", { room_id: roomId }); - } else if (action === "kick") { - await rcon.send("kickall", { room_id: roomId }); - } else if (action === "lock") { - await rcon.send("updateroom", { room_id: roomId, state: "locked" }); - } else if (action === "unlock") { - await rcon.send("updateroom", { room_id: roomId, state: "open" }); - } +export async function roomRconAction({ + roomId, + action, +}: { + roomId: number; + action: string; +}) { + await requireStaff(); + if (action === "reload") { + await rcon.send("reloadroom", { room_id: roomId }); + } else if (action === "kick") { + await rcon.send("kickall", { room_id: roomId }); + } else if (action === "lock") { + await rcon.send("updateroom", { room_id: roomId, state: "locked" }); + } else if (action === "unlock") { + await rcon.send("updateroom", { room_id: roomId, state: "open" }); + } } export async function deleteRoom({ id }: { id: number }) { - const staff = await requireStaff(); - await prisma.rooms.delete({ where: { id } }); - await logStaffActivity({ - staffId: staff.id, - action: "room_delete", - description: `Deleted room #${id}`, - targetType: "room", - targetId: id, - }); - revalidatePath("/admin/rooms"); + const staff = await requireStaff(); + await prisma.rooms.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "room_delete", + description: `Deleted room #${id}`, + targetType: "room", + targetId: id, + }); + revalidatePath("/admin/rooms"); } export async function updateRoom({ - id, - ...data + id, + ...data }: { - id: number; - name?: string; - description?: string; - state?: string; - usersMax?: number; + id: number; + name?: string; + description?: string; + state?: string; + usersMax?: number; }) { - const staff = await requireStaff(); - await prisma.rooms.update({ where: { id }, data: data as any }); - await logStaffActivity({ - staffId: staff.id, - action: "room_update", - description: `Updated room #${id}`, - targetType: "room", - targetId: id, - }); - revalidatePath(`/admin/rooms/${id}`); + const staff = await requireStaff(); + await prisma.rooms.update({ where: { id }, data: data as any }); + await logStaffActivity({ + staffId: staff.id, + action: "room_update", + description: `Updated room #${id}`, + targetType: "room", + targetId: id, + }); + revalidatePath(`/admin/rooms/${id}`); } diff --git a/src/actions/save-favicon.ts b/src/actions/save-favicon.ts index b5ca68c013..b34b31eba8 100644 --- a/src/actions/save-favicon.ts +++ b/src/actions/save-favicon.ts @@ -1,108 +1,131 @@ "use server"; +import { mkdir, unlink, writeFile } from "node:fs/promises"; +import path from "node:path"; import { revalidatePath } from "next/cache"; -import { mkdir, writeFile, unlink } from "fs/promises"; -import path from "path"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; const FAVICON_DIR = "public/assets/images/media/favicon"; const MAX_SIZE = 2 * 1024 * 1024; // 2MB -const ALLOWED = ["image/png", "image/jpeg", "image/gif", "image/webp", "image/x-icon", "image/svg+xml"]; +const ALLOWED = [ + "image/png", + "image/jpeg", + "image/gif", + "image/webp", + "image/x-icon", + "image/svg+xml", +]; export async function saveFavicon( - formData: FormData, + formData: FormData, ): Promise<{ success: boolean; url?: string; error?: string }> { - try { - const file = formData.get("file") as File | null; - if (!file || file.size === 0) return { success: false, error: "No file provided" }; - if (file.size > MAX_SIZE) return { success: false, error: "File too large (max 2MB)" }; - if (!ALLOWED.includes(file.type)) - return { success: false, error: "Invalid file type. Allowed: PNG, JPEG, GIF, WebP, ICO, SVG" }; + try { + const file = formData.get("file") as File | null; + if (!file || file.size === 0) + return { success: false, error: "No file provided" }; + if (file.size > MAX_SIZE) + return { success: false, error: "File too large (max 2MB)" }; + if (!ALLOWED.includes(file.type)) + return { + success: false, + error: "Invalid file type. Allowed: PNG, JPEG, GIF, WebP, ICO, SVG", + }; - const mimeExt: Record = { - "image/png": "png", - "image/jpeg": "jpg", - "image/gif": "gif", - "image/webp": "webp", - "image/x-icon": "ico", - "image/svg+xml": "svg", - }; - const ext = mimeExt[file.type] ?? "png"; - const filename = `favicon-${Date.now()}.${ext}`; - const baseDir = path.resolve(process.cwd(), FAVICON_DIR); - const filePath = path.resolve(baseDir, filename); - if (!filePath.startsWith(baseDir + path.sep)) { - return { success: false, error: "Invalid path" }; - } + const mimeExt: Record = { + "image/png": "png", + "image/jpeg": "jpg", + "image/gif": "gif", + "image/webp": "webp", + "image/x-icon": "ico", + "image/svg+xml": "svg", + }; + const ext = mimeExt[file.type] ?? "png"; + const filename = `favicon-${Date.now()}.${ext}`; + const baseDir = path.resolve(process.cwd(), FAVICON_DIR); + const filePath = path.resolve(baseDir, filename); + if (!filePath.startsWith(baseDir + path.sep)) { + return { success: false, error: "Invalid path" }; + } - const buffer = Buffer.from(await file.arrayBuffer()); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await mkdir(baseDir, { recursive: true }); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await writeFile(filePath, buffer); + const buffer = Buffer.from(await file.arrayBuffer()); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await mkdir(baseDir, { recursive: true }); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await writeFile(filePath, buffer); - const url = `/api/media/favicon/${filename}`; + const url = `/api/media/favicon/${filename}`; - // Remove old favicon file if it exists - const oldUrl = await siteSettings.get("cms_favicon"); - if (oldUrl && oldUrl.startsWith("/api/media/favicon/")) { - const oldName = oldUrl.replace("/api/media/favicon/", ""); - if (!oldName.includes("..") && !oldName.includes("/")) { - const oldPath = path.resolve(baseDir, oldName); - if (oldPath.startsWith(baseDir + path.sep)) { - try { - // eslint-disable-next-line security/detect-non-literal-fs-filename - await unlink(oldPath); - } catch { - /* ignore if file doesn't exist */ - } - } - } - } + // Remove old favicon file if it exists + const oldUrl = await siteSettings.get("cms_favicon"); + if (oldUrl?.startsWith("/api/media/favicon/")) { + const oldName = oldUrl.replace("/api/media/favicon/", ""); + if (!oldName.includes("..") && !oldName.includes("/")) { + const oldPath = path.resolve(baseDir, oldName); + if (oldPath.startsWith(baseDir + path.sep)) { + try { + // eslint-disable-next-line security/detect-non-literal-fs-filename + await unlink(oldPath); + } catch { + /* ignore if file doesn't exist */ + } + } + } + } - await prisma.websiteSetting.upsert({ - where: { key: "cms_favicon" }, - update: { value: url }, - create: { key: "cms_favicon", value: url, comment: "Favicon URL" }, - }); + await prisma.websiteSetting.upsert({ + where: { key: "cms_favicon" }, + update: { value: url }, + create: { key: "cms_favicon", value: url, comment: "Favicon URL" }, + }); - siteSettings.reload(); - revalidatePath("/", "layout"); - revalidatePath("/admin/favicon"); + siteSettings.reload(); + revalidatePath("/", "layout"); + revalidatePath("/admin/favicon"); - return { success: true, url }; - } catch (e) { - return { success: false, error: e instanceof Error ? e.message : "Unknown error" }; - } + return { success: true, url }; + } catch (e) { + return { + success: false, + error: e instanceof Error ? e.message : "Unknown error", + }; + } } -export async function deleteFavicon(): Promise<{ success: boolean; error?: string }> { - try { - const oldUrl = await siteSettings.get("cms_favicon"); - if (oldUrl && oldUrl.startsWith("/api/media/favicon/")) { - const baseDir = path.resolve(process.cwd(), FAVICON_DIR); - const oldName = oldUrl.replace("/api/media/favicon/", ""); - if (!oldName.includes("..") && !oldName.includes("/")) { - const oldPath = path.resolve(baseDir, oldName); - if (oldPath.startsWith(baseDir + path.sep)) { - try { - // eslint-disable-next-line security/detect-non-literal-fs-filename - await unlink(oldPath); - } catch { - /* ignore */ - } - } - } - } +export async function deleteFavicon(): Promise<{ + success: boolean; + error?: string; +}> { + try { + const oldUrl = await siteSettings.get("cms_favicon"); + if (oldUrl?.startsWith("/api/media/favicon/")) { + const baseDir = path.resolve(process.cwd(), FAVICON_DIR); + const oldName = oldUrl.replace("/api/media/favicon/", ""); + if (!oldName.includes("..") && !oldName.includes("/")) { + const oldPath = path.resolve(baseDir, oldName); + if (oldPath.startsWith(baseDir + path.sep)) { + try { + // eslint-disable-next-line security/detect-non-literal-fs-filename + await unlink(oldPath); + } catch { + /* ignore */ + } + } + } + } - await prisma.websiteSetting.delete({ where: { key: "cms_favicon" } }).catch(() => {}); - siteSettings.reload(); - revalidatePath("/", "layout"); - revalidatePath("/admin/favicon"); + await prisma.websiteSetting + .delete({ where: { key: "cms_favicon" } }) + .catch(() => {}); + siteSettings.reload(); + revalidatePath("/", "layout"); + revalidatePath("/admin/favicon"); - return { success: true }; - } catch (e) { - return { success: false, error: e instanceof Error ? e.message : "Unknown error" }; - } + return { success: true }; + } catch (e) { + return { + success: false, + error: e instanceof Error ? e.message : "Unknown error", + }; + } } diff --git a/src/actions/save-logo.ts b/src/actions/save-logo.ts index f915b2a191..c9584e1f2d 100644 --- a/src/actions/save-logo.ts +++ b/src/actions/save-logo.ts @@ -1,105 +1,112 @@ "use server"; +import { mkdir, writeFile } from "node:fs/promises"; +import path from "node:path"; import { revalidatePath } from "next/cache"; -import { mkdir, writeFile } from "fs/promises"; -import path from "path"; import { prisma } from "@/lib/prisma"; import { siteSettings } from "@/lib/services/site-settings"; const MEDIA_DIR = "public/assets/images/media/logo"; export async function saveLogo( - formData: FormData, + formData: FormData, ): Promise<{ success: boolean; url?: string; error?: string }> { - try { - const file = formData.get("file") as File | null; - if (!file) return { success: false, error: "No file provided" }; + try { + const file = formData.get("file") as File | null; + if (!file) return { success: false, error: "No file provided" }; - const ext = - file.type === "image/png" - ? "png" - : file.type === "image/gif" - ? "gif" - : file.type === "image/jpeg" - ? "jpg" - : file.type === "image/webp" - ? "webp" - : "png"; - const filename = `logo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; - const baseDir = path.resolve(process.cwd(), MEDIA_DIR); - const filePath = path.resolve(baseDir, filename); - if (!filePath.startsWith(baseDir + path.sep)) { - return { success: false, error: "Invalid path" }; - } + const ext = + file.type === "image/png" + ? "png" + : file.type === "image/gif" + ? "gif" + : file.type === "image/jpeg" + ? "jpg" + : file.type === "image/webp" + ? "webp" + : "png"; + const filename = `logo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; + const baseDir = path.resolve(process.cwd(), MEDIA_DIR); + const filePath = path.resolve(baseDir, filename); + if (!filePath.startsWith(baseDir + path.sep)) { + return { success: false, error: "Invalid path" }; + } - const buffer = Buffer.from(await file.arrayBuffer()); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await mkdir(baseDir, { recursive: true }); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await writeFile(filePath, buffer); + const buffer = Buffer.from(await file.arrayBuffer()); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await mkdir(baseDir, { recursive: true }); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await writeFile(filePath, buffer); - const url = `/api/media/logo/${filename}`; + const url = `/api/media/logo/${filename}`; - await prisma.websiteSetting.upsert({ - where: { key: "cms_logo" }, - update: { value: url }, - create: { key: "cms_logo", value: url, comment: "Logo (generator)" }, - }); + await prisma.websiteSetting.upsert({ + where: { key: "cms_logo" }, + update: { value: url }, + create: { key: "cms_logo", value: url, comment: "Logo (generator)" }, + }); - siteSettings.reload(); - revalidatePath("/", "layout"); + siteSettings.reload(); + revalidatePath("/", "layout"); - return { success: true, url }; - } catch (e) { - return { success: false, error: e instanceof Error ? e.message : "Unknown error" }; - } + return { success: true, url }; + } catch (e) { + return { + success: false, + error: e instanceof Error ? e.message : "Unknown error", + }; + } } export async function saveLogoFromUrl( - gifUrl: string, + gifUrl: string, ): Promise<{ success: boolean; url?: string; error?: string }> { - try { - const res = await fetch(gifUrl); - if (!res.ok) return { success: false, error: `Failed to fetch GIF: ${res.status}` }; + try { + const res = await fetch(gifUrl); + if (!res.ok) + return { success: false, error: `Failed to fetch GIF: ${res.status}` }; - const contentType = res.headers.get("content-type") ?? "image/gif"; - const buffer = Buffer.from(await res.arrayBuffer()); + const contentType = res.headers.get("content-type") ?? "image/gif"; + const buffer = Buffer.from(await res.arrayBuffer()); - const ext = - contentType === "image/png" - ? "png" - : contentType === "image/gif" - ? "gif" - : contentType === "image/jpeg" - ? "jpg" - : contentType === "image/webp" - ? "webp" - : "gif"; - const filename = `logo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; - const baseDir = path.resolve(process.cwd(), MEDIA_DIR); - const filePath = path.resolve(baseDir, filename); - if (!filePath.startsWith(baseDir + path.sep)) { - return { success: false, error: "Invalid path" }; - } + const ext = + contentType === "image/png" + ? "png" + : contentType === "image/gif" + ? "gif" + : contentType === "image/jpeg" + ? "jpg" + : contentType === "image/webp" + ? "webp" + : "gif"; + const filename = `logo-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${ext}`; + const baseDir = path.resolve(process.cwd(), MEDIA_DIR); + const filePath = path.resolve(baseDir, filename); + if (!filePath.startsWith(baseDir + path.sep)) { + return { success: false, error: "Invalid path" }; + } - // eslint-disable-next-line security/detect-non-literal-fs-filename - await mkdir(baseDir, { recursive: true }); - // eslint-disable-next-line security/detect-non-literal-fs-filename - await writeFile(filePath, buffer); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await mkdir(baseDir, { recursive: true }); + // eslint-disable-next-line security/detect-non-literal-fs-filename + await writeFile(filePath, buffer); - const url = `/api/media/logo/${filename}`; + const url = `/api/media/logo/${filename}`; - await prisma.websiteSetting.upsert({ - where: { key: "cms_logo" }, - update: { value: url }, - create: { key: "cms_logo", value: url, comment: "Logo (generator)" }, - }); + await prisma.websiteSetting.upsert({ + where: { key: "cms_logo" }, + update: { value: url }, + create: { key: "cms_logo", value: url, comment: "Logo (generator)" }, + }); - siteSettings.reload(); - revalidatePath("/", "layout"); + siteSettings.reload(); + revalidatePath("/", "layout"); - return { success: true, url }; - } catch (e) { - return { success: false, error: e instanceof Error ? e.message : "Unknown error" }; - } + return { success: true, url }; + } catch (e) { + return { + success: false, + error: e instanceof Error ? e.message : "Unknown error", + }; + } } diff --git a/src/actions/social.ts b/src/actions/social.ts index 0599e83d72..87bc63ba7c 100644 --- a/src/actions/social.ts +++ b/src/actions/social.ts @@ -21,50 +21,50 @@ const MESSAGE_MAX = 10000; * target). The emulator surfaces the pending request in the in-game messenger. */ export async function sendFriendRequest(formData: FormData): Promise { - const session = await auth(); - const fromId = Number(session?.user?.id); - if (!Number.isInteger(fromId) || fromId <= 0) return; + const session = await auth(); + const fromId = Number(session?.user?.id); + if (!Number.isInteger(fromId) || fromId <= 0) return; - const toId = Number(formData.get("userId")); - if (!Number.isInteger(toId) || toId <= 0) return; + const toId = Number(formData.get("userId")); + if (!Number.isInteger(toId) || toId <= 0) return; - // Can't befriend yourself. - if (toId === fromId) return; + // Can't befriend yourself. + if (toId === fromId) return; - try { - // Guard against duplicate pending requests and already-existing friendships. - const [existingRequest, existingFriendship] = await Promise.all([ - prisma.messengerFriendrequests.findFirst({ - where: { userFromId: fromId, userToId: toId }, - select: { id: true }, - }), - prisma.messengerFriendships.findFirst({ - where: { - OR: [ - { userOneId: fromId, userTwoId: toId }, - { userOneId: toId, userTwoId: fromId }, - ], - }, - select: { id: true }, - }), - ]); + try { + // Guard against duplicate pending requests and already-existing friendships. + const [existingRequest, existingFriendship] = await Promise.all([ + prisma.messengerFriendrequests.findFirst({ + where: { userFromId: fromId, userToId: toId }, + select: { id: true }, + }), + prisma.messengerFriendships.findFirst({ + where: { + OR: [ + { userOneId: fromId, userTwoId: toId }, + { userOneId: toId, userTwoId: fromId }, + ], + }, + select: { id: true }, + }), + ]); - if (existingRequest || existingFriendship) return; + if (existingRequest || existingFriendship) return; - await prisma.messengerFriendrequests.create({ - data: { userFromId: fromId, userToId: toId }, - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + await prisma.messengerFriendrequests.create({ + data: { userFromId: fromId, userToId: toId }, + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - // Optional: revalidate the target profile if a username was supplied, purely - // to refresh any request-state UI rendered there. - const username = String(formData.get("username") ?? "") - .normalize("NFC") - .trim(); - if (username) revalidatePath(`/u/${username}`); + // Optional: revalidate the target profile if a username was supplied, purely + // to refresh any request-state UI rendered there. + const username = String(formData.get("username") ?? "") + .normalize("NFC") + .trim(); + if (username) revalidatePath(`/u/${username}`); } /** @@ -80,65 +80,65 @@ export async function sendFriendRequest(formData: FormData): Promise { * stamp posts_count = 1 to match the emulator's bookkeeping. */ export async function postThread(formData: FormData): Promise { - const session = await auth(); - const openerId = Number(session?.user?.id); - if (!Number.isInteger(openerId) || openerId <= 0) return; + const session = await auth(); + const openerId = Number(session?.user?.id); + if (!Number.isInteger(openerId) || openerId <= 0) return; - const guildId = Number(formData.get("guildId")); - if (!Number.isInteger(guildId) || guildId <= 0) return; + const guildId = Number(formData.get("guildId")); + if (!Number.isInteger(guildId) || guildId <= 0) return; - const subject = String(formData.get("subject") ?? "") - .normalize("NFC") - .trim() - .slice(0, SUBJECT_MAX); - const message = String(formData.get("message") ?? "") - .normalize("NFC") - .trim() - .slice(0, MESSAGE_MAX); - if (!subject || !message) return; + const subject = String(formData.get("subject") ?? "") + .normalize("NFC") + .trim() + .slice(0, SUBJECT_MAX); + const message = String(formData.get("message") ?? "") + .normalize("NFC") + .trim() + .slice(0, MESSAGE_MAX); + if (!subject || !message) return; - const now = Math.floor(Date.now() / 1000); + const now = Math.floor(Date.now() / 1000); - try { - // Confirm the guild exists (and has a forum) before opening a thread. - const guild = await prisma.guilds.findUnique({ - where: { id: guildId }, - select: { id: true }, - }); - if (!guild) return; + try { + // Confirm the guild exists (and has a forum) before opening a thread. + const guild = await prisma.guilds.findUnique({ + where: { id: guildId }, + select: { id: true }, + }); + if (!guild) return; - await prisma.$transaction(async (tx) => { - const thread = await tx.guildsForumsThreads.create({ - data: { - guildId, - openerId, - subject, - postsCount: 1, - createdAt: now, - updatedAt: now, - state: 0, - pinned: 0, - locked: 0, - adminId: 0, - }, - select: { id: true }, - }); + await prisma.$transaction(async (tx) => { + const thread = await tx.guildsForumsThreads.create({ + data: { + guildId, + openerId, + subject, + postsCount: 1, + createdAt: now, + updatedAt: now, + state: 0, + pinned: 0, + locked: 0, + adminId: 0, + }, + select: { id: true }, + }); - await tx.guildsForumsComments.create({ - data: { - threadId: thread.id, - userId: openerId, - message, - createdAt: now, - state: 0, - adminId: 0, - }, - }); - }); - } catch { - // DB unavailable — fail soft; nothing to persist. - return; - } + await tx.guildsForumsComments.create({ + data: { + threadId: thread.id, + userId: openerId, + message, + createdAt: now, + state: 0, + adminId: 0, + }, + }); + }); + } catch { + // DB unavailable — fail soft; nothing to persist. + return; + } - revalidatePath(`/guilds/${guildId}/forum`); + revalidatePath(`/guilds/${guildId}/forum`); } diff --git a/src/actions/soundtracks.ts b/src/actions/soundtracks.ts index 9649c182de..3d3a12d70f 100644 --- a/src/actions/soundtracks.ts +++ b/src/actions/soundtracks.ts @@ -5,28 +5,28 @@ import { requireStaff } from "@/lib/admin/guard"; import { prisma } from "@/lib/prisma"; export async function deleteSoundtrack({ id }: { id: number }) { - await requireStaff(); - await prisma.soundtracks.delete({ where: { id } }); - revalidatePath("/admin/sounds"); + await requireStaff(); + await prisma.soundtracks.delete({ where: { id } }); + revalidatePath("/admin/sounds"); } export async function updateSoundtrack({ - id, - name, - author, - track, - length, + id, + name, + author, + track, + length, }: { - id: number; - name: string; - author: string; - track: string; - length: number; + id: number; + name: string; + author: string; + track: string; + length: number; }) { - await requireStaff(); - await prisma.soundtracks.update({ - where: { id }, - data: { name, author, track, length }, - }); - revalidatePath("/admin/sounds"); + await requireStaff(); + await prisma.soundtracks.update({ + where: { id }, + data: { name, author, track, length }, + }); + revalidatePath("/admin/sounds"); } diff --git a/src/actions/ticket-templates.ts b/src/actions/ticket-templates.ts index 52bf9a83d4..ad1626c006 100644 --- a/src/actions/ticket-templates.ts +++ b/src/actions/ticket-templates.ts @@ -7,39 +7,45 @@ import { adminAction } from "@/lib/safe-action"; import { ActionError, actionOk } from "@/lib/safe-action-shared"; const templateSchema = z.object({ - title: z.string().min(1).max(255), - content: z.string().min(1), - category: z.string().max(50).optional().default("general"), - sortOrder: z.coerce.number().int().min(0).default(0), + title: z.string().min(1).max(255), + content: z.string().min(1), + category: z.string().max(50).optional().default("general"), + sortOrder: z.coerce.number().int().min(0).default(0), }); export const createTemplate = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: templateSchema }, - async (ctx) => { - const tpl = await prisma.websiteTicketTemplate.create({ data: ctx.data }); - return actionOk({ id: tpl.id }); - }, + { permission: PERMS.TICKETS_EDIT, schema: templateSchema }, + async (ctx) => { + const tpl = await prisma.websiteTicketTemplate.create({ data: ctx.data }); + return actionOk({ id: tpl.id }); + }, ); -const updateTemplateInput = templateSchema.partial().extend({ id: z.coerce.number().int().positive() }); +const updateTemplateInput = templateSchema + .partial() + .extend({ id: z.coerce.number().int().positive() }); export const updateTemplate = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: updateTemplateInput }, - async (ctx) => { - const { id, ...data } = ctx.data; - const existing = await prisma.websiteTicketTemplate.findUnique({ where: { id } }); - if (!existing) throw new ActionError("Template not found"); - await prisma.websiteTicketTemplate.update({ where: { id }, data }); - return actionOk({ id }); - }, + { permission: PERMS.TICKETS_EDIT, schema: updateTemplateInput }, + async (ctx) => { + const { id, ...data } = ctx.data; + const existing = await prisma.websiteTicketTemplate.findUnique({ + where: { id }, + }); + if (!existing) throw new ActionError("Template not found"); + await prisma.websiteTicketTemplate.update({ where: { id }, data }); + return actionOk({ id }); + }, ); -const deleteTemplateInput = z.object({ id: z.coerce.number().int().positive() }); +const deleteTemplateInput = z.object({ + id: z.coerce.number().int().positive(), +}); export const deleteTemplate = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: deleteTemplateInput }, - async (ctx) => { - await prisma.websiteTicketTemplate.delete({ where: { id: ctx.data.id } }); - return actionOk(); - }, + { permission: PERMS.TICKETS_EDIT, schema: deleteTemplateInput }, + async (ctx) => { + await prisma.websiteTicketTemplate.delete({ where: { id: ctx.data.id } }); + return actionOk(); + }, ); diff --git a/src/actions/tickets.ts b/src/actions/tickets.ts index b3b46e9d82..d171e57999 100644 --- a/src/actions/tickets.ts +++ b/src/actions/tickets.ts @@ -7,223 +7,232 @@ import { ActionError, actionOk } from "@/lib/safe-action-shared"; import { logAudit } from "@/lib/services/audit"; import { notify } from "@/lib/services/webhook"; import { - assignTicketSchema, - createTicketSchema, - replyTicketSchema, - updateTicketPrioritySchema, - updateTicketStatusSchema, + assignTicketSchema, + createTicketSchema, + replyTicketSchema, + updateTicketPrioritySchema, + updateTicketStatusSchema, } from "@/lib/validators/ticket"; // ── User actions (authenticated, no admin perms needed) ────────────── -export const createTicket = authAction({ schema: createTicketSchema }, async (ctx) => { - const ticket = await prisma.websiteTicket.create({ - data: { - subject: ctx.data.subject, - category: ctx.data.category, - creatorId: ctx.session.user.id, - }, - }); +export const createTicket = authAction( + { schema: createTicketSchema }, + async (ctx) => { + const ticket = await prisma.websiteTicket.create({ + data: { + subject: ctx.data.subject, + category: ctx.data.category, + creatorId: ctx.session.user.id, + }, + }); - // Create the first message - await prisma.websiteTicketMessage.create({ - data: { - ticketId: ticket.id, - userId: ctx.session.user.id, - message: ctx.data.message, - isStaff: 0, - }, - }); + // Create the first message + await prisma.websiteTicketMessage.create({ + data: { + ticketId: ticket.id, + userId: ctx.session.user.id, + message: ctx.data.message, + isStaff: 0, + }, + }); - notify({ - action: "ticket_create", - actor: ctx.session.user.username, - target: `#${ticket.id} - ${ticket.subject}`, - details: `Category: ${ticket.category}`, - }); + notify({ + action: "ticket_create", + actor: ctx.session.user.username, + target: `#${ticket.id} - ${ticket.subject}`, + details: `Category: ${ticket.category}`, + }); - return actionOk({ id: ticket.id }); -}); + return actionOk({ id: ticket.id }); + }, +); -export const userReplyTicket = authAction({ schema: replyTicketSchema }, async (ctx) => { - const ticket = await prisma.websiteTicket.findUnique({ - where: { id: ctx.data.ticketId }, - }); +export const userReplyTicket = authAction( + { schema: replyTicketSchema }, + async (ctx) => { + const ticket = await prisma.websiteTicket.findUnique({ + where: { id: ctx.data.ticketId }, + }); - if (!ticket) throw new ActionError("Ticket not found"); - if (ticket.creatorId !== ctx.session.user.id) throw new ActionError("Unauthorized"); - if (ticket.status === "closed") throw new ActionError("Ticket is closed"); + if (!ticket) throw new ActionError("Ticket not found"); + if (ticket.creatorId !== ctx.session.user.id) + throw new ActionError("Unauthorized"); + if (ticket.status === "closed") throw new ActionError("Ticket is closed"); - await prisma.websiteTicketMessage.create({ - data: { - ticketId: ctx.data.ticketId, - userId: ctx.session.user.id, - message: ctx.data.message, - isStaff: 0, - }, - }); + await prisma.websiteTicketMessage.create({ + data: { + ticketId: ctx.data.ticketId, + userId: ctx.session.user.id, + message: ctx.data.message, + isStaff: 0, + }, + }); - // If ticket was in "waiting" (waiting for user), move back to open - if (ticket.status === "waiting") { - await prisma.websiteTicket.update({ - where: { id: ctx.data.ticketId }, - data: { status: "open" }, - }); - } + // If ticket was in "waiting" (waiting for user), move back to open + if (ticket.status === "waiting") { + await prisma.websiteTicket.update({ + where: { id: ctx.data.ticketId }, + data: { status: "open" }, + }); + } - return actionOk(); -}); + return actionOk(); + }, +); export const closeTicketByUser = authAction( - { schema: replyTicketSchema.pick({ ticketId: true }) }, - async (ctx) => { - const ticket = await prisma.websiteTicket.findUnique({ - where: { id: ctx.data.ticketId }, - }); + { schema: replyTicketSchema.pick({ ticketId: true }) }, + async (ctx) => { + const ticket = await prisma.websiteTicket.findUnique({ + where: { id: ctx.data.ticketId }, + }); - if (!ticket) throw new ActionError("Ticket not found"); - if (ticket.creatorId !== ctx.session.user.id) throw new ActionError("Unauthorized"); - if (ticket.status === "closed") throw new ActionError("Ticket is already closed"); + if (!ticket) throw new ActionError("Ticket not found"); + if (ticket.creatorId !== ctx.session.user.id) + throw new ActionError("Unauthorized"); + if (ticket.status === "closed") + throw new ActionError("Ticket is already closed"); - await prisma.websiteTicket.update({ - where: { id: ctx.data.ticketId }, - data: { status: "closed", closedAt: new Date() }, - }); + await prisma.websiteTicket.update({ + where: { id: ctx.data.ticketId }, + data: { status: "closed", closedAt: new Date() }, + }); - return actionOk(); - }, + return actionOk(); + }, ); // ── Admin actions ──────────────────────────────────────────────────── export const adminReplyTicket = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: replyTicketSchema }, - async (ctx) => { - const ticket = await prisma.websiteTicket.findUnique({ - where: { id: ctx.data.ticketId }, - }); + { permission: PERMS.TICKETS_EDIT, schema: replyTicketSchema }, + async (ctx) => { + const ticket = await prisma.websiteTicket.findUnique({ + where: { id: ctx.data.ticketId }, + }); - if (!ticket) throw new ActionError("Ticket not found"); + if (!ticket) throw new ActionError("Ticket not found"); - await prisma.websiteTicketMessage.create({ - data: { - ticketId: ctx.data.ticketId, - userId: ctx.session.user.id, - message: ctx.data.message, - isStaff: 1, - }, - }); + await prisma.websiteTicketMessage.create({ + data: { + ticketId: ctx.data.ticketId, + userId: ctx.session.user.id, + message: ctx.data.message, + isStaff: 1, + }, + }); - // Auto-assign if not assigned yet - const updates: Record = { status: "waiting" }; - if (!ticket.assigneeId) { - updates.assigneeId = ctx.session.user.id; - } + // Auto-assign if not assigned yet + const updates: Record = { status: "waiting" }; + if (!ticket.assigneeId) { + updates.assigneeId = ctx.session.user.id; + } - await prisma.websiteTicket.update({ - where: { id: ctx.data.ticketId }, - data: updates, - }); + await prisma.websiteTicket.update({ + where: { id: ctx.data.ticketId }, + data: updates, + }); - logAudit({ - userId: ctx.session.user.id, - action: "ticket_reply", - target: "WebsiteTicket", - targetId: ctx.data.ticketId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "ticket_reply", + target: "WebsiteTicket", + targetId: ctx.data.ticketId, + }); - return actionOk(); - }, + return actionOk(); + }, ); export const updateTicketStatus = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: updateTicketStatusSchema }, - async (ctx) => { - const ticket = await prisma.websiteTicket.findUnique({ - where: { id: ctx.data.ticketId }, - }); + { permission: PERMS.TICKETS_EDIT, schema: updateTicketStatusSchema }, + async (ctx) => { + const ticket = await prisma.websiteTicket.findUnique({ + where: { id: ctx.data.ticketId }, + }); - if (!ticket) throw new ActionError("Ticket not found"); + if (!ticket) throw new ActionError("Ticket not found"); - const data: Record = { status: ctx.data.status }; - if (ctx.data.status === "closed") { - data.closedAt = new Date(); - } - if (ctx.data.status === "in_progress" && !ticket.assigneeId) { - data.assigneeId = ctx.session.user.id; - } + const data: Record = { status: ctx.data.status }; + if (ctx.data.status === "closed") { + data.closedAt = new Date(); + } + if (ctx.data.status === "in_progress" && !ticket.assigneeId) { + data.assigneeId = ctx.session.user.id; + } - await prisma.websiteTicket.update({ - where: { id: ctx.data.ticketId }, - data, - }); + await prisma.websiteTicket.update({ + where: { id: ctx.data.ticketId }, + data, + }); - logAudit({ - userId: ctx.session.user.id, - action: "ticket_status_change", - target: "WebsiteTicket", - targetId: ctx.data.ticketId, - before: { status: ticket.status }, - after: { status: ctx.data.status }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "ticket_status_change", + target: "WebsiteTicket", + targetId: ctx.data.ticketId, + before: { status: ticket.status }, + after: { status: ctx.data.status }, + }); - return actionOk(); - }, + return actionOk(); + }, ); export const assignTicket = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: assignTicketSchema }, - async (ctx) => { - const ticket = await prisma.websiteTicket.findUnique({ - where: { id: ctx.data.ticketId }, - }); + { permission: PERMS.TICKETS_EDIT, schema: assignTicketSchema }, + async (ctx) => { + const ticket = await prisma.websiteTicket.findUnique({ + where: { id: ctx.data.ticketId }, + }); - if (!ticket) throw new ActionError("Ticket not found"); + if (!ticket) throw new ActionError("Ticket not found"); - await prisma.websiteTicket.update({ - where: { id: ctx.data.ticketId }, - data: { - assigneeId: ctx.data.assigneeId, - status: ctx.data.assigneeId ? "in_progress" : "open", - }, - }); + await prisma.websiteTicket.update({ + where: { id: ctx.data.ticketId }, + data: { + assigneeId: ctx.data.assigneeId, + status: ctx.data.assigneeId ? "in_progress" : "open", + }, + }); - logAudit({ - userId: ctx.session.user.id, - action: "ticket_assign", - target: "WebsiteTicket", - targetId: ctx.data.ticketId, - before: { assigneeId: ticket.assigneeId }, - after: { assigneeId: ctx.data.assigneeId }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "ticket_assign", + target: "WebsiteTicket", + targetId: ctx.data.ticketId, + before: { assigneeId: ticket.assigneeId }, + after: { assigneeId: ctx.data.assigneeId }, + }); - return actionOk(); - }, + return actionOk(); + }, ); export const updateTicketPriority = adminAction( - { permission: PERMS.TICKETS_EDIT, schema: updateTicketPrioritySchema }, - async (ctx) => { - const ticket = await prisma.websiteTicket.findUnique({ - where: { id: ctx.data.ticketId }, - }); + { permission: PERMS.TICKETS_EDIT, schema: updateTicketPrioritySchema }, + async (ctx) => { + const ticket = await prisma.websiteTicket.findUnique({ + where: { id: ctx.data.ticketId }, + }); - if (!ticket) throw new ActionError("Ticket not found"); + if (!ticket) throw new ActionError("Ticket not found"); - await prisma.websiteTicket.update({ - where: { id: ctx.data.ticketId }, - data: { priority: ctx.data.priority }, - }); + await prisma.websiteTicket.update({ + where: { id: ctx.data.ticketId }, + data: { priority: ctx.data.priority }, + }); - logAudit({ - userId: ctx.session.user.id, - action: "ticket_priority_change", - target: "WebsiteTicket", - targetId: ctx.data.ticketId, - before: { priority: ticket.priority }, - after: { priority: ctx.data.priority }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "ticket_priority_change", + target: "WebsiteTicket", + targetId: ctx.data.ticketId, + before: { priority: ticket.priority }, + after: { priority: ctx.data.priority }, + }); - return actionOk(); - }, + return actionOk(); + }, ); diff --git a/src/actions/translations.ts b/src/actions/translations.ts index 2ef5c16096..997d2d5ffb 100644 --- a/src/actions/translations.ts +++ b/src/actions/translations.ts @@ -4,93 +4,123 @@ import fs from "node:fs/promises"; import path from "node:path"; import JSON5 from "json5"; import { z } from "zod"; -import { CLIENT_TRANSLATION_FILES, getClientTranslationFile } from "@/lib/client-translation-files"; +import { + CLIENT_TRANSLATION_FILES, + getClientTranslationFile, +} from "@/lib/client-translation-files"; import { patchJson5 } from "@/lib/json5-patch"; import { adminAction } from "@/lib/safe-action"; import { ActionError, actionOk } from "@/lib/safe-action-shared"; const saveTranslationsSchema = z.object({ - locale: z.enum([ - "en", - "it", - "nl", - "de", - "fr", - "es", - "pt", - "pl", - "sv", - "tr", - "ro", - "hu", - "cs", - "sk", - "da", - "no", - "el", - "bg", - "hr", - "sr", - "uk", - "ru", - ]), - data: z.record(z.string(), z.unknown()), + locale: z.enum([ + "en", + "it", + "nl", + "de", + "fr", + "es", + "pt", + "pl", + "sv", + "tr", + "ro", + "hu", + "cs", + "sk", + "da", + "no", + "el", + "bg", + "hr", + "sr", + "uk", + "ru", + ]), + data: z.record(z.string(), z.unknown()), }); -export const saveTranslations = adminAction({ schema: saveTranslationsSchema }, async (ctx) => { - if (ctx.session.user.rank < 7) throw new ActionError("Forbidden"); +export const saveTranslations = adminAction( + { schema: saveTranslationsSchema }, + async (ctx) => { + if (ctx.session.user.rank < 7) throw new ActionError("Forbidden"); - const filePath = path.join(process.cwd(), "messages", `${ctx.data.locale}.json`); - await fs.writeFile(filePath, JSON.stringify(ctx.data.data, null, 2), "utf-8"); + const filePath = path.join( + process.cwd(), + "messages", + `${ctx.data.locale}.json`, + ); + await fs.writeFile( + filePath, + JSON.stringify(ctx.data.data, null, 2), + "utf-8", + ); - return actionOk(); -}); + return actionOk(); + }, +); const saveClientTranslationsSchema = z.object({ - fileId: z.enum(CLIENT_TRANSLATION_FILES.map((f) => f.id) as [string, ...string[]]), - data: z.record(z.string(), z.string()), + fileId: z.enum( + CLIENT_TRANSLATION_FILES.map((f) => f.id) as [string, ...string[]], + ), + data: z.record(z.string(), z.string()), }); -export const saveClientTranslations = adminAction({ schema: saveClientTranslationsSchema }, async (ctx) => { - if (ctx.session.user.rank < 7) throw new ActionError("Forbidden"); +export const saveClientTranslations = adminAction( + { schema: saveClientTranslationsSchema }, + async (ctx) => { + if (ctx.session.user.rank < 7) throw new ActionError("Forbidden"); - const file = getClientTranslationFile(ctx.data.fileId); - if (!file) throw new ActionError("Unknown file"); - if (file.readOnly) throw new ActionError("File is read-only"); + const file = getClientTranslationFile(ctx.data.fileId); + if (!file) throw new ActionError("Unknown file"); + if (file.readOnly) throw new ActionError("File is read-only"); - // file.relPath comes from CLIENT_TRANSLATION_FILES (closed enum) but - // Turbopack's static tracer can't prove that — without the hint it - // pulls the entire project into the NFT list. - const absPath = path.join(/*turbopackIgnore: true*/ process.cwd(), file.relPath); - const raw = await fs.readFile(absPath, "utf-8"); + // file.relPath comes from CLIENT_TRANSLATION_FILES (closed enum) but + // Turbopack's static tracer can't prove that — without the hint it + // pulls the entire project into the NFT list. + const absPath = path.join( + /*turbopackIgnore: true*/ process.cwd(), + file.relPath, + ); + const raw = await fs.readFile(absPath, "utf-8"); - if (file.format === "json") { - // Plain JSON — no comments to preserve, just round-trip. - await fs.writeFile(absPath, JSON.stringify(ctx.data.data, null, 4), "utf-8"); - return actionOk({ commentsLost: false, unpatchedKeys: [] as string[] }); - } + if (file.format === "json") { + // Plain JSON — no comments to preserve, just round-trip. + await fs.writeFile( + absPath, + JSON.stringify(ctx.data.data, null, 4), + "utf-8", + ); + return actionOk({ commentsLost: false, unpatchedKeys: [] as string[] }); + } - // JSON5: surgical line-level patch keeps headers and section comments - // intact. Falls back to a full re-serialization (which DOES drop comments) - // only when an edited key cannot be located via the patch contract. - const original: Record = {}; - const parsed = JSON5.parse(raw); - if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { - for (const [k, v] of Object.entries(parsed)) { - original[k] = v == null ? "" : String(v); - } - } + // JSON5: surgical line-level patch keeps headers and section comments + // intact. Falls back to a full re-serialization (which DOES drop comments) + // only when an edited key cannot be located via the patch contract. + const original: Record = {}; + const parsed = JSON5.parse(raw); + if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { + for (const [k, v] of Object.entries(parsed)) { + original[k] = v == null ? "" : String(v); + } + } - const { content, unpatchedKeys } = patchJson5(raw, original, ctx.data.data); + const { content, unpatchedKeys } = patchJson5(raw, original, ctx.data.data); - if (unpatchedKeys.length === 0) { - await fs.writeFile(absPath, content, "utf-8"); - return actionOk({ commentsLost: false, unpatchedKeys }); - } + if (unpatchedKeys.length === 0) { + await fs.writeFile(absPath, content, "utf-8"); + return actionOk({ commentsLost: false, unpatchedKeys }); + } - // At least one key could not be patched surgically (e.g. unusual - // formatting or a brand-new key). Fall back to a full re-serialization - // and warn the caller that comments were lost. - await fs.writeFile(absPath, JSON5.stringify(ctx.data.data, null, 4), "utf-8"); - return actionOk({ commentsLost: true, unpatchedKeys }); -}); + // At least one key could not be patched surgically (e.g. unusual + // formatting or a brand-new key). Fall back to a full re-serialization + // and warn the caller that comments were lost. + await fs.writeFile( + absPath, + JSON5.stringify(ctx.data.data, null, 4), + "utf-8", + ); + return actionOk({ commentsLost: true, unpatchedKeys }); + }, +); diff --git a/src/actions/twofactor.ts b/src/actions/twofactor.ts index 24cbe056dd..7705f63c8e 100644 --- a/src/actions/twofactor.ts +++ b/src/actions/twofactor.ts @@ -1,128 +1,135 @@ "use server"; +import { randomBytes } from "node:crypto"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; -import { randomBytes } from "node:crypto"; +import { env } from "@/env"; +import { auth } from "@/lib/auth"; import { LaravelEncrypter } from "@/lib/auth/laravel-encrypter"; import { generateTotpSecret, verifyTotp } from "@/lib/auth/totp"; -import { auth } from "@/lib/auth"; import { prisma } from "@/lib/prisma"; import { rateLimit } from "@/lib/rate-limit"; -import { env } from "@/env"; async function sessionUserId(): Promise { - const session = await auth(); - if (!session?.user?.id) redirect("/login"); - return Number(session.user.id); + const session = await auth(); + if (!session?.user?.id) redirect("/login"); + return Number(session.user.id); } function generateRecoveryCodes(): string[] { - const codes: string[] = []; - for (let i = 0; i < 8; i++) { - codes.push( - randomBytes(4) - .toString("hex") - .toUpperCase() - .replace(/(.{4})/, "$1-"), - ); - } - return codes; + const codes: string[] = []; + for (let i = 0; i < 8; i++) { + codes.push( + randomBytes(4) + .toString("hex") + .toUpperCase() + .replace(/(.{4})/, "$1-"), + ); + } + return codes; } /** Verify a TOTP code OR a recovery code. Returns the updated recovery codes (minus used one) if a recovery code was used, or null on failure. */ async function verifyTwoFactorCode( - userId: number, - code: string, + userId: number, + code: string, ): Promise<{ ok: boolean; updatedRecoveryCodes?: string | null }> { - const user = await prisma.user.findUnique({ - where: { id: userId }, - select: { twoFactorSecret: true, twoFactorRecoveryCodes: true }, - }); - if (!user?.twoFactorSecret) return { ok: false }; + const user = await prisma.user.findUnique({ + where: { id: userId }, + select: { twoFactorSecret: true, twoFactorRecoveryCodes: true }, + }); + if (!user?.twoFactorSecret) return { ok: false }; - // Try TOTP first - try { - const secret = new LaravelEncrypter(env.APP_KEY as string).decrypt(user.twoFactorSecret); - if (verifyTotp(code, secret)) return { ok: true }; - } catch { - /* fall through to recovery */ - } + // Try TOTP first + try { + const secret = new LaravelEncrypter(env.APP_KEY as string).decrypt( + user.twoFactorSecret, + ); + if (verifyTotp(code, secret)) return { ok: true }; + } catch { + /* fall through to recovery */ + } - // Try recovery codes - if (user.twoFactorRecoveryCodes) { - let codes: string[]; - try { - codes = JSON.parse(user.twoFactorRecoveryCodes) as string[]; - } catch { - codes = []; - } - const idx = codes.indexOf(code); - if (idx !== -1) { - codes.splice(idx, 1); - const remaining = codes.length > 0 ? JSON.stringify(codes) : null; - return { ok: true, updatedRecoveryCodes: remaining }; - } - } + // Try recovery codes + if (user.twoFactorRecoveryCodes) { + let codes: string[]; + try { + codes = JSON.parse(user.twoFactorRecoveryCodes) as string[]; + } catch { + codes = []; + } + const idx = codes.indexOf(code); + if (idx !== -1) { + codes.splice(idx, 1); + const remaining = codes.length > 0 ? JSON.stringify(codes) : null; + return { ok: true, updatedRecoveryCodes: remaining }; + } + } - return { ok: false }; + return { ok: false }; } /** Step 1: generate a secret and recovery codes, store encrypted but UNconfirmed. */ export async function beginTwoFactor(): Promise { - const id = await sessionUserId(); - if (!env.APP_KEY) redirect("/settings/2fa?error=noappkey"); - const secret = generateTotpSecret(); - const encrypted = new LaravelEncrypter(env.APP_KEY).encrypt(secret); - const codes = generateRecoveryCodes(); - await prisma.user.update({ - where: { id }, - data: { - twoFactorSecret: encrypted, - twoFactorConfirmedAt: null, - twoFactorRecoveryCodes: JSON.stringify(codes), - }, - }); - revalidatePath("/settings/2fa"); + const id = await sessionUserId(); + if (!env.APP_KEY) redirect("/settings/2fa?error=noappkey"); + const secret = generateTotpSecret(); + const encrypted = new LaravelEncrypter(env.APP_KEY).encrypt(secret); + const codes = generateRecoveryCodes(); + await prisma.user.update({ + where: { id }, + data: { + twoFactorSecret: encrypted, + twoFactorConfirmedAt: null, + twoFactorRecoveryCodes: JSON.stringify(codes), + }, + }); + revalidatePath("/settings/2fa"); } /** Step 2: verify a code against the pending secret, then confirm and show recovery codes. */ export async function confirmTwoFactor(formData: FormData): Promise { - const id = await sessionUserId(); - if (!env.APP_KEY) redirect("/settings/2fa?error=noappkey"); + const id = await sessionUserId(); + if (!env.APP_KEY) redirect("/settings/2fa?error=noappkey"); - if (!(await rateLimit(`2fa-confirm:${id}`, 5, 30_000)).ok) redirect("/settings/2fa?error=ratelimit"); + if (!(await rateLimit(`2fa-confirm:${id}`, 5, 30_000)).ok) + redirect("/settings/2fa?error=ratelimit"); - const code = String(formData.get("code") ?? "") - .normalize("NFC") - .trim(); + const code = String(formData.get("code") ?? "") + .normalize("NFC") + .trim(); - const { ok } = await verifyTwoFactorCode(id, code); - if (!ok) redirect("/settings/2fa?error=badcode"); + const { ok } = await verifyTwoFactorCode(id, code); + if (!ok) redirect("/settings/2fa?error=badcode"); - await prisma.user.update({ where: { id }, data: { twoFactorConfirmedAt: new Date() } }); - redirect("/settings/2fa?enabled=1"); + await prisma.user.update({ + where: { id }, + data: { twoFactorConfirmedAt: new Date() }, + }); + redirect("/settings/2fa?enabled=1"); } export async function disableTwoFactor(formData: FormData): Promise { - const id = await sessionUserId(); - if (!env.APP_KEY) redirect("/settings/2fa?error=noappkey"); + const id = await sessionUserId(); + if (!env.APP_KEY) redirect("/settings/2fa?error=noappkey"); - if (!(await rateLimit(`2fa-disable:${id}`, 5, 30_000)).ok) redirect("/settings/2fa?error=ratelimit"); + if (!(await rateLimit(`2fa-disable:${id}`, 5, 30_000)).ok) + redirect("/settings/2fa?error=ratelimit"); - const code = String(formData.get("code") ?? "") - .normalize("NFC") - .trim(); + const code = String(formData.get("code") ?? "") + .normalize("NFC") + .trim(); - const { ok } = await verifyTwoFactorCode(id, code); - if (!ok) redirect("/settings/2fa?error=badcode"); + const { ok } = await verifyTwoFactorCode(id, code); + if (!ok) redirect("/settings/2fa?error=badcode"); - await prisma.user.update({ - where: { id }, - data: { - twoFactorSecret: null, - twoFactorRecoveryCodes: null, - twoFactorConfirmedAt: null, - }, - }); - redirect("/settings/2fa?disabled=1"); + await prisma.user.update({ + where: { id }, + data: { + twoFactorSecret: null, + twoFactorRecoveryCodes: null, + twoFactorConfirmedAt: null, + }, + }); + redirect("/settings/2fa?disabled=1"); } diff --git a/src/actions/user-settings.ts b/src/actions/user-settings.ts index 1369529ae5..45d3968705 100644 --- a/src/actions/user-settings.ts +++ b/src/actions/user-settings.ts @@ -1,40 +1,45 @@ "use server"; -import { z } from "zod"; import { revalidatePath } from "next/cache"; +import { z } from "zod"; +import { actionOk, authAction } from "@/lib/foundation/action"; +import { DatabaseError } from "@/lib/foundation/errors"; import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; -import { authAction, actionOk } from "@/lib/foundation/action"; -import { DatabaseError } from "@/lib/foundation/errors"; const MOTTO_MAX = 127; const mottoSchema = z.object({ - motto: z.string().max(MOTTO_MAX, `Motto must be at most ${MOTTO_MAX} characters`), + motto: z + .string() + .max(MOTTO_MAX, `Motto must be at most ${MOTTO_MAX} characters`), }); const updateMottoAction = authAction({ schema: mottoSchema }, async (ctx) => { - try { - await prisma.user.update({ where: { id: ctx.session.user.id }, data: { motto: ctx.data.motto } }); - } catch { - throw new DatabaseError("Failed to update motto"); - } + try { + await prisma.user.update({ + where: { id: ctx.session.user.id }, + data: { motto: ctx.data.motto }, + }); + } catch { + throw new DatabaseError("Failed to update motto"); + } - try { - await rcon.setMotto(ctx.session.user.id, ctx.data.motto); - } catch { - // RCON is best-effort; the change is already persisted. - } + try { + await rcon.setMotto(ctx.session.user.id, ctx.data.motto); + } catch { + // RCON is best-effort; the change is already persisted. + } - revalidatePath("/settings"); - return actionOk(); + revalidatePath("/settings"); + return actionOk(); }); export async function updateMotto(formData: FormData): Promise { - const motto = String(formData.get("motto") ?? "") - .normalize("NFC") - .slice(0, MOTTO_MAX); - await updateMottoAction({ motto }); + const motto = String(formData.get("motto") ?? "") + .normalize("NFC") + .slice(0, MOTTO_MAX); + await updateMottoAction({ motto }); } export { updateMottoAction }; diff --git a/src/actions/users.ts b/src/actions/users.ts index aed67c5a25..1a16262889 100644 --- a/src/actions/users.ts +++ b/src/actions/users.ts @@ -11,404 +11,447 @@ import { ActionError, actionOk } from "@/lib/safe-action-shared"; import { logAudit } from "@/lib/services/audit"; import { rcon } from "@/lib/services/rcon"; import { notify } from "@/lib/services/webhook"; -import { banUserSchema, createUserSchema, giveBadgeSchema, updateUserSchema } from "@/lib/validators/user"; +import { + banUserSchema, + createUserSchema, + giveBadgeSchema, + updateUserSchema, +} from "@/lib/validators/user"; -const DEFAULT_LOOK = "hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64"; +const DEFAULT_LOOK = + "hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64"; export const createUser = adminAction( - { permission: PERMS.USERS_EDIT, schema: createUserSchema }, - async (ctx) => { - const { username, mail, password, rank, motto } = ctx.data; + { permission: PERMS.USERS_EDIT, schema: createUserSchema }, + async (ctx) => { + const { username, mail, password, rank, motto } = ctx.data; - if (rank >= ctx.session.user.rank && ctx.session.user.rank < 7) { - throw new ActionError("Cannot assign rank equal or higher than your own"); - } + if (rank >= ctx.session.user.rank && ctx.session.user.rank < 7) { + throw new ActionError("Cannot assign rank equal or higher than your own"); + } - const hashedPassword = await hash(password, 12); - const now = Math.floor(Date.now() / 1000); + const hashedPassword = await hash(password, 12); + const now = Math.floor(Date.now() / 1000); - try { - const user = await prisma.$transaction(async (tx) => { - const created = await tx.user.create({ - data: { - username, - mail, - password: hashedPassword, - rank, - motto: motto || "I'm new here!", - look: DEFAULT_LOOK, - credits: 5000, - pixels: 5000, - accountCreated: now, - ipRegister: "0.0.0.0", - ipCurrent: "0.0.0.0", - }, - }); + try { + const user = await prisma.$transaction(async (tx) => { + const created = await tx.user.create({ + data: { + username, + mail, + password: hashedPassword, + rank, + motto: motto || "I'm new here!", + look: DEFAULT_LOOK, + credits: 5000, + pixels: 5000, + accountCreated: now, + ipRegister: "0.0.0.0", + ipCurrent: "0.0.0.0", + }, + }); - await tx.usersSettings.create({ data: { userId: created.id } }); - await tx.usersCurrency.createMany({ - data: [ - { userId: created.id, type: 0, amount: 5000 }, - { userId: created.id, type: 5, amount: 5000 }, - ], - }); + await tx.usersSettings.create({ data: { userId: created.id } }); + await tx.usersCurrency.createMany({ + data: [ + { userId: created.id, type: 0, amount: 5000 }, + { userId: created.id, type: 5, amount: 5000 }, + ], + }); - return created; - }); + return created; + }); - logAudit({ - userId: ctx.session.user.id, - action: "user_create", - target: "User", - targetId: user.id, - after: { username, mail, rank }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "user_create", + target: "User", + targetId: user.id, + after: { username, mail, rank }, + }); - notify({ - action: "user_edit", - actor: ctx.session.user.username, - target: username, - targetId: user.id, - details: "Account created by admin", - }); + notify({ + action: "user_edit", + actor: ctx.session.user.username, + target: username, + targetId: user.id, + details: "Account created by admin", + }); - return actionOk({ id: user.id, username: user.username }); - } catch (err) { - if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") { - const target = (err.meta?.target as string[]) ?? []; - if (target.includes("username")) throw new ActionError("Username already taken"); - if (target.includes("mail")) throw new ActionError("Email already registered"); - throw new ActionError("Username or email already in use"); - } - throw err; - } - }, + return actionOk({ id: user.id, username: user.username }); + } catch (err) { + if ( + err instanceof Prisma.PrismaClientKnownRequestError && + err.code === "P2002" + ) { + const target = (err.meta?.target as string[]) ?? []; + if (target.includes("username")) + throw new ActionError("Username already taken"); + if (target.includes("mail")) + throw new ActionError("Email already registered"); + throw new ActionError("Username or email already in use"); + } + throw err; + } + }, ); const updateUserInput = updateUserSchema.extend({ - id: z.coerce.number().int().positive(), + id: z.coerce.number().int().positive(), }); export const updateUser = adminAction( - { permission: PERMS.USERS_EDIT, schema: updateUserInput }, - async (ctx) => { - const { id, diamonds, duckets, ...userData } = ctx.data; + { permission: PERMS.USERS_EDIT, schema: updateUserInput }, + async (ctx) => { + const { id, diamonds, duckets, ...userData } = ctx.data; - const targetUser = await guardRank(id, ctx.session.user.rank); + const targetUser = await guardRank(id, ctx.session.user.rank); - if (userData.rank !== undefined && userData.rank >= ctx.session.user.rank && ctx.session.user.rank < 7) { - throw new ActionError("Cannot assign rank equal or higher than your own"); - } + if ( + userData.rank !== undefined && + userData.rank >= ctx.session.user.rank && + ctx.session.user.rank < 7 + ) { + throw new ActionError("Cannot assign rank equal or higher than your own"); + } - await prisma.user.update({ where: { id }, data: userData }); + await prisma.user.update({ where: { id }, data: userData }); - if (diamonds !== undefined) { - await prisma.usersCurrency.upsert({ - where: { userId_type: { userId: id, type: 5 } }, - update: { amount: diamonds }, - create: { userId: id, type: 5, amount: diamonds }, - }); - } - if (duckets !== undefined) { - await prisma.usersCurrency.upsert({ - where: { userId_type: { userId: id, type: 0 } }, - update: { amount: duckets }, - create: { userId: id, type: 0, amount: duckets }, - }); - } + if (diamonds !== undefined) { + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId: id, type: 5 } }, + update: { amount: diamonds }, + create: { userId: id, type: 5, amount: diamonds }, + }); + } + if (duckets !== undefined) { + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId: id, type: 0 } }, + update: { amount: duckets }, + create: { userId: id, type: 0, amount: duckets }, + }); + } - logAudit({ - userId: ctx.session.user.id, - action: "user_edit", - target: "User", - targetId: id, - before: { username: targetUser.username, mail: targetUser.mail, rank: targetUser.rank }, - after: userData, - }); + logAudit({ + userId: ctx.session.user.id, + action: "user_edit", + target: "User", + targetId: id, + before: { + username: targetUser.username, + mail: targetUser.mail, + rank: targetUser.rank, + }, + after: userData, + }); - notify({ - action: "user_edit", - actor: ctx.session.user.username, - target: targetUser.username, - targetId: id, - }); + notify({ + action: "user_edit", + actor: ctx.session.user.username, + target: targetUser.username, + targetId: id, + }); - return actionOk(); - }, + return actionOk(); + }, ); const banInput = banUserSchema.extend({}); -export const banUser = adminAction({ permission: PERMS.USERS_BAN, schema: banInput }, async (ctx) => { - const { userId, reason, duration, type, ip } = ctx.data; +export const banUser = adminAction( + { permission: PERMS.USERS_BAN, schema: banInput }, + async (ctx) => { + const { userId, reason, duration, type, ip } = ctx.data; - const targetUser = await guardRank(userId, ctx.session.user.rank); + const targetUser = await guardRank(userId, ctx.session.user.rank); - const now = Math.floor(Date.now() / 1000); - const banExpire = duration > 0 ? now + duration * 3600 : 0; + const now = Math.floor(Date.now() / 1000); + const banExpire = duration > 0 ? now + duration * 3600 : 0; - await prisma.ban.create({ - data: { - userId, - userStaffId: ctx.session.user.id, - timestamp: now, - banExpire, - banReason: reason, - type: type || "account", - ip: ip || "", - machineId: "", - }, - }); + await prisma.ban.create({ + data: { + userId, + userStaffId: ctx.session.user.id, + timestamp: now, + banExpire, + banReason: reason, + type: type || "account", + ip: ip || "", + machineId: "", + }, + }); - await rcon.disconnectUser(userId); + await rcon.disconnectUser(userId); - logAudit({ - userId: ctx.session.user.id, - action: "ban", - target: "User", - targetId: userId, - after: { reason, type, duration }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "ban", + target: "User", + targetId: userId, + after: { reason, type, duration }, + }); - notify({ - action: "ban", - actor: ctx.session.user.username, - target: targetUser.username, - details: reason, - }); + notify({ + action: "ban", + actor: ctx.session.user.username, + target: targetUser.username, + details: reason, + }); - return actionOk(); -}); + return actionOk(); + }, +); const unbanInput = z.object({ userId: z.coerce.number().int().positive() }); -export const unbanUser = adminAction({ permission: PERMS.USERS_BAN, schema: unbanInput }, async (ctx) => { - const { userId } = ctx.data; +export const unbanUser = adminAction( + { permission: PERMS.USERS_BAN, schema: unbanInput }, + async (ctx) => { + const { userId } = ctx.data; - const targetUser = await guardRank(userId, ctx.session.user.rank); + const targetUser = await guardRank(userId, ctx.session.user.rank); - await prisma.ban.deleteMany({ where: { userId } }); + await prisma.ban.deleteMany({ where: { userId } }); - logAudit({ - userId: ctx.session.user.id, - action: "unban", - target: "User", - targetId: userId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "unban", + target: "User", + targetId: userId, + }); - notify({ - action: "unban", - actor: ctx.session.user.username, - target: targetUser.username, - }); + notify({ + action: "unban", + actor: ctx.session.user.username, + target: targetUser.username, + }); - return actionOk(); -}); + return actionOk(); + }, +); export const giveBadge = adminAction( - { permission: PERMS.USERS_EDIT, schema: giveBadgeSchema }, - async (ctx) => { - const { userId, badgeCode } = ctx.data; + { permission: PERMS.USERS_EDIT, schema: giveBadgeSchema }, + async (ctx) => { + const { userId, badgeCode } = ctx.data; - await guardRank(userId, ctx.session.user.rank); + await guardRank(userId, ctx.session.user.rank); - const existing = await prisma.usersBadges.findFirst({ where: { userId, badgeCode } }); - if (existing) throw new ActionError("Badge already assigned"); + const existing = await prisma.usersBadges.findFirst({ + where: { userId, badgeCode }, + }); + if (existing) throw new ActionError("Badge already assigned"); - await prisma.usersBadges.create({ data: { userId, badgeCode } }); - await rcon.giveBadge(userId, badgeCode); + await prisma.usersBadges.create({ data: { userId, badgeCode } }); + await rcon.giveBadge(userId, badgeCode); - return actionOk(); - }, + return actionOk(); + }, ); // ── Remove Badge ──────────────────────────────────────────────────── const removeBadgeSchema = z.object({ - userId: z.coerce.number().int().positive(), - badgeCode: z.string().min(1), + userId: z.coerce.number().int().positive(), + badgeCode: z.string().min(1), }); export const removeBadge = adminAction( - { permission: PERMS.USERS_EDIT, schema: removeBadgeSchema }, - async (ctx) => { - const { userId, badgeCode } = ctx.data; + { permission: PERMS.USERS_EDIT, schema: removeBadgeSchema }, + async (ctx) => { + const { userId, badgeCode } = ctx.data; - await guardRank(userId, ctx.session.user.rank); + await guardRank(userId, ctx.session.user.rank); - const existing = await prisma.usersBadges.findFirst({ where: { userId, badgeCode } }); - if (!existing) throw new ActionError("Badge not found"); + const existing = await prisma.usersBadges.findFirst({ + where: { userId, badgeCode }, + }); + if (!existing) throw new ActionError("Badge not found"); - await prisma.usersBadges.delete({ where: { id: existing.id } }); - await rcon.removeBadge(userId, badgeCode); + await prisma.usersBadges.delete({ where: { id: existing.id } }); + await rcon.removeBadge(userId, badgeCode); - return actionOk(); - }, + return actionOk(); + }, ); // ── Rank guard helper ─────────────────────────────────────────────── async function guardRank(targetUserId: number, sessionRank: number) { - const target = await prisma.user.findUnique({ - where: { id: targetUserId }, - select: { username: true, rank: true, mail: true }, - }); - if (!target) throw new ActionError("User not found"); - if (target.rank >= sessionRank && sessionRank < 7) { - throw new ActionError("Cannot modify user with equal or higher rank"); - } - return target; + const target = await prisma.user.findUnique({ + where: { id: targetUserId }, + select: { username: true, rank: true, mail: true }, + }); + if (!target) throw new ActionError("User not found"); + if (target.rank >= sessionRank && sessionRank < 7) { + throw new ActionError("Cannot modify user with equal or higher rank"); + } + return target; } // ── Reset Password ────────────────────────────────────────────────── const resetPasswordSchema = z.object({ - userId: z.coerce.number().int().positive(), + userId: z.coerce.number().int().positive(), }); export const resetPassword = adminAction( - { permission: PERMS.USERS_RESET_PASSWORD, schema: resetPasswordSchema }, - async (ctx) => { - const target = await guardRank(ctx.data.userId, ctx.session.user.rank); + { permission: PERMS.USERS_RESET_PASSWORD, schema: resetPasswordSchema }, + async (ctx) => { + const target = await guardRank(ctx.data.userId, ctx.session.user.rank); - const newPassword = crypto.randomBytes(12).toString("base64url").slice(0, 16); - const hashed = await hash(newPassword, 10); + const newPassword = crypto + .randomBytes(12) + .toString("base64url") + .slice(0, 16); + const hashed = await hash(newPassword, 10); - await prisma.user.update({ - where: { id: ctx.data.userId }, - data: { password: hashed }, - }); + await prisma.user.update({ + where: { id: ctx.data.userId }, + data: { password: hashed }, + }); - logAudit({ - userId: ctx.session.user.id, - action: "reset_password", - target: "User", - targetId: ctx.data.userId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "reset_password", + target: "User", + targetId: ctx.data.userId, + }); - notify({ - action: "user_edit", - actor: ctx.session.user.username, - target: target.username, - details: "Password reset", - }); + notify({ + action: "user_edit", + actor: ctx.session.user.username, + target: target.username, + details: "Password reset", + }); - return actionOk({ newPassword }); - }, + return actionOk({ newPassword }); + }, ); // ── Disconnect User ───────────────────────────────────────────────── const disconnectSchema = z.object({ - userId: z.coerce.number().int().positive(), + userId: z.coerce.number().int().positive(), }); export const disconnectUser = adminAction( - { permission: PERMS.USERS_EDIT, schema: disconnectSchema }, - async (ctx) => { - const target = await guardRank(ctx.data.userId, ctx.session.user.rank); - const success = await rcon.disconnectUser(ctx.data.userId); - if (!success) throw new ActionError("Failed to disconnect. Is the emulator running?"); + { permission: PERMS.USERS_EDIT, schema: disconnectSchema }, + async (ctx) => { + const target = await guardRank(ctx.data.userId, ctx.session.user.rank); + const success = await rcon.disconnectUser(ctx.data.userId); + if (!success) + throw new ActionError("Failed to disconnect. Is the emulator running?"); - logAudit({ - userId: ctx.session.user.id, - action: "user_disconnect", - target: "User", - targetId: ctx.data.userId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "user_disconnect", + target: "User", + targetId: ctx.data.userId, + }); - notify({ - action: "disconnect", - actor: ctx.session.user.username, - target: target.username, - }); + notify({ + action: "disconnect", + actor: ctx.session.user.username, + target: target.username, + }); - return actionOk(); - }, + return actionOk(); + }, ); // ── Alert User (in-game message) ──────────────────────────────────── const alertUserSchema = z.object({ - userId: z.coerce.number().int().positive(), - message: z.string().min(1).max(500), + userId: z.coerce.number().int().positive(), + message: z.string().min(1).max(500), }); export const alertUser = adminAction( - { permission: PERMS.USERS_EDIT, schema: alertUserSchema }, - async (ctx) => { - const success = await rcon.alertUser(ctx.data.userId, ctx.data.message); - if (!success) throw new ActionError("Failed to send alert. Is the emulator running?"); - return actionOk(); - }, + { permission: PERMS.USERS_EDIT, schema: alertUserSchema }, + async (ctx) => { + const success = await rcon.alertUser(ctx.data.userId, ctx.data.message); + if (!success) + throw new ActionError("Failed to send alert. Is the emulator running?"); + return actionOk(); + }, ); // ── Mute User ─────────────────────────────────────────────────────── const muteSchema = z.object({ - userId: z.coerce.number().int().positive(), - duration: z.coerce.number().int().min(0).default(0), + userId: z.coerce.number().int().positive(), + duration: z.coerce.number().int().min(0).default(0), }); -export const muteUser = adminAction({ permission: PERMS.USERS_EDIT, schema: muteSchema }, async (ctx) => { - const _target = await guardRank(ctx.data.userId, ctx.session.user.rank); - const success = await rcon.muteUser(ctx.data.userId, ctx.data.duration); - if (!success) throw new ActionError("Failed to mute. Is the emulator running?"); +export const muteUser = adminAction( + { permission: PERMS.USERS_EDIT, schema: muteSchema }, + async (ctx) => { + const _target = await guardRank(ctx.data.userId, ctx.session.user.rank); + const success = await rcon.muteUser(ctx.data.userId, ctx.data.duration); + if (!success) + throw new ActionError("Failed to mute. Is the emulator running?"); - logAudit({ - userId: ctx.session.user.id, - action: "user_mute", - target: "User", - targetId: ctx.data.userId, - after: { duration: ctx.data.duration }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "user_mute", + target: "User", + targetId: ctx.data.userId, + after: { duration: ctx.data.duration }, + }); - return actionOk(); -}); + return actionOk(); + }, +); // ── Unmute User ───────────────────────────────────────────────────── const unmuteSchema = z.object({ - userId: z.coerce.number().int().positive(), + userId: z.coerce.number().int().positive(), }); -export const unmuteUser = adminAction({ permission: PERMS.USERS_EDIT, schema: unmuteSchema }, async (ctx) => { - await guardRank(ctx.data.userId, ctx.session.user.rank); - const success = await rcon.unmuteUser(ctx.data.userId); - if (!success) throw new ActionError("Failed to unmute. Is the emulator running?"); +export const unmuteUser = adminAction( + { permission: PERMS.USERS_EDIT, schema: unmuteSchema }, + async (ctx) => { + await guardRank(ctx.data.userId, ctx.session.user.rank); + const success = await rcon.unmuteUser(ctx.data.userId); + if (!success) + throw new ActionError("Failed to unmute. Is the emulator running?"); - logAudit({ - userId: ctx.session.user.id, - action: "user_unmute", - target: "User", - targetId: ctx.data.userId, - }); + logAudit({ + userId: ctx.session.user.id, + action: "user_unmute", + target: "User", + targetId: ctx.data.userId, + }); - return actionOk(); -}); + return actionOk(); + }, +); // ── Send Credits via RCON ─────────────────────────────────────────── const sendCreditsSchema = z.object({ - userId: z.coerce.number().int().positive(), - amount: z.coerce.number().int().min(1).max(1000000), + userId: z.coerce.number().int().positive(), + amount: z.coerce.number().int().min(1).max(1000000), }); export const sendCredits = adminAction( - { permission: PERMS.USERS_EDIT, schema: sendCreditsSchema }, - async (ctx) => { - const _target = await guardRank(ctx.data.userId, ctx.session.user.rank); - const success = await rcon.giveCredits(ctx.data.userId, ctx.data.amount); - if (!success) throw new ActionError("Failed to send credits. Is the emulator running?"); + { permission: PERMS.USERS_EDIT, schema: sendCreditsSchema }, + async (ctx) => { + const _target = await guardRank(ctx.data.userId, ctx.session.user.rank); + const success = await rcon.giveCredits(ctx.data.userId, ctx.data.amount); + if (!success) + throw new ActionError("Failed to send credits. Is the emulator running?"); - logAudit({ - userId: ctx.session.user.id, - action: "user_send_credits", - target: "User", - targetId: ctx.data.userId, - after: { amount: ctx.data.amount }, - }); + logAudit({ + userId: ctx.session.user.id, + action: "user_send_credits", + target: "User", + targetId: ctx.data.userId, + after: { amount: ctx.data.amount }, + }); - return actionOk(); - }, + return actionOk(); + }, ); diff --git a/src/actions/voucher.ts b/src/actions/voucher.ts index a76750f499..c2f1ee32d7 100644 --- a/src/actions/voucher.ts +++ b/src/actions/voucher.ts @@ -23,102 +23,127 @@ export type RedeemState = { ok: boolean; message: string } | null; * voucher schema carries a single `amount`, granted as the website credits * wallet currency. */ -export async function redeem(_prev: RedeemState, formData: FormData): Promise { - const session = await auth(); - if (!session?.user?.id) { - return { ok: false, message: "You must be signed in to redeem a voucher." }; - } +export async function redeem( + _prev: RedeemState, + formData: FormData, +): Promise { + const session = await auth(); + if (!session?.user?.id) { + return { ok: false, message: "You must be signed in to redeem a voucher." }; + } - const userId = Number(session.user.id); - if (!Number.isFinite(userId)) { - return { ok: false, message: "Your session is invalid. Please sign in again." }; - } + const userId = Number(session.user.id); + if (!Number.isFinite(userId)) { + return { + ok: false, + message: "Your session is invalid. Please sign in again.", + }; + } - const code = String(formData.get("code") ?? "") - .normalize("NFC") - .trim(); - if (!code) { - return { ok: false, message: "Please enter a voucher code." }; - } + const code = String(formData.get("code") ?? "") + .normalize("NFC") + .trim(); + if (!code) { + return { ok: false, message: "Please enter a voucher code." }; + } - // Look up the code (website_shop_vouchers.code is unique). - let voucher: { - id: bigint; - amount: number; - maxUses: number; - useCount: number; - expiresAt: Date | null; - } | null; - try { - voucher = await prisma.websiteShopVouchers.findUnique({ - where: { code }, - select: { id: true, amount: true, maxUses: true, useCount: true, expiresAt: true }, - }); - } catch { - return { ok: false, message: "We couldn't reach the server. Please try again." }; - } + // Look up the code (website_shop_vouchers.code is unique). + let voucher: { + id: bigint; + amount: number; + maxUses: number; + useCount: number; + expiresAt: Date | null; + } | null; + try { + voucher = await prisma.websiteShopVouchers.findUnique({ + where: { code }, + select: { + id: true, + amount: true, + maxUses: true, + useCount: true, + expiresAt: true, + }, + }); + } catch { + return { + ok: false, + message: "We couldn't reach the server. Please try again.", + }; + } - // Not found OR already expired -> generic "no active voucher" (matches AtomCMS). - if (!voucher || (voucher.expiresAt && voucher.expiresAt.getTime() <= Date.now())) { - return { ok: false, message: "No active voucher with the given code was found." }; - } + // Not found OR already expired -> generic "no active voucher" (matches AtomCMS). + if ( + !voucher || + (voucher.expiresAt && voucher.expiresAt.getTime() <= Date.now()) + ) { + return { + ok: false, + message: "No active voucher with the given code was found.", + }; + } - // One redemption per user. - try { - const already = await prisma.websiteUsedShopVouchers.findFirst({ - where: { userId, voucherId: voucher.id }, - select: { id: true }, - }); - if (already) { - return { ok: false, message: "You can only use each shop voucher once." }; - } - } catch { - return { ok: false, message: "We couldn't reach the server. Please try again." }; - } + // One redemption per user. + try { + const already = await prisma.websiteUsedShopVouchers.findFirst({ + where: { userId, voucherId: voucher.id }, + select: { id: true }, + }); + if (already) { + return { ok: false, message: "You can only use each shop voucher once." }; + } + } catch { + return { + ok: false, + message: "We couldn't reach the server. Please try again.", + }; + } - // Record the redemption first so a successful grant can never be double-claimed. - try { - await prisma.websiteUsedShopVouchers.create({ - data: { userId, voucherId: voucher.id }, - }); - } catch { - // Most likely a race (another tab redeemed it) — treat as already used. - return { ok: false, message: "You can only use each shop voucher once." }; - } + // Record the redemption first so a successful grant can never be double-claimed. + try { + await prisma.websiteUsedShopVouchers.create({ + data: { userId, voucherId: voucher.id }, + }); + } catch { + // Most likely a race (another tab redeemed it) — treat as already used. + return { ok: false, message: "You can only use each shop voucher once." }; + } - // Grant the reward. The voucher carries a single amount, delivered as credits. - try { - await sendCurrency({ rcon, db: prisma }, userId, "credits", voucher.amount); - } catch { - // sendCurrency already falls back to a direct DB write; if it still throws, - // the used-row stands and the balance simply wasn't credited — surface that. - return { - ok: false, - message: "Your voucher was accepted but the reward could not be delivered. Contact staff.", - }; - } + // Grant the reward. The voucher carries a single amount, delivered as credits. + try { + await sendCurrency({ rcon, db: prisma }, userId, "credits", voucher.amount); + } catch { + // sendCurrency already falls back to a direct DB write; if it still throws, + // the used-row stands and the balance simply wasn't credited — surface that. + return { + ok: false, + message: + "Your voucher was accepted but the reward could not be delivered. Contact staff.", + }; + } - // Bump use_count and expire the voucher once the cap is reached. - try { - const updated = await prisma.websiteShopVouchers.update({ - where: { id: voucher.id }, - data: { useCount: { increment: 1 } }, - select: { maxUses: true, useCount: true }, - }); - if (updated.maxUses && updated.useCount >= updated.maxUses) { - await prisma.websiteShopVouchers.update({ - where: { id: voucher.id }, - data: { expiresAt: new Date() }, - }); - } - } catch { - // Reward already delivered; the counter bump is best-effort. - } + // Bump use_count and expire the voucher once the cap is reached. + try { + const updated = await prisma.websiteShopVouchers.update({ + where: { id: voucher.id }, + data: { useCount: { increment: 1 } }, + select: { maxUses: true, useCount: true }, + }); + if (updated.maxUses && updated.useCount >= updated.maxUses) { + await prisma.websiteShopVouchers.update({ + where: { id: voucher.id }, + data: { expiresAt: new Date() }, + }); + } + } catch { + // Reward already delivered; the counter bump is best-effort. + } - revalidatePath("/redeem"); + revalidatePath("/redeem"); - return { - ok: true, - message: `Success! Your balance has been increased by ${voucher.amount.toLocaleString()} credits.`, - }; + return { + ok: true, + message: `Success! Your balance has been increased by ${voucher.amount.toLocaleString()} credits.`, + }; } diff --git a/src/actions/watch.ts b/src/actions/watch.ts index 42eb1f6210..6115a0ef15 100644 --- a/src/actions/watch.ts +++ b/src/actions/watch.ts @@ -8,8 +8,8 @@ import { adminAction } from "@/lib/safe-action"; import { actionOk } from "@/lib/safe-action-shared"; const toggleWatchSchema = z.object({ - targetUserId: z.coerce.number().int().positive(), - reason: z.string().max(255).optional(), + targetUserId: z.coerce.number().int().positive(), + reason: z.string().max(255).optional(), }); /** @@ -19,25 +19,25 @@ const toggleWatchSchema = z.object({ * the UI can flip the badge without re-fetching. */ export const toggleUserWatch = adminAction( - { permission: PERMS.USERS_VIEW, schema: toggleWatchSchema }, - async (ctx) => { - const staffId = ctx.session.user.id; - const { targetUserId, reason } = ctx.data; + { permission: PERMS.USERS_VIEW, schema: toggleWatchSchema }, + async (ctx) => { + const staffId = ctx.session.user.id; + const { targetUserId, reason } = ctx.data; - const existing = await prisma.userWatch.findUnique({ - where: { staffId_targetUserId: { staffId, targetUserId } }, - }); + const existing = await prisma.userWatch.findUnique({ + where: { staffId_targetUserId: { staffId, targetUserId } }, + }); - if (existing) { - await prisma.userWatch.delete({ where: { id: existing.id } }); - revalidateTag(`user-watch:${staffId}`, { expire: 0 }); - return actionOk({ watching: false }); - } + if (existing) { + await prisma.userWatch.delete({ where: { id: existing.id } }); + revalidateTag(`user-watch:${staffId}`, { expire: 0 }); + return actionOk({ watching: false }); + } - await prisma.userWatch.create({ - data: { staffId, targetUserId, reason: reason ?? "" }, - }); - revalidateTag(`user-watch:${staffId}`, { expire: 0 }); - return actionOk({ watching: true }); - }, + await prisma.userWatch.create({ + data: { staffId, targetUserId, reason: reason ?? "" }, + }); + revalidateTag(`user-watch:${staffId}`, { expire: 0 }); + return actionOk({ watching: true }); + }, ); diff --git a/src/app/admin/achievements/page.tsx b/src/app/admin/achievements/page.tsx index d12a4e0ad9..b30279c302 100644 --- a/src/app/admin/achievements/page.tsx +++ b/src/app/admin/achievements/page.tsx @@ -6,107 +6,120 @@ import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; type Achievement = { - name: string; - category: string; - level: number; - rewardAmount: number; - rewardType: number; - points: number | null; - progressNeeded: number; + name: string; + category: string; + level: number; + rewardAmount: number; + rewardType: number; + points: number | null; + progressNeeded: number; }; export default async function AdminAchievements() { - const t = await getTranslations("pages.admin.achievements"); + const t = await getTranslations("pages.admin.achievements"); - let achievements: Achievement[]; - try { - achievements = await prisma.achievements.findMany({ - select: { - name: true, - category: true, - level: true, - rewardAmount: true, - rewardType: true, - points: true, - progressNeeded: true, - }, - orderBy: [{ category: "asc" }, { name: "asc" }, { level: "asc" }], - }); - } catch { - achievements = []; - } + let achievements: Achievement[]; + try { + achievements = await prisma.achievements.findMany({ + select: { + name: true, + category: true, + level: true, + rewardAmount: true, + rewardType: true, + points: true, + progressNeeded: true, + }, + orderBy: [{ category: "asc" }, { name: "asc" }, { level: "asc" }], + }); + } catch { + achievements = []; + } - const groups = new Map(); - for (const a of achievements) { - const list = groups.get(a.category) ?? []; - list.push(a); - groups.set(a.category, list); - } + const groups = new Map(); + for (const a of achievements) { + const list = groups.get(a.category) ?? []; + list.push(a); + groups.set(a.category, list); + } - const distinctNames = new Set(achievements.map((a) => a.name)).size; + const distinctNames = new Set(achievements.map((a) => a.name)).size; - return ( -
-
-
- -
-
-

{t("title")}

-

- {t("subtitle", { rows: achievements.length, categories: groups.size })} -

-
-
+ return ( +
+
+
+ +
+
+

+ {t("title")} +

+

+ {t("subtitle", { + rows: achievements.length, + categories: groups.size, + })} +

+
+
-
- - - -
+
+ + + +
- {groups.size === 0 ? ( -
{t("noAchievements")}
- ) : ( - [...groups.entries()].map(([category, rows]) => ( -
-

- {category}{" "} - - {rows.length} - -

-
- - - - - - - - - - - - - {rows.map((a) => ( - - - - - - - - - ))} - -
{t("colName")}{t("colLevel")}{t("colProgress")}{t("colRewardType")}{t("colRewardAmount")}{t("colPoints")}
- {a.name} - {a.level}{a.progressNeeded}{a.rewardType}{a.rewardAmount}{a.points ?? 0}
-
-
- )) - )} -
- ); + {groups.size === 0 ? ( +
{t("noAchievements")}
+ ) : ( + [...groups.entries()].map(([category, rows]) => ( +
+

+ {category}{" "} + + {rows.length} + +

+
+ + + + + + + + + + + + + {rows.map((a) => ( + + + + + + + + + ))} + +
{t("colName")}{t("colLevel")}{t("colProgress")}{t("colRewardType")}{t("colRewardAmount")}{t("colPoints")}
+ {a.name} + {a.level}{a.progressNeeded}{a.rewardType}{a.rewardAmount}{a.points ?? 0}
+
+
+ )) + )} +
+ ); } diff --git a/src/app/admin/ads/[id]/page.tsx b/src/app/admin/ads/[id]/page.tsx index 0e17800cb0..c79cd6be84 100644 --- a/src/app/admin/ads/[id]/page.tsx +++ b/src/app/admin/ads/[id]/page.tsx @@ -1,47 +1,59 @@ -import Link from "next/link"; import { Megaphone } from "lucide-react"; +import Link from "next/link"; import { notFound } from "next/navigation"; import { deleteAd, updateAd } from "@/actions/admin-ads"; import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; -export default async function EditAd({ params }: { params: Promise<{ id: string }> }) { - const { id } = await params; - let ad: Awaited> = null; - try { - ad = await prisma.websiteAds.findUnique({ where: { id: BigInt(id) } }); - } catch { - notFound(); - } - if (!ad) notFound(); +export default async function EditAd({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { id } = await params; + let ad: Awaited> = null; + try { + ad = await prisma.websiteAds.findUnique({ where: { id: BigInt(id) } }); + } catch { + notFound(); + } + if (!ad) notFound(); - return ( -
-
-
- -
-
-

Edit advertisement

-

- ← Advertisements -

-
-
-
- - - -
-
- - -
-
- ); + return ( +
+
+
+ +
+
+

+ Edit advertisement +

+

+ ← Advertisements +

+
+
+
+ + + +
+
+ + +
+
+ ); } diff --git a/src/app/admin/ads/new/page.tsx b/src/app/admin/ads/new/page.tsx index d95edcd952..3004d84c60 100644 --- a/src/app/admin/ads/new/page.tsx +++ b/src/app/admin/ads/new/page.tsx @@ -1,27 +1,29 @@ -import Link from "next/link"; import { Megaphone } from "lucide-react"; +import Link from "next/link"; import { createAd } from "@/actions/admin-ads"; export default function NewAd() { - return ( -
-
-
- -
-
-

New advertisement

-

- ← Advertisements -

-
-
-
- - -
-
- ); + return ( +
+
+
+ +
+
+

+ New advertisement +

+

+ ← Advertisements +

+
+
+
+ + +
+
+ ); } diff --git a/src/app/admin/ads/page.tsx b/src/app/admin/ads/page.tsx index 4fa7ffeaa0..fc652ce489 100644 --- a/src/app/admin/ads/page.tsx +++ b/src/app/admin/ads/page.tsx @@ -1,81 +1,98 @@ +import { Megaphone } from "lucide-react"; import Link from "next/link"; import { getTranslations } from "next-intl/server"; -import { Megaphone } from "lucide-react"; import { StatusCard } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; export default async function AdminAds() { - const t = await getTranslations("pages.admin.ads"); - let ads: Awaited>; - try { - ads = await prisma.websiteAds.findMany({ - orderBy: { id: "desc" }, - take: 500, - }); - } catch { - ads = []; - } + const t = await getTranslations("pages.admin.ads"); + let ads: Awaited>; + try { + ads = await prisma.websiteAds.findMany({ + orderBy: { id: "desc" }, + take: 500, + }); + } catch { + ads = []; + } - const latest = ads[0]?.createdAt ? ads[0].createdAt.toISOString().slice(0, 10) : "—"; + const latest = ads[0]?.createdAt + ? ads[0].createdAt.toISOString().slice(0, 10) + : "—"; - return ( -
-
-
- -
-
-

{t("title")}

-

{t("subtitle")}

-
- - + {t("newAd")} - -
+ return ( +
+
+
+ +
+
+

+ {t("title")} +

+

+ {t("subtitle")} +

+
+ + + {t("newAd")} + +
-
- - -
+
+ + +
-
-

{t("allAds")}

- {ads.length === 0 ? ( -
{t("noAds")}
- ) : ( - - - - - - - - - - {ads.map((ad) => ( - - - - - - - ))} - -
{t("colPreview")}{t("colImage")}{t("colCreated")} -
- {/* eslint-disable-next-line @next/next/no-img-element */} - - - {ad.image} - - {ad.createdAt ? ad.createdAt.toISOString().slice(0, 10) : "—"} - - {t("edit")} -
- )} -
-
- ); +
+

{t("allAds")}

+ {ads.length === 0 ? ( +
{t("noAds")}
+ ) : ( + + + + + + + + + + {ads.map((ad) => ( + + + + + + + ))} + +
{t("colPreview")}{t("colImage")}{t("colCreated")} +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + + + {ad.image} + + {ad.createdAt + ? ad.createdAt.toISOString().slice(0, 10) + : "—"} + + {t("edit")} +
+ )} +
+
+ ); } diff --git a/src/app/admin/alerts/page.tsx b/src/app/admin/alerts/page.tsx index 02dbbd617f..92a4701d2c 100644 --- a/src/app/admin/alerts/page.tsx +++ b/src/app/admin/alerts/page.tsx @@ -1,5 +1,5 @@ -import { getTranslations } from "next-intl/server"; import { Bell } from "lucide-react"; +import { getTranslations } from "next-intl/server"; import { sendHotelAlert } from "@/actions/admin-alerts"; import { StatusCard } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; @@ -10,147 +10,171 @@ const CRITICAL = new Set(["critical", "error", "danger"]); const WARNING = new Set(["warning", "warn"]); function fromDate(d: Date | null | undefined): string { - return d ? d.toISOString().slice(0, 16).replace("T", " ") : "—"; + return d ? d.toISOString().slice(0, 16).replace("T", " ") : "—"; } function severityColor(severity: string): string { - switch (severity.toLowerCase()) { - case "critical": - case "error": - case "danger": - return "var(--color-danger)"; - case "warning": - case "warn": - return "var(--admin-accent)"; - case "success": - return "var(--color-secondary)"; - case "info": - case "notice": - return "var(--color-accent)"; - default: - return "var(--admin-text-muted)"; - } + switch (severity.toLowerCase()) { + case "critical": + case "error": + case "danger": + return "var(--color-danger)"; + case "warning": + case "warn": + return "var(--admin-accent)"; + case "success": + return "var(--color-secondary)"; + case "info": + case "notice": + return "var(--color-accent)"; + default: + return "var(--admin-text-muted)"; + } } function Badge({ label, color }: { label: string; color: string }) { - return ( - - {label} - - ); + return ( + + {label} + + ); } export default async function AdminAlerts() { - const t = await getTranslations("pages.admin.alerts"); + const t = await getTranslations("pages.admin.alerts"); - let alerts: Awaited>; - try { - alerts = await prisma.alertLogs.findMany({ - orderBy: { id: "desc" }, - take: 100, - }); - } catch { - alerts = []; - } + let alerts: Awaited>; + try { + alerts = await prisma.alertLogs.findMany({ + orderBy: { id: "desc" }, + take: 100, + }); + } catch { + alerts = []; + } - const critical = alerts.filter((a) => CRITICAL.has((a.severity || "").toLowerCase())).length; - const warnings = alerts.filter((a) => WARNING.has((a.severity || "").toLowerCase())).length; - const unread = alerts.filter((a) => !a.isRead).length; + const critical = alerts.filter((a) => + CRITICAL.has((a.severity || "").toLowerCase()), + ).length; + const warnings = alerts.filter((a) => + WARNING.has((a.severity || "").toLowerCase()), + ).length; + const unread = alerts.filter((a) => !a.isRead).length; - return ( -
-
-
- -
-
-

{t("title")}

-

{t("subtitle")}

-
-
+ return ( +
+
+
+ +
+
+

+ {t("title")} +

+

+ {t("subtitle")} +

+
+
-
- - 0 ? "danger" : "ok"} icon="🚨" /> - 0 ? "warn" : "neutral"} - icon="⚠️" - /> - 0 ? "warn" : "ok"} icon="📨" /> -
+
+ + 0 ? "danger" : "ok"} + icon="🚨" + /> + 0 ? "warn" : "neutral"} + icon="⚠️" + /> + 0 ? "warn" : "ok"} + icon="📨" + /> +
-
-

{t("form.send")}

-

- {t.rich("form.description", { - code: (chunks) => {chunks}, - })} -

-
-