diff --git a/.gitignore b/.gitignore index f222a6619..59e2780ea 100644 --- a/.gitignore +++ b/.gitignore @@ -105,3 +105,6 @@ supabase/ migration.sh stat.sh + +CLAUDE.md +interview diff --git a/README.md b/README.md index aedb989de..20ec18009 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,13 @@ bun install ```bash bun setup ``` -4. Run Autumn: + +4. Generate the relevant tables in your postgres DB +```bash +bun db:generate && bun db:migrate +``` + +5. Run Autumn: For Windows ```bash diff --git a/biome.json b/biome.json index 6c514ad12..3a9e72855 100644 --- a/biome.json +++ b/biome.json @@ -15,7 +15,10 @@ "linter": { "enabled": true, "rules": { - "recommended": true + "recommended": true, + "complexity": { + "noStaticOnlyClass": "off" + } } }, "javascript": { diff --git a/package.json b/package.json index 24e59704e..7be32b2d5 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,11 @@ "dev:wsl": "concurrently \"cd server && npm run dev\" \"cd vite && npm run dev\" \"cd shared && npm run dev\"", "setup": "node setup.js", "setupci": "node setupci", - "db:push": " pnpm -F shared db:push", - "db:generate": "pnpm -F shared db:generate", - "db:migrate": " pnpm -F shared db:migrate", + + "db:push": " bun -F @autumn/shared db:push", + "db:generate": "bun -F @autumn/shared db:generate", + "db:migrate": " bun -F @autumn/shared db:migrate", + "docker:up": "docker compose -f docker-compose.dev.yml up --build", "docker:up:unix": "docker compose -f docker-compose.unix.yml up --build", "docker:up:ci": "docker compose -f docker-compose.ci.yml up --build", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..fb7b64c60 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,15047 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@wooorm/starry-night': + specifier: ^3.8.0 + version: 3.8.0 + ag-charts-react: + specifier: ^12.0.1 + version: 12.1.2(react@18.3.1) + chalk: + specifier: ^5.3.0 + version: 5.6.0 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.4 + tailwind-scrollbar-hide: + specifier: ^4.0.0 + version: 4.0.0(tailwindcss@4.1.12) + devDependencies: + '@types/node': + specifier: ^24.0.3 + version: 24.3.0 + concurrently: + specifier: ^9.1.2 + version: 9.2.1 + dotenv: + specifier: ^16.5.0 + version: 16.6.1 + inquirer: + specifier: ^12.6.3 + version: 12.9.4(@types/node@24.3.0) + + server: + dependencies: + '@ai-sdk/anthropic': + specifier: ^1.2.10 + version: 1.2.12(zod@3.25.76) + '@anthropic-ai/sdk': + specifier: ^0.32.1 + version: 0.32.1 + '@autumn/shared': + specifier: workspace:* + version: link:../shared + '@axiomhq/pino': + specifier: ^1.3.1 + version: 1.3.1 + '@browserbasehq/sdk': + specifier: ^2.6.0 + version: 2.6.0 + '@clerk/express': + specifier: ^1.3.22 + version: 1.7.26(express@4.21.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clickhouse/client': + specifier: ^1.11.2 + version: 1.12.1 + '@date-fns/tz': + specifier: ^1.2.0 + version: 1.4.1 + '@date-fns/utc': + specifier: ^2.1.0 + version: 2.1.1 + '@hyperbrowser/sdk': + specifier: ^0.54.0 + version: 0.54.0 + '@hyperdx/node-opentelemetry': + specifier: ^0.8.2 + version: 0.8.2 + '@logtail/node': + specifier: ^0.5.2 + version: 0.5.5 + '@opentelemetry/api': + specifier: ^1.9.0 + version: 1.9.0 + '@opentelemetry/auto-instrumentations-node': + specifier: ^0.60.1 + version: 0.60.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)) + '@opentelemetry/exporter-trace-otlp-proto': + specifier: ^0.202.0 + version: 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': + specifier: ^2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': + specifier: ^2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': + specifier: ^0.202.0 + version: 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': + specifier: ^2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': + specifier: ^2.0.1 + version: 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': + specifier: ^1.34.0 + version: 1.36.0 + '@react-email/components': + specifier: ^0.0.42 + version: 0.0.42(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@sentry/node': + specifier: ^9.30.0 + version: 9.46.0 + '@supabase/supabase-js': + specifier: ^2.46.2 + version: 2.56.1 + '@upstash/redis': + specifier: ^1.35.1 + version: 1.35.3 + ai: + specifier: ^4.3.10 + version: 4.3.19(react@18.3.1)(zod@3.25.76) + autumn-js: + specifier: ^0.1.8 + version: 0.1.28(better-auth@1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76))(better-call@1.0.16)(convex@1.26.2(@clerk/clerk-react@5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1))(react@18.3.1) + axios: + specifier: ^1.8.3 + version: 1.11.0 + better-auth: + specifier: ^1.2.9 + version: 1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76) + body-parser: + specifier: ^1.20.3 + version: 1.20.3 + bullmq: + specifier: ^5.56.2 + version: 5.58.4 + chai: + specifier: ^5.1.2 + version: 5.3.3 + chai-http: + specifier: ^5.1.1 + version: 5.1.2 + chalk: + specifier: ^5.3.0 + version: 5.6.0 + cloudflare: + specifier: ^4.4.1 + version: 4.5.0 + cors: + specifier: ^2.8.5 + version: 2.8.5 + cron: + specifier: ^3.5.0 + version: 3.5.0 + csv-parse: + specifier: ^5.6.0 + version: 5.6.0 + csv-parser: + specifier: ^3.2.0 + version: 3.2.0 + currency-symbol-map: + specifier: ^5.1.0 + version: 5.1.0 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + decimal.js: + specifier: ^10.5.0 + version: 10.6.0 + detect-content-type: + specifier: ^1.2.0 + version: 1.2.0 + dotenv: + specifier: ^16.5.0 + version: 16.6.1 + drizzle-orm: + specifier: ^0.43.1 + version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) + express: + specifier: ^4.21.1 + version: 4.21.2 + express-rate-limit: + specifier: ^7.5.1 + version: 7.5.1(express@4.21.2) + fetch-retry: + specifier: ^6.0.0 + version: 6.0.0 + http-status-codes: + specifier: ^2.3.0 + version: 2.3.0 + ioredis: + specifier: ^5.5.0 + version: 5.7.0 + ksuid: + specifier: ^3.0.0 + version: 3.0.0 + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + loops: + specifier: ^5.0.1 + version: 5.0.1 + mime-detect: + specifier: ^1.3.0 + version: 1.3.0 + openai: + specifier: ^4.85.2 + version: 4.104.0(ws@8.18.3)(zod@3.25.76) + pg: + specifier: ^8.13.1 + version: 8.16.3 + pino: + specifier: ^9.6.0 + version: 9.9.0 + pino-pretty: + specifier: ^13.0.0 + version: 13.1.1 + postgres: + specifier: ^3.4.7 + version: 3.4.7 + posthog-node: + specifier: ^4.17.2 + version: 4.18.0 + puppeteer-core: + specifier: ^24.14.0 + version: 24.18.0 + react: + specifier: ^18.2.0 + version: 18.3.1 + recaseai: + specifier: ^0.0.37 + version: 0.0.37(ws@8.18.3)(zod@3.25.76) + resend: + specifier: ^4.1.1 + version: 4.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + stripe: + specifier: ^18.4.0 + version: 18.5.0(@types/node@22.18.0) + svix: + specifier: ^1.45.1 + version: 1.75.0 + tsc-alias: + specifier: ^1.8.16 + version: 1.8.16 + ws: + specifier: ^8.18.0 + version: 8.18.3 + zod: + specifier: ^3.25.23 + version: 3.25.76 + devDependencies: + '@types/chai': + specifier: ^5.0.1 + version: 5.2.2 + '@types/chai-http': + specifier: ^3.0.5 + version: 3.0.5 + '@types/cors': + specifier: ^2.8.19 + version: 2.8.19 + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 + '@types/lodash-es': + specifier: ^4.17.12 + version: 4.17.12 + '@types/mocha': + specifier: ^10.0.10 + version: 10.0.10 + '@types/node': + specifier: ^22.13.4 + version: 22.18.0 + '@types/pg': + specifier: ^8.11.10 + version: 8.15.5 + '@types/react': + specifier: ^18.3.18 + version: 18.3.24 + '@types/react-dom': + specifier: ^18.3.5 + version: 18.3.7(@types/react@18.3.24) + '@types/ws': + specifier: ^8.18.1 + version: 8.18.1 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.4 + mocha: + specifier: ^11.1.0 + version: 11.7.1 + nodemon: + specifier: ^3.1.10 + version: 3.1.10 + react-email: + specifier: 4.0.16 + version: 4.0.16(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.18.0)(typescript@5.7.3) + tsconfig-paths: + specifier: ^4.2.0 + version: 4.2.0 + tsx: + specifier: ^4.19.4 + version: 4.20.5 + typescript: + specifier: ^5.7.3 + version: 5.7.3 + + shared: + dependencies: + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + decimal.js: + specifier: ^10.5.0 + version: 10.6.0 + dotenv: + specifier: ^16.5.0 + version: 16.6.1 + drizzle-kit: + specifier: ^0.31.1 + version: 0.31.4 + drizzle-orm: + specifier: ^0.43.1 + version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) + drizzle-zod: + specifier: ^0.8.2 + version: 0.8.3(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.76) + zod: + specifier: ^3.25.23 + version: 3.25.76 + devDependencies: + '@types/bun': + specifier: latest + version: 1.2.21(@types/react@18.3.24) + '@types/node': + specifier: ^24.0.3 + version: 24.3.0 + '@typescript/native-preview': + specifier: ^7.0.0-dev.20250731.1 + version: 7.0.0-dev.20250901.1 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + nodemon: + specifier: ^3.1.7 + version: 3.1.10 + tsx: + specifier: ^4.19.4 + version: 4.20.5 + typescript: + specifier: ^5.7.2 + version: 5.7.3 + + vite: + dependencies: + '@autumn/shared': + specifier: workspace:* + version: link:../shared + '@better-auth/stripe': + specifier: ^1.2.12 + version: 1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76) + '@clerk/clerk-react': + specifier: ^5.24.2 + version: 5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@fortawesome/free-brands-svg-icons': + specifier: ^6.7.2 + version: 6.7.2 + '@fortawesome/react-fontawesome': + specifier: ^0.2.2 + version: 0.2.6(@fortawesome/fontawesome-svg-core@7.0.0)(react@18.3.1) + '@heroicons/react': + specifier: ^2.2.0 + version: 2.2.0(react@18.3.1) + '@radix-ui/react-accordion': + specifier: ^1.2.11 + version: 1.2.12(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-checkbox': + specifier: ^1.1.4 + version: 1.3.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-context-menu': + specifier: ^2.2.16 + version: 2.2.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': + specifier: ^1.1.14 + version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.15 + version: 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': + specifier: ^1.1.14 + version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-scroll-area': + specifier: ^1.2.3 + version: 1.2.10(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-select': + specifier: ^2.1.6 + version: 2.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-separator': + specifier: ^1.1.2 + version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-switch': + specifier: ^1.2.5 + version: 1.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tabs': + specifier: ^1.1.3 + version: 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': + specifier: ^1.1.8 + version: 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@supabase/ssr': + specifier: ^0.5.2 + version: 0.5.2(@supabase/supabase-js@2.56.1) + '@tailwindcss/vite': + specifier: ^4.0.13 + version: 4.1.12(vite@6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + '@tanstack/react-query': + specifier: ^5.85.6 + version: 5.87.1(react@18.3.1) + '@tanstack/react-query-devtools': + specifier: ^5.85.6 + version: 5.87.1(@tanstack/react-query@5.87.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-table': + specifier: ^8.21.3 + version: 8.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/recharts': + specifier: ^2.0.1 + version: 2.0.1(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(redux@5.0.1) + '@wooorm/starry-night': + specifier: ^3.6.0 + version: 3.8.0 + ag-charts-community: + specifier: ^12.0.2 + version: 12.1.2 + ag-grid-community: + specifier: ^34.0.2 + version: 34.1.2 + ag-grid-react: + specifier: ^34.0.2 + version: 34.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + autumn-js: + specifier: ^0.1.8 + version: 0.1.28(better-auth@1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76))(better-call@1.0.16)(convex@1.26.2(@clerk/clerk-react@5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1))(react@18.3.1) + axios: + specifier: ^1.8.3 + version: 1.11.0 + better-auth: + specifier: ^1.2.9 + version: 1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + cmdk: + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + date-fns: + specifier: ^3.6.0 + version: 3.6.0 + decimal.js: + specifier: ^10.5.0 + version: 10.6.0 + hast-util-to-html: + specifier: ^9.0.5 + version: 9.0.5 + input-otp: + specifier: ^1.4.2 + version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lodash: + specifier: ^4.17.21 + version: 4.17.21 + lucide-react: + specifier: ^0.525.0 + version: 0.525.0(react@18.3.1) + motion: + specifier: ^12.16.0 + version: 12.23.12(@emotion/is-prop-valid@0.7.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + nuqs: + specifier: ^2.4.3 + version: 2.5.2(next@15.5.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router-dom@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + posthog-js: + specifier: ^1.230.4 + version: 1.261.0 + prism-themes: + specifier: ^1.9.0 + version: 1.9.0 + prismjs: + specifier: ^1.29.0 + version: 1.30.0 + react: + specifier: ^18.2.0 + version: 18.3.1 + react-confetti-explosion: + specifier: ^2.1.2 + version: 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-day-picker: + specifier: ^8.10.1 + version: 8.10.1(date-fns@3.6.0)(react@18.3.1) + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + react-dropzone: + specifier: ^14.3.8 + version: 14.3.8(react@18.3.1) + react-hot-toast: + specifier: ^2.5.2 + version: 2.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-hotkeys-hook: + specifier: ^4.6.1 + version: 4.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: + specifier: ^7.3.0 + version: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router-dom: + specifier: ^7.6.2 + version: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts: + specifier: ^3.1.0 + version: 3.1.2(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(redux@5.0.1) + sonner: + specifier: ^2.0.1 + version: 2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + stripe: + specifier: ^18.3.0 + version: 18.5.0(@types/node@22.18.0) + svix-react: + specifier: ^1.13.3 + version: 1.13.6(@babel/core@7.28.3)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svix@1.75.0) + swr: + specifier: ^2.3.3 + version: 2.3.6(react@18.3.1) + tailwind-merge: + specifier: ^3.0.2 + version: 3.3.1 + tailwindcss: + specifier: ^4.0.13 + version: 4.1.12 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@4.1.12) + vaul: + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + zod: + specifier: ^3.25.23 + version: 3.25.76 + devDependencies: + '@eslint/js': + specifier: ^9.21.0 + version: 9.34.0 + '@types/lodash': + specifier: ^4.17.16 + version: 4.17.20 + '@types/node': + specifier: ^22.13.10 + version: 22.18.0 + '@types/prismjs': + specifier: ^1.26.5 + version: 1.26.5 + '@types/react': + specifier: ^18.3.18 + version: 18.3.24 + '@types/react-dom': + specifier: ^18.3.5 + version: 18.3.7(@types/react@18.3.24) + '@types/react-router-dom': + specifier: ^5.3.3 + version: 5.3.3 + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.7.0(vite@6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5)) + eslint: + specifier: ^9.21.0 + version: 9.34.0(jiti@2.5.1) + eslint-plugin-react-hooks: + specifier: ^5.1.0 + version: 5.2.0(eslint@9.34.0(jiti@2.5.1)) + eslint-plugin-react-refresh: + specifier: ^0.4.19 + version: 0.4.20(eslint@9.34.0(jiti@2.5.1)) + globals: + specifier: ^15.15.0 + version: 15.15.0 + typescript: + specifier: ~5.7.2 + version: 5.7.3 + typescript-eslint: + specifier: ^8.24.1 + version: 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + vite: + specifier: ^6.2.0 + version: 6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + +packages: + + '@ai-sdk/anthropic@1.2.12': + resolution: {integrity: sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.0.0 + + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider@1.1.3': + resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@anthropic-ai/sdk@0.32.1': + resolution: {integrity: sha512-U9JwTrDvdQ9iWuABVsMLj8nJVwAyQz6QXvgLsVhryhCEPkLsbcP/MXxm+jYcAwLoV8ESbaTTjnD4kuAFa+Hyjg==} + + '@axiomhq/js@1.3.1': + resolution: {integrity: sha512-Ytf5V3wKz8FKNiqJxnqZmUhjgJ7TItKUoyHVNE/H2V9dN1ozD6NNnsueenOjKdA48cm2sGRyP432nworst18aA==} + engines: {node: '>=16'} + + '@axiomhq/pino@1.3.1': + resolution: {integrity: sha512-zf6p2rU+b5XAk8Nj6EdjqdXTCuWQlf+C8UGdumD9xbtDWBYvk/EYkxXKMqK8mo2Gp+Fi+p5eHgjuOtbeop2XBQ==} + engines: {node: '>=16'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.0': + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.3': + resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.3': + resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.27.1': + resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.3': + resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.3': + resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-proposal-private-property-in-object@7.21.11': + resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==} + engines: {node: '>=6.9.0'} + deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead. + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.28.3': + resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.3': + resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@better-auth/stripe@1.3.7': + resolution: {integrity: sha512-hmcWJu9RB1IOf+Mb8izQyLOAkRFKes8O0j/VoIFwLR0l2zWTknXKzkLUjadFPeKXHtV3i2n2++prjuVev6F6zw==} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + '@better-auth/utils@0.2.6': + resolution: {integrity: sha512-3y/vaL5Ox33dBwgJ6ub3OPkVqr6B5xL2kgxNHG8eHZuryLyG/4JSPGqjbdRSgjuy9kALUZYDFl+ORIAxlWMSuA==} + + '@better-fetch/fetch@1.1.18': + resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} + + '@browserbasehq/sdk@2.6.0': + resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} + + '@clerk/backend@2.10.1': + resolution: {integrity: sha512-6UV9OblBhApQolpTBYg9jRHT5LGEwbAkC10nQoycWs6+3r2LiBE5DAGFp3R0NX0VzqJt4Es3d3e691V/waNAPw==} + engines: {node: '>=18.17.0'} + + '@clerk/clerk-react@5.45.0': + resolution: {integrity: sha512-TgEEUDToeBZlEi+FuzbibAIed7+UazLWmzZZ0Z1ztvI7U8iGYaGrC8+VIE4PosMjrWUy11aW4C5nCwQdq2o5hw==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + + '@clerk/express@1.7.26': + resolution: {integrity: sha512-qVNg5hHUTZsN0Cs5s6InQllz3P1LLCkDA5DOCGuvp3EJ7+tFil1yUTmpn3QyXg3j1IlP8QMGPeUiYev7Mt+ivA==} + engines: {node: '>=18.17.0'} + peerDependencies: + express: ^4.17.0 || ^5.0.0 + + '@clerk/shared@3.23.0': + resolution: {integrity: sha512-HzUzkpjoCMGq3RlpFlWmWYUzD7b2Arqqz5EgPJ5+FsQhCLbq1kHqoTQByTJDX0Ahf8Pvp9E+fbkovaMzBVbuow==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@clerk/types@4.83.0': + resolution: {integrity: sha512-WEILYgTGkwdB7OUoxHPrdHe/UAUPYvwutJxrwPyHfTejBmRD0RcKmB8VwTqwOxlfQgcKVDfLQ9jLW8m7NeJHIA==} + engines: {node: '>=18.17.0'} + + '@clickhouse/client-common@1.12.1': + resolution: {integrity: sha512-ccw1N6hB4+MyaAHIaWBwGZ6O2GgMlO99FlMj0B0UEGfjxM9v5dYVYql6FpP19rMwrVAroYs/IgX2vyZEBvzQLg==} + + '@clickhouse/client@1.12.1': + resolution: {integrity: sha512-7ORY85rphRazqHzImNXMrh4vsaPrpetFoTWpZYueCO2bbO6PXYDXp/GQ4DgxnGIqbWB/Di1Ai+Xuwq2o7DJ36A==} + engines: {node: '>=16'} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@date-fns/tz@1.4.1': + resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} + + '@date-fns/utc@2.1.1': + resolution: {integrity: sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==} + + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + + '@emotion/is-prop-valid@0.7.3': + resolution: {integrity: sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==} + + '@emotion/memoize@0.7.1': + resolution: {integrity: sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==} + + '@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/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.25.9': + resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.9': + resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@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.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.9': + resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.9': + resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.9': + resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.9': + resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.9': + resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.9': + resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@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.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.9': + resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.9': + resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.9': + resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.9': + resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.9': + resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.9': + resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.9': + resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.9': + resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.9': + resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.25.9': + resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.9': + resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.25.9': + resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@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.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.9': + resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.9': + resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.9': + resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.9': + resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.9': + resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.9': + resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.34.0': + resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@fortawesome/fontawesome-common-types@6.7.2': + resolution: {integrity: sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==} + engines: {node: '>=6'} + + '@fortawesome/fontawesome-common-types@7.0.0': + resolution: {integrity: sha512-PGMrIYXLGA5K8RWy8zwBkd4vFi4z7ubxtet6Yn13Plf6krRTwPbdlCwlcfmoX0R7B4Z643QvrtHmdQ5fNtfFCg==} + engines: {node: '>=6'} + + '@fortawesome/fontawesome-svg-core@7.0.0': + resolution: {integrity: sha512-obBEF+zd98r/KtKVW6A+8UGWeaOoyMpl6Q9P3FzHsOnsg742aXsl8v+H/zp09qSSu/a/Hxe9LNKzbBaQq1CEbA==} + engines: {node: '>=6'} + + '@fortawesome/free-brands-svg-icons@6.7.2': + resolution: {integrity: sha512-zu0evbcRTgjKfrr77/2XX+bU+kuGfjm0LbajJHVIgBWNIDzrhpRxiCPNT8DW5AdmSsq7Mcf9D1bH0aSeSUSM+Q==} + engines: {node: '>=6'} + + '@fortawesome/react-fontawesome@0.2.6': + resolution: {integrity: sha512-mtBFIi1UsYQo7rYonYFkjgYKGoL8T+fEH6NGUpvuqtY3ytMsAoDaPo5rk25KuMtKDipY4bGYM/CkmCHA1N3FUg==} + peerDependencies: + '@fortawesome/fontawesome-svg-core': ~1 || ~6 || ~7 + react: ^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@grpc/grpc-js@1.13.4': + resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + + '@heroicons/react@2.2.0': + resolution: {integrity: sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==} + peerDependencies: + react: '>= 16 || ^19.0.0-rc' + + '@hexagon/base64@1.1.28': + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@hyperbrowser/sdk@0.54.0': + resolution: {integrity: sha512-QmbwMG6niqInlS0FfUWWzvv+DfyINHGnF8i/0oNuyyuiIdqSXnp+UG/74Ci+yz8QuyezmuKzBSp6dBXlVG0Glw==} + + '@hyperdx/instrumentation-exception@0.1.0': + resolution: {integrity: sha512-Jgk7JY5J07Mq9fgXApGVhSkS4+WdzzRcWLReAZhxgo46KShxE6w614mFqUSnuo+z6ghlehsy4ForViUfxrFyew==} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@hyperdx/instrumentation-sentry-node@0.1.0': + resolution: {integrity: sha512-n8d/K/8M2owL2w4FNfV+lSVW6yoznEj5SdRCysV/ZIfyrZwpijiiSn7gkRcrOfKHmrxrupyp7DVg5L19cGuH6A==} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@hyperdx/node-opentelemetry@0.8.2': + resolution: {integrity: sha512-ryL8nqgyqKcYQ6KRTHiwPOI540inxo0yPHoy+tIDkJrqpOG+K0GyFu99CGg38dx3ZgT8dPlxHYzhnFLHHEEpsw==} + hasBin: true + + '@img/sharp-darwin-arm64@0.34.3': + resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.3': + resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.0': + resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.0': + resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.0': + resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.0': + resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.0': + resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.0': + resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.0': + resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.3': + resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.3': + resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.3': + resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.3': + resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.3': + resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.3': + resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.3': + resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.3': + resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.3': + resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.3': + resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.3': + resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@inquirer/checkbox@4.2.2': + resolution: {integrity: sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.16': + resolution: {integrity: sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.2.0': + resolution: {integrity: sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.18': + resolution: {integrity: sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.18': + resolution: {integrity: sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.1': + resolution: {integrity: sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.13': + resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} + engines: {node: '>=18'} + + '@inquirer/input@4.2.2': + resolution: {integrity: sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.18': + resolution: {integrity: sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.18': + resolution: {integrity: sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.8.4': + resolution: {integrity: sha512-MuxVZ1en1g5oGamXV3DWP89GEkdD54alcfhHd7InUW5BifAdKQEK9SLFa/5hlWbvuhMPlobF0WAx7Okq988Jxg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.6': + resolution: {integrity: sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.1.1': + resolution: {integrity: sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.3.2': + resolution: {integrity: sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.8': + resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@ioredis/commands@1.3.1': + resolution: {integrity: sha512-bYtU8avhGIcje3IhvF9aSjsa5URMZBHnwKtOvXsT4sfYy9gppW11gLPT/9oNqlJZD47yPKveQFTAFWpHjKvUoQ==} + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.30': + resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + + '@levischuck/tiny-cbor@0.2.11': + resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} + + '@logtail/core@0.5.4': + resolution: {integrity: sha512-UlbxvUg5MDroqk6gEhtfKuuBShrvD/VaXl3T3jGU1U6I19Y1OwfB7e681LvH7RbsCAnOJFziM/7rMQib/CJTdg==} + + '@logtail/node@0.5.5': + resolution: {integrity: sha512-hSZDjl2wFRxe/+pxSlYNSpm0V3a2ZsiWqBifvtCBHTriwrNkd0LjQtedRlrjoBBumVxIMGdDOCPayAIbcux5YQ==} + + '@logtail/tools@0.5.4': + resolution: {integrity: sha512-S0PtfWOVVgvb36q8mLksMw910mO2TahDaqIYnUM/ZEYX+AwqKRGov4zt/X33zHRzCFVpSKoZFv7bFs8TBie/pQ==} + + '@logtail/types@0.5.3': + resolution: {integrity: sha512-5uKf1xQ7wRqSW0lYIwDpXdXcFHxPKH5EM22MoEjwTpd8suymOYuEdx9qCCHCYey4uOhPq7x6OkZf86bTO83JsA==} + + '@msgpack/msgpack@2.8.0': + resolution: {integrity: sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==} + engines: {node: '>= 10'} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@next/env@15.5.2': + resolution: {integrity: sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==} + + '@next/swc-darwin-arm64@15.5.2': + resolution: {integrity: sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.2': + resolution: {integrity: sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.2': + resolution: {integrity: sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.5.2': + resolution: {integrity: sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.5.2': + resolution: {integrity: sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.5.2': + resolution: {integrity: sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.5.2': + resolution: {integrity: sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.2': + resolution: {integrity: sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@noble/ciphers@0.6.0': + resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@opentelemetry/api-logs@0.202.0': + resolution: {integrity: sha512-fTBjMqKCfotFWfLzaKyhjLvyEyq5vDKTTFfBmx21btv3gvy8Lq6N5Dh2OzqeuN4DjtpSvNT1uNVfg08eD2Rfxw==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api-logs@0.51.1': + resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} + engines: {node: '>=14'} + + '@opentelemetry/api-logs@0.57.2': + resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/auto-instrumentations-node@0.56.1': + resolution: {integrity: sha512-4cK0+unfkXRRbQQg2r9K3ki8JlE0j9Iw8+4DZEkChShAnmviiE+/JMgHGvK+VVcLrSlgV6BBHv4+ZTLukQwhkA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.4.1 + + '@opentelemetry/auto-instrumentations-node@0.60.1': + resolution: {integrity: sha512-oMBVXiun0qWhj693Y24Ie+75q45YXHRFeH9vX/XBWKRNJIM/02ufjmNvmOdoHY0EPxU9rBmWCW82Uidf54iSPA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.4.1 + '@opentelemetry/core': ^2.0.0 + + '@opentelemetry/context-async-hooks@1.30.1': + resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/context-async-hooks@2.0.1': + resolution: {integrity: sha512-XuY23lSI3d4PEqKA+7SLtAgwqIfc6E/E9eAQWLN1vlpC53ybO3o6jW4BsXo1xvz9lYyyWItfQDDLzezER01mCw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@1.30.1': + resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.0.1': + resolution: {integrity: sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-logs-otlp-grpc@0.202.0': + resolution: {integrity: sha512-Y84L8Yja/A2qjGEzC/To0yrMUXHrtwJzHtZ2za1/ulZplRe5QFsLNyHixIS42ZYUKuNyWMDgOFhnN2Pz5uThtg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-grpc@0.57.2': + resolution: {integrity: sha512-eovEy10n3umjKJl2Ey6TLzikPE+W4cUQ4gCwgGP1RqzTGtgDra0WjIqdy29ohiUKfvmbiL3MndZww58xfIvyFw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-http@0.202.0': + resolution: {integrity: sha512-mJWLkmoG+3r+SsYQC+sbWoy1rjowJhMhFvFULeIPTxSI+EZzKPya0+NZ3+vhhgx2UTybGQlye3FBtCH3o6Rejg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-http@0.57.2': + resolution: {integrity: sha512-0rygmvLcehBRp56NQVLSleJ5ITTduq/QfU7obOkyWgPpFHulwpw2LYTqNIz5TczKZuy5YY+5D3SDnXZL1tXImg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-proto@0.202.0': + resolution: {integrity: sha512-qYwbmNWPkP7AbzX8o4DRu5bb/a0TWYNcpZc1NEAOhuV7pgBpAUPEClxRWPN94ulIia+PfQjzFGMaRwmLGmNP6g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-logs-otlp-proto@0.57.2': + resolution: {integrity: sha512-ta0ithCin0F8lu9eOf4lEz9YAScecezCHkMMyDkvd9S7AnZNX5ikUmC5EQOQADU+oCcgo/qkQIaKcZvQ0TYKDw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-grpc@0.202.0': + resolution: {integrity: sha512-/dq/rf4KCkTYoP+NyPXTE+5wjvfhAHSqK62vRsJ/IalG61VPQvwaL18yWcavbI+44ImQwtMeZxfIJSox7oQL0w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-grpc@0.57.2': + resolution: {integrity: sha512-r70B8yKR41F0EC443b5CGB4rUaOMm99I5N75QQt6sHKxYDzSEc6gm48Diz1CI1biwa5tDPznpylTrywO/pT7qw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-http@0.202.0': + resolution: {integrity: sha512-ooYcrf/m9ZuVGpQnER7WRH+JZbDPD389HG7VS/EnvIEF5WpNYEqf+NdmtaAcs51d81QrytTYAubc5bVWi//28w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-http@0.57.2': + resolution: {integrity: sha512-ttb9+4iKw04IMubjm3t0EZsYRNWr3kg44uUuzfo9CaccYlOh8cDooe4QObDUkvx9d5qQUrbEckhrWKfJnKhemA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-proto@0.202.0': + resolution: {integrity: sha512-X0RpPpPjyCAmIq9tySZm0Hk3Ltw8KWsqeNq5I7gS9AR9RzbVHb/l+eiMI1CqSRvW9R47HXcUu/epmEzY8ebFAg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-metrics-otlp-proto@0.57.2': + resolution: {integrity: sha512-HX068Q2eNs38uf7RIkNN9Hl4Ynl+3lP0++KELkXMCpsCbFO03+0XNNZ1SkwxPlP9jrhQahsMPMkzNXpq3fKsnw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.202.0': + resolution: {integrity: sha512-6RvQqZHAPFiwL1OKRJe4ta6SgJx/g8or41B+OovVVEie3HeCDhDGL9S1VJNkBozUz6wTY8a47fQwdMrCOUdMhQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.57.2': + resolution: {integrity: sha512-VqIqXnuxWMWE/1NatAGtB1PvsQipwxDcdG4RwA/umdBcW3/iOHp0uejvFHTRN2O78ZPged87ErJajyUBPUhlDQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.202.0': + resolution: {integrity: sha512-d5wLdbNA3ahpSeD0I34vbDFMTh4vPsXemH0bKDXLeCVULCAjOJXuZmEiuRammiDgVvvX7CAb/IGLDz8d2QHvoA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.57.2': + resolution: {integrity: sha512-gHU1vA3JnHbNxEXg5iysqCWxN9j83d7/epTYBZflqQnTyCC4N7yZXn/dMM+bEmyhQPGjhCkNZLx4vZuChH1PYw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-http@0.202.0': + resolution: {integrity: sha512-/hKE8DaFCJuaQqE1IxpgkcjOolUIwgi3TgHElPVKGdGRBSmJMTmN/cr6vWa55pCJIXPyhKvcMrbrya7DZ3VmzA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-http@0.57.2': + resolution: {integrity: sha512-sB/gkSYFu+0w2dVQ0PWY9fAMl172PKMZ/JrHkkW8dmjCL0CYkmXeE+ssqIL/yBUTPOvpLIpenX5T9RwXRBW/3g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.202.0': + resolution: {integrity: sha512-z3vzdMclCETGIn8uUBgpz7w651ftCiH2qh3cewhBk+rF0EYPNQ3mJvyxktLnKIBZ/ci0zUknAzzYC7LIIZmggQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.57.2': + resolution: {integrity: sha512-awDdNRMIwDvUtoRYxRhja5QYH6+McBLtoz1q9BeEsskhZcrGmH/V1fWpGx8n+Rc+542e8pJA6y+aullbIzQmlw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-zipkin@1.30.1': + resolution: {integrity: sha512-6S2QIMJahIquvFaaxmcwpvQQRD/YFaMTNoIxrfPIPOeITN+a8lfEcPDxNxn8JDAaxkg+4EnXhz8upVDYenoQjA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/exporter-zipkin@2.0.1': + resolution: {integrity: sha512-a9eeyHIipfdxzCfc2XPrE+/TI3wmrZUDFtG2RRXHSbZZULAny7SyybSvaDvS77a7iib5MPiAvluwVvbGTsHxsw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-amqplib@0.46.1': + resolution: {integrity: sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-amqplib@0.49.0': + resolution: {integrity: sha512-OCGkE+1JoUN+gOzs3u0GSa7GV//KX6NMKzaPchedae7ZwFVyyBQ8VECJngHgW3k/FLABFnq9Oiym2WZGiWugVQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-aws-lambda@0.50.3': + resolution: {integrity: sha512-kotm/mRvSWUauudxcylc5YCDei+G/r+jnOH6q5S99aPLQ/Ms8D2yonMIxEJUILIPlthEmwLYxkw3ualWzMjm/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-aws-lambda@0.53.1': + resolution: {integrity: sha512-canWSwigcvxq2mIrrxAdj6Cf21ysoxpRoe1T4UGINlKuyvrFiYAKLVZlPqVgP2Dwh9w/5+6yGBouoDhO/dQ0Kg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-aws-sdk@0.49.1': + resolution: {integrity: sha512-Vbj4BYeV/1K4Pbbfk+gQ8gwYL0w+tBeUwG88cOxnF7CLPO1XnskGV8Q3Gzut2Ah/6Dg17dBtlzEqL3UiFP2Z6A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-aws-sdk@0.54.0': + resolution: {integrity: sha512-4XnXfpACX8fpOnt/D8d/1AFg3uOwBTG9TopQBuikDZJYUrLUSdT7UiotCFqAM/Z6hQJh72Jy3591C/OrmKct7A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-bunyan@0.45.1': + resolution: {integrity: sha512-T9POV9ccS41UjpsjLrJ4i0m8LfplBiN3dMeH9XZ2btiDrjoaWtDrst6tNb1avetBjkeshOuBp1EWKP22EVSr0g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-bunyan@0.48.0': + resolution: {integrity: sha512-Q6ay5CXIKuyejadPoLboz+jKumB3Zuxyk35ycFh9vfIeww3+mNRyMVj6KxHRS0Imbv9zhNbP3uyrUpvEMMyHuw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-cassandra-driver@0.45.1': + resolution: {integrity: sha512-RqnP0rK2hcKK1AKcmYvedLiL6G5TvFGiSUt2vI9wN0cCBdTt9Y9+wxxY19KoGxq7e9T/aHow6P5SUhCVI1sHvQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-cassandra-driver@0.48.0': + resolution: {integrity: sha512-0dcX8Kx0S6ZAOknrbA+BBh1j5lg5F20W18m5VYoGUxkuLIUbWkQA3uaqeTfqbOwmnBmb1upDPUWPR+g5N12B4Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.43.1': + resolution: {integrity: sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.46.0': + resolution: {integrity: sha512-YNq/7M1JXnWRkpKPC9dbYZA36cg547gY0p1bijW7vuZJ9t5f3alo6w8TWtZwV/hOFtBGHDXVhKVfp2Mh6zVHjQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-cucumber@0.14.1': + resolution: {integrity: sha512-ybO+tmH85pDO0ywTskmrMtZcccKyQr7Eb7wHy1keR2HFfx46SzZbjHo1AuGAX//Hook3gjM7+w211gJ2bwKe1Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-cucumber@0.17.1': + resolution: {integrity: sha512-s/18qxBzjEWcJ8BIPJx5oP6GCB7huOaQai3lpJRXe9AcRTOUn6Jzp6oOeY52GE2uvTebo9/3CurM8So2J96fag==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-dataloader@0.16.1': + resolution: {integrity: sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-dataloader@0.19.0': + resolution: {integrity: sha512-zIVRnRs3zDZCqStQcpIdRx3Dz9WXFSVj9qimqI7CRuKao9qnrZYUVQHvvVlLZX3JAg+nDC6JRS95zvbq50hj4A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-dns@0.43.1': + resolution: {integrity: sha512-e/tMZYU1nc+k+J3259CQtqVZIPsPRSLNoAQbGEmSKrjLEY/KJSbpBZ17lu4dFVBzqoF1cZYIZxn9WPQxy4V9ng==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-dns@0.46.0': + resolution: {integrity: sha512-m8u72x2fSIjhP1ITJX9Ims3eR4Qn8ze+QWy9NHYO01JlmiMamoc9TfIOd4dyOtxVja4tjnkWceKQdlEH9F9BoA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.47.1': + resolution: {integrity: sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.51.1': + resolution: {integrity: sha512-cKmzev7RolYGedQ82hVUoH+74BP4E0xmhUCEagOxmW3aKilXYx01KwsNN6wnx3IXR7u2nlYugQsEsLLA4d829A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.44.2': + resolution: {integrity: sha512-arSp97Y4D2NWogoXRb8CzFK3W2ooVdvqRRtQDljFt9uC3zI6OuShgey6CVFC0JxT1iGjkAr1r4PDz23mWrFULQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.47.1': + resolution: {integrity: sha512-zTVFju7I67wA7Y2ZJ9Gb5u3zqiXIx5Zcaa2KSapal6VZ6gS8OkoC3t35M/6iazfBIPd9e1uCsonbm8jz8v+x1A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fs@0.19.1': + resolution: {integrity: sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fs@0.22.0': + resolution: {integrity: sha512-ktQVFD6pd8eAIW6t2DtDuXj2lxq+wnQ8WUkJLNZzl3rEE2TZEiHg7wIkWVoxl4Cz4pJ2YZJbdU2fHAizuDebDw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-generic-pool@0.43.1': + resolution: {integrity: sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-generic-pool@0.46.1': + resolution: {integrity: sha512-4rH/7nqxY2rnAodfP5nHMpwC6aFJUGRq9PZ44L5nUf+dxNyeuSPkuvxUr1tOf+qduqkhs2sZDP8/53n9/YmNzQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.47.1': + resolution: {integrity: sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.50.0': + resolution: {integrity: sha512-Nn3vBS5T0Dv4+9WF1dGR0Lgsxuz6ztQmTsxoHvesm6YAAXiHffnwsxBEJUKEJcjxfXzjO1SVuLDkv1bAeQ3NFw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-grpc@0.202.0': + resolution: {integrity: sha512-dWvefHNAyAfaHVmxQ/ySLQSI2hGKLgK1sBtvae4w9xruqU08bBMtvmVeGMA/5whfiUDU8ftp1/84U4Zoe5N56A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-grpc@0.57.2': + resolution: {integrity: sha512-TR6YQA67cLSZzdxbf2SrbADJy2Y8eBW1+9mF15P0VK2MYcpdoUSmQTF1oMkBwa3B9NwqDFA2fq7wYTTutFQqaQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.45.2': + resolution: {integrity: sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.49.0': + resolution: {integrity: sha512-d4BcCjbW7Pfg4FpbAAF0cK/ue3dN02WMw0uO2G792KzDjxj05MtZm3eBTz672j3ejV9hM0HvPPhUHUsIC0H6Gw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.202.0': + resolution: {integrity: sha512-oX+jyY2KBg4/nVH3vZhSWDbhywkHgE0fq3YinhUBx0jv+YUWC2UKA7qLkxr/CSzfKsFi/Km0NKV+llH17yYGKw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.57.2': + resolution: {integrity: sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.47.1': + resolution: {integrity: sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.50.1': + resolution: {integrity: sha512-HKrWKOM23qNwqNjWfzkw7mePversmcH5ac6T1dUdiRyJVYaLr4qfydyYkgKIGWHOF2TKvQGobXo3CjvxABQWVw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-kafkajs@0.11.0': + resolution: {integrity: sha512-+i9VqVEPNObB1tkwcLV6zAafnve72h2Iwo48E11M/kVXMNXlgGhiYckYCmzba8c2u5XD/V98XZDrCIyO8CLCNA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-kafkajs@0.7.1': + resolution: {integrity: sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-knex@0.44.1': + resolution: {integrity: sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-knex@0.47.0': + resolution: {integrity: sha512-OjqjnzXD5+FXVGkOznbRAz9yByb4UWzIUhXjuHvOQ50IUY8mv3rM2Gj6Ar7m5JsENiS5DtAy2Vfwk4e9zNC0ng==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.47.1': + resolution: {integrity: sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.50.2': + resolution: {integrity: sha512-+XTWg7a+u6lu4bm6HN0ItirTF0bBFUJlayqe+iVE87Cwpha7W47DV0aoNL8AzGPczlF1UQVVO0kvcfI8bnrkHw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-lru-memoizer@0.44.1': + resolution: {integrity: sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-lru-memoizer@0.47.0': + resolution: {integrity: sha512-UJ2UlCAIF+N4zNkiHdMr4O0caN0K6YboAso3/zaFdG1QiPR2zqZcbWAGFBikZ9HSByU+NwbxTXDzlpkcDZIqWg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-memcached@0.43.1': + resolution: {integrity: sha512-rK5YWC22gmsLp2aEbaPk5F+9r6BFFZuc9GTnW/ErrWpz2XNHUgeFInoPDg4t+Trs8OttIfn8XwkfFkSKqhxanw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-memcached@0.46.0': + resolution: {integrity: sha512-FFDcOVJUxZQqbg57gVskZGXRfEsZXwOvCaPv6/qIZRw5glLXPTulpnfG/s8NAltsj2buXSvS4eKFo+0HKH0apw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.52.0': + resolution: {integrity: sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.55.1': + resolution: {integrity: sha512-Wb13YixWm8nB27ZSQW3h070UWkivoh6bjeyDUY6lLimSUulALr+YHBn0t71U1aTcUeaZv3IBNaPRimFXhz6gBA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.46.1': + resolution: {integrity: sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.49.0': + resolution: {integrity: sha512-nF+43QFe8IoW20TmTJZdxZhnVZGEglODUvzAo3fRmaBFAkwUXRGzRgABS255PCjIbScEaRRDCXc6EAsSkwRNPg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.45.2': + resolution: {integrity: sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.48.1': + resolution: {integrity: sha512-deJbaCC595WYhqDoXR5UfRx0bPmYyr1WhiFv6BjHRndMEUStHYYnsF6+mjpa3f0nebVj8Nv8eDcke4iTs7AfBw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.45.1': + resolution: {integrity: sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.48.1': + resolution: {integrity: sha512-wP5+wIfQXmnKY4riKlx+1PiHpMSkG8Zu2cMXkiX6u84HqAWY62+N/Wv3fx9FU+/DU+fz3BdQQbo0EpVWziqqvg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.44.1': + resolution: {integrity: sha512-4TXaqJK27QXoMqrt4+hcQ6rKFd8B6V4JfrTJKnqBmWR1cbaqd/uwyl9yxhNH1JEkyo8GaBfdpBC4ZE4FuUhPmg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.48.1': + resolution: {integrity: sha512-rH0IUQRf9wjxEkiPfltM17DVqgSe/rgeIlg1CKRDAhuxWkbXlcHwdOnBfngGKhJNGOlGUo6HzZesavPAlmm3Fw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-net@0.43.1': + resolution: {integrity: sha512-TaMqP6tVx9/SxlY81dHlSyP5bWJIKq+K7vKfk4naB/LX4LBePPY3++1s0edpzH+RfwN+tEGVW9zTb9ci0up/lQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-net@0.46.1': + resolution: {integrity: sha512-r7Buqem+odrTTPlWfT7EqS24QnDAL4U+c4e38RzcRtdZF00Z34oqEpge7TZcQLo0vEASWbHQ/WjWNR7ZYKFKBA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-oracledb@0.28.0': + resolution: {integrity: sha512-VObbQRd3g8nDLLOeGjm5l6TnB9dtEaJoedLfLwMGrlD6lkai+hdfalYh6FOF5dce+dJouZdW6NUUAaBj4f4KcA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.51.1': + resolution: {integrity: sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.54.1': + resolution: {integrity: sha512-vy2WbrB76iRpJccGCC7HNICVm+f5zg20dYBuB4r1X7TaXYsi1txrjvemOEH0h9lTpm2vVFoheHsVoClxleXvvQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pino@0.46.1': + resolution: {integrity: sha512-HB8gD/9CNAKlTV+mdZehnFC4tLUtQ7e+729oGq88e4WipxzZxmMYuRwZ2vzOA9/APtq+MRkERJ9PcoDqSIjZ+g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pino@0.49.1': + resolution: {integrity: sha512-QTD4HQA0fhtu4Hvw9iIlnroDiQju8nmEmnTQrS3xrb6g9AsdBMYnXXyzNBlLpSvtqwfPvgAxfY4DY1IYtnNwDg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.46.1': + resolution: {integrity: sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.49.0': + resolution: {integrity: sha512-i+Wsl7M2LXEDA2yXouNJ3fttSzzb5AhlehvSBVRIFuinY51XrrKSH66biO0eox+pYQMwAlPxJ778XcMQffN78A==} + engines: {node: ^18.19.0 || >=20.6.0} + deprecated: Use "@opentelemetry/instrumentation-redis", which (as of v0.50.0) includes support for instrumenting redis v4. + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis@0.46.1': + resolution: {integrity: sha512-AN7OvlGlXmlvsgbLHs6dS1bggp6Fcki+GxgYZdSrb/DB692TyfjR7sVILaCe0crnP66aJuXsg9cge3hptHs9UA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis@0.49.1': + resolution: {integrity: sha512-Ds5Ke9qE9kTlDThqLSJJntkIvuMQCBPiFKwHntocb/3q/9q5D47BNwawO5Mj9sVMV6zkld5M5Pb9Av39iieuOg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-restify@0.45.1': + resolution: {integrity: sha512-Zd6Go9iEa+0zcoA2vDka9r/plYKaT3BhD3ESIy4JNIzFWXeQBGbH3zZxQIsz0jbNTMEtonlymU7eTLeaGWiApA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-restify@0.48.2': + resolution: {integrity: sha512-V+Bac9zMkQI7p1BsJV/jqg175nBs5aIpeh5HRHxrvlnDaH7TDlHDpax2i+sSrTHjSqxF/jeaIDDjuCW5C5WKbg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-router@0.44.1': + resolution: {integrity: sha512-l4T/S7ByjpY5TCUPeDe1GPns02/5BpR0jroSMexyH3ZnXJt9PtYqx1IKAlOjaFEGEOQF2tGDsMi4PY5l+fSniQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-router@0.47.0': + resolution: {integrity: sha512-U0zA1LTDqtTWyd5e4SdoqQA/8QUOhc4LDv9U7b+8FMFTty95OF84apUdatl09Dzc51XeWPWIV7VutmSCd/zsUg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-runtime-node@0.12.2': + resolution: {integrity: sha512-HNBW1rJiHDBTHQlh5oH1IAcV8O5VR7/L5BBOfGAMpGno3Jq9cNqTh96uUp0qBXBuxD8Yl1eoI5N+B5TdmjLteQ==} + engines: {node: '>=17.4.0'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-runtime-node@0.16.0': + resolution: {integrity: sha512-Q/GB9LsKLrRCEIPLAQTDQvydnLmLXBSRkYkWzwKzY/LCkOs+Cl8YiJG08p6D4CaJ6lvP0iG4kwPHk1ydNbdehg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-socket.io@0.46.1': + resolution: {integrity: sha512-9AsCVUAHOqvfe2RM/2I0DsDnx2ihw1d5jIN4+Bly1YPFTJIbk4+bXjAkr9+X6PUfhiV5urQHZkiYYPU1Q4yzPA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-socket.io@0.49.0': + resolution: {integrity: sha512-DpMtNBEcaLCcbP1WVBPCSgRiBs31igTQkal1gUm40VL/XAv5GUqRAUnvHZrQh3yPipOqzV65pdb0jJXdps/tug==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-tedious@0.18.1': + resolution: {integrity: sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-tedious@0.21.1': + resolution: {integrity: sha512-RDQyesAIJ3JHw8w5vthJjrzI3jTm/l6aeEQzIZ6cMG5hcW7ySPSyaxWbtmmp0FHPkdoGwc3Li+UQfV77+xLA1Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-undici@0.10.1': + resolution: {integrity: sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.7.0 + + '@opentelemetry/instrumentation-undici@0.13.2': + resolution: {integrity: sha512-rO8CNuHnVN13rKrXayvtuXMXwPQkem3H0r/UWZhyNGQeHlqlQgpgtu5mR9dzSuv9kLRrxZb/WjK+sGOP5kwetg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.7.0 + + '@opentelemetry/instrumentation-winston@0.44.1': + resolution: {integrity: sha512-iexblTsT3fP0hHUz/M1mWr+Ylg3bsYN2En/jvKXZtboW3Qkvt17HrQJYTF9leVIkXAfN97QxAcTE99YGbQW7vQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-winston@0.47.0': + resolution: {integrity: sha512-r+GqnZU/aFldQyB5QdOlxsMlH9KZ4+zJfnYplz3lbC9f9ozAIlVAeoshvWTtbv7Oxp2NnK64EfnNP1pClaGEqA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.202.0': + resolution: {integrity: sha512-Uz3BxZWPgDwgHM2+vCKEQRh0R8WKrd/q6Tus1vThRClhlPO39Dyz7mDrOr6KuqGXAlBQ1e5Tnymzri4RMZNaWA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.51.1': + resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.57.2': + resolution: {integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.202.0': + resolution: {integrity: sha512-nMEOzel+pUFYuBJg2znGmHJWbmvMbdX5/RhoKNKowguMbURhz0fwik5tUKplLcUtl8wKPL1y9zPnPxeBn65N0Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.57.2': + resolution: {integrity: sha512-XdxEzL23Urhidyebg5E6jZoaiW5ygP/mRjxLHixogbqwDy2Faduzb5N0o/Oi+XTIJu+iyxXdVORjXax+Qgfxag==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.202.0': + resolution: {integrity: sha512-yIEHVxFA5dmYif7lZbbB66qulLLhrklj6mI2X3cuGW5hYPyUErztEmbroM+6teu/XobBi9bLHid2VT4NIaRuGg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.57.2': + resolution: {integrity: sha512-USn173KTWy0saqqRB5yU9xUZ2xdgb1Rdu5IosJnm9aV4hMTuFFRTUsQxbgc24QxpCHeoKzzCSnS/JzdV0oM2iQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.202.0': + resolution: {integrity: sha512-5XO77QFzs9WkexvJQL9ksxL8oVFb/dfi9NWQSq7Sv0Efr9x3N+nb1iklP1TeVgxqJ7m1xWiC/Uv3wupiQGevMw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.57.2': + resolution: {integrity: sha512-48IIRj49gbQVK52jYsw70+Jv+JbahT8BqT2Th7C4H7RCM9d0gZ5sgNPoMpWldmfjvIsSgiGJtjfk9MeZvjhoig==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/propagation-utils@0.30.16': + resolution: {integrity: sha512-ZVQ3Z/PQ+2GQlrBfbMMMT0U7MzvYZLCPP800+ooyaBqm4hMvuQHfP028gB9/db0mwkmyEAMad9houukUVxhwcw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/propagation-utils@0.31.3': + resolution: {integrity: sha512-ZI6LKjyo+QYYZY5SO8vfoCQ9A69r1/g+pyjvtu5RSK38npINN1evEmwqbqhbg2CdcIK3a4PN6pDAJz/yC5/gAA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/propagator-b3@1.30.1': + resolution: {integrity: sha512-oATwWWDIJzybAZ4pO76ATN5N6FFbOA1otibAVlS8v90B4S1wClnhRUk7K+2CHAwN1JKYuj4jh/lpCEG5BAqFuQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-b3@2.0.1': + resolution: {integrity: sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@1.30.1': + resolution: {integrity: sha512-Pj/BfnYEKIOImirH76M4hDaBSx6HyZ2CXUqk+Kj02m6BB80c/yo4BdWkn/1gDFfU+YPY+bPR2U0DKBfdxCKwmg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@2.0.1': + resolution: {integrity: sha512-7PMdPBmGVH2eQNb/AtSJizQNgeNTfh6jQFqys6lfhd6P4r+m/nTh3gKPPpaCXVdRQ+z93vfKk+4UGty390283w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/redis-common@0.36.2': + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + + '@opentelemetry/redis-common@0.37.0': + resolution: {integrity: sha512-tJwgE6jt32bLs/9J6jhQRKU2EZnsD8qaO13aoFyXwF6s4LhpT7YFHf3Z03MqdILk6BA2BFUhoyh7k9fj9i032A==} + engines: {node: ^18.19.0 || >=20.6.0} + + '@opentelemetry/redis-common@0.38.0': + resolution: {integrity: sha512-4Wc0AWURII2cfXVVoZ6vDqK+s5n4K5IssdrlVrvGsx6OEOKdghKtJZqXAHWFiZv4nTDLH2/2fldjIHY8clMOjQ==} + engines: {node: ^18.19.0 || >=20.6.0} + + '@opentelemetry/resource-detector-alibaba-cloud@0.30.1': + resolution: {integrity: sha512-9l0FVP3F4+Z6ax27vMzkmhZdNtxAbDqEfy7rduzya3xFLaRiJSvOpw6cru6Edl5LwO+WvgNui+VzHa9ViE8wCg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-alibaba-cloud@0.31.3': + resolution: {integrity: sha512-I556LHcLVsBXEgnbPgQISP/JezDt5OfpgOaJNR1iVJl202r+K145OSSOxnH5YOc/KvrydBD0FOE03F7x0xnVTw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-aws@1.12.0': + resolution: {integrity: sha512-Cvi7ckOqiiuWlHBdA1IjS0ufr3sltex2Uws2RK6loVp4gzIJyOijsddAI6IZ5kiO8h/LgCWe8gxPmwkTKImd+Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-aws@2.3.0': + resolution: {integrity: sha512-PkD/lyXG3B3REq1Y6imBLckljkJYXavtqGYSryAeJYvGOf5Ds3doR+BCGjmKeF6ObAtI5MtpBeUStTDtGtBsWA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-azure@0.6.1': + resolution: {integrity: sha512-Djr31QCExVfWViaf9cGJnH+bUInD72p0GEfgDGgjCAztyvyji6WJvKjs6qmkpPN+Ig6KLk0ho2VgzT5Kdl4L2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-azure@0.9.0': + resolution: {integrity: sha512-5wJwAAW2vhbqIhgaRisU1y0F5mUco59F/dKgmnnnT6YNbxjrbdUZYxKF5Wl7deJoACVdL5wi/3N97GCXPEwwCQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-container@0.6.1': + resolution: {integrity: sha512-o4sLzx149DQXDmVa8pgjBDEEKOj9SuQnkSLbjUVOpQNnn10v0WNR6wLwh30mFsK26xOJ6SpqZBGKZiT7i5MjlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-container@0.7.3': + resolution: {integrity: sha512-SK+xUFw6DKYbQniaGmIFsFxAZsr8RpRSRWxKi5/ZJAoqqPnjcyGI/SeUx8zzPk4XLO084zyM4pRHgir0hRTaSQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-gcp@0.33.1': + resolution: {integrity: sha512-/aZJXI1rU6Eus04ih2vU0hxXAibXXMzH1WlDZ8bXcTJmhwmTY8cP392+6l7cWeMnTQOibBUz8UKV3nhcCBAefw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resource-detector-gcp@0.36.0': + resolution: {integrity: sha512-mWnEcg4tA+IDPrkETWo42psEsDN20dzYZSm4ZH8m8uiQALnNksVmf5C3An0GUEj5zrrxMasjSuv4zEH1gI40XQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/resources@1.30.1': + resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/resources@2.0.1': + resolution: {integrity: sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.202.0': + resolution: {integrity: sha512-pv8QiQLQzk4X909YKm0lnW4hpuQg4zHwJ4XBd5bZiXcd9urvrJNoNVKnxGHPiDVX/GiLFvr5DMYsDBQbZCypRQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.57.2': + resolution: {integrity: sha512-TXFHJ5c+BKggWbdEQ/inpgIzEmS2BGQowLE9UhsMd7YYlUfBQJ4uax0VF/B5NYigdM/75OoJGhAV3upEhK+3gg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@1.30.1': + resolution: {integrity: sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.0.1': + resolution: {integrity: sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-node@0.202.0': + resolution: {integrity: sha512-SF9vXWVd9I5CZ69mW3GfwfLI2SHgyvEqntcg0en5y8kRp5+2PPoa3Mkgj0WzFLrbSgTw4PsXn7c7H6eSdrtV0w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-node@0.57.2': + resolution: {integrity: sha512-8BaeqZyN5sTuPBtAoY+UtKwXBdqyuRKmekN5bFzAO40CgbGzAxfTpiL3PBerT7rhZ7p2nBdq7FaMv/tBQgHE4A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@1.30.1': + resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.0.1': + resolution: {integrity: sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@1.30.1': + resolution: {integrity: sha512-cBjYOINt1JxXdpw1e5MlHmFRc5fgj4GW/86vsKFxJCJ8AL4PdVtYH41gWwl4qd4uQjqEL1oJVrXkSy5cnduAnQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@2.0.1': + resolution: {integrity: sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.28.0': + resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.36.0': + resolution: {integrity: sha512-TtxJSRD8Ohxp6bKkhrm27JRHAxPczQA7idtcTOMYI+wQRRrfgqxHv1cFbCApcSnNjtXkmzFozn6jQtFrOmbjPQ==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.40.1': + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@opentelemetry/sql-common@0.41.0': + resolution: {integrity: sha512-pmzXctVbEERbqSfiAgdes9Y63xjoOyXcD7B6IXBkVb+vbM7M9U98mn33nGXxPf4dfYR0M+vhcKRZmbSJ7HfqFA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + + '@peculiar/asn1-android@2.4.0': + resolution: {integrity: sha512-YFueREq97CLslZZBI8dKzis7jMfEHSLxM+nr0Zdx1POiXFLjqqwoY5s0F1UimdBiEw/iKlHey2m56MRDv7Jtyg==} + + '@peculiar/asn1-ecc@2.4.0': + resolution: {integrity: sha512-fJiYUBCJBDkjh347zZe5H81BdJ0+OGIg0X9z06v8xXUoql3MFeENUX0JsjCaVaU9A0L85PefLPGYkIoGpTnXLQ==} + + '@peculiar/asn1-rsa@2.4.0': + resolution: {integrity: sha512-6PP75voaEnOSlWR9sD25iCQyLgFZHXbmxvUfnnDcfL6Zh5h2iHW38+bve4LfH7a60x7fkhZZNmiYqAlAff9Img==} + + '@peculiar/asn1-schema@2.4.0': + resolution: {integrity: sha512-umbembjIWOrPSOzEGG5vxFLkeM8kzIhLkgigtsOrfLKnuzxWxejAcUX+q/SoZCdemlODOcr5WiYa7+dIEzBXZQ==} + + '@peculiar/asn1-x509@2.4.0': + resolution: {integrity: sha512-F7mIZY2Eao2TaoVqigGMLv+NDdpwuBKU1fucHPONfzaBS4JXXCNCmfO0Z3dsy7JzKGqtDcYC1mr9JjaZQZNiuw==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@posthog/core@1.0.2': + resolution: {integrity: sha512-hWk3rUtJl2crQK0WNmwg13n82hnTwB99BT99/XI5gZSvIlYZ1TPmMZE8H2dhJJ98J/rm9vYJ/UXNzw3RV5HTpQ==} + + '@prisma/instrumentation@6.11.1': + resolution: {integrity: sha512-mrZOev24EDhnefmnZX7WVVT7v+r9LttPRqf54ONvj6re4XMF7wFTpK2tLJi4XHB7fFp/6xhYbgRel8YV7gQiyA==} + peerDependencies: + '@opentelemetry/api': ^1.8 + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@puppeteer/browsers@2.10.8': + resolution: {integrity: sha512-f02QYEnBDE0p8cteNoPYHHjbDuwyfbe4cCIVlNi8/MRicIxFW4w4CfgU0LNgWEID6s06P+hRJ1qjpBLMhPRCiQ==} + engines: {node: '>=18'} + hasBin: true + + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.3': + resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.2.16': + resolution: {integrity: sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.10': + resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.2.6': + resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.2.6': + resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@react-email/body@0.0.11': + resolution: {integrity: sha512-ZSD2SxVSgUjHGrB0Wi+4tu3MEpB4fYSbezsFNEJk2xCWDBkFiOeEsjTmR5dvi+CxTK691hQTQlHv0XWuP7ENTg==} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/button@0.0.19': + resolution: {integrity: sha512-HYHrhyVGt7rdM/ls6FuuD6XE7fa7bjZTJqB2byn6/oGsfiEZaogY77OtoLL/mrQHjHjZiJadtAMSik9XLcm7+A==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/code-block@0.0.13': + resolution: {integrity: sha512-4DE4yPSgKEOnZMzcrDvRuD6mxsNxOex0hCYEG9F9q23geYgb2WCCeGBvIUXVzK69l703Dg4Vzrd5qUjl+JfcwA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/code-inline@0.0.5': + resolution: {integrity: sha512-MmAsOzdJpzsnY2cZoPHFPk6uDO/Ncpb4Kh1hAt9UZc1xOW3fIzpe1Pi9y9p6wwUmpaeeDalJxAxH6/fnTquinA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/column@0.0.13': + resolution: {integrity: sha512-Lqq17l7ShzJG/d3b1w/+lVO+gp2FM05ZUo/nW0rjxB8xBICXOVv6PqjDnn3FXKssvhO5qAV20lHM6S+spRhEwQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/components@0.0.42': + resolution: {integrity: sha512-KZtf7RjCoLgEwa5swrsbEF/liGrmfMlZCDNDXqaAjlgF3iRq0h5KY/HNMs6LbLmW7fEneRhnynrwApwbkEwe+A==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/container@0.0.15': + resolution: {integrity: sha512-Qo2IQo0ru2kZq47REmHW3iXjAQaKu4tpeq/M8m1zHIVwKduL2vYOBQWbC2oDnMtWPmkBjej6XxgtZByxM6cCFg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/font@0.0.9': + resolution: {integrity: sha512-4zjq23oT9APXkerqeslPH3OZWuh5X4crHK6nx82mVHV2SrLba8+8dPEnWbaACWTNjOCbcLIzaC9unk7Wq2MIXw==} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/head@0.0.12': + resolution: {integrity: sha512-X2Ii6dDFMF+D4niNwMAHbTkeCjlYYnMsd7edXOsi0JByxt9wNyZ9EnhFiBoQdqkE+SMDcu8TlNNttMrf5sJeMA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/heading@0.0.15': + resolution: {integrity: sha512-xF2GqsvBrp/HbRHWEfOgSfRFX+Q8I5KBEIG5+Lv3Vb2R/NYr0s8A5JhHHGf2pWBMJdbP4B2WHgj/VUrhy8dkIg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/hr@0.0.11': + resolution: {integrity: sha512-S1gZHVhwOsd1Iad5IFhpfICwNPMGPJidG/Uysy1AwmspyoAP5a4Iw3OWEpINFdgh9MHladbxcLKO2AJO+cA9Lw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/html@0.0.11': + resolution: {integrity: sha512-qJhbOQy5VW5qzU74AimjAR9FRFQfrMa7dn4gkEXKMB/S9xZN8e1yC1uA9C15jkXI/PzmJ0muDIWmFwatm5/+VA==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/img@0.0.11': + resolution: {integrity: sha512-aGc8Y6U5C3igoMaqAJKsCpkbm1XjguQ09Acd+YcTKwjnC2+0w3yGUJkjWB2vTx4tN8dCqQCXO8FmdJpMfOA9EQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/link@0.0.12': + resolution: {integrity: sha512-vF+xxQk2fGS1CN7UPQDbzvcBGfffr+GjTPNiWM38fhBfsLv6A/YUfaqxWlmL7zLzVmo0K2cvvV9wxlSyNba1aQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/markdown@0.0.15': + resolution: {integrity: sha512-UQA9pVm5sbflgtg3EX3FquUP4aMBzmLReLbGJ6DZQZnAskBF36aI56cRykDq1o+1jT+CKIK1CducPYziaXliag==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/preview@0.0.13': + resolution: {integrity: sha512-F7j9FJ0JN/A4d7yr+aw28p4uX7VLWs7hTHtLo7WRyw4G+Lit6Zucq4UWKRxJC8lpsUdzVmG7aBJnKOT+urqs/w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/render@1.1.2': + resolution: {integrity: sha512-RnRehYN3v9gVlNMehHPHhyp2RQo7+pSkHDtXPvg3s0GbzM9SQMW4Qrf8GRNvtpLC4gsI+Wt0VatNRUFqjvevbw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/row@0.0.12': + resolution: {integrity: sha512-HkCdnEjvK3o+n0y0tZKXYhIXUNPDx+2vq1dJTmqappVHXS5tXS6W5JOPZr5j+eoZ8gY3PShI2LWj5rWF7ZEtIQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/section@0.0.16': + resolution: {integrity: sha512-FjqF9xQ8FoeUZYKSdt8sMIKvoT9XF8BrzhT3xiFKdEMwYNbsDflcjfErJe3jb7Wj/es/lKTbV5QR1dnLzGpL3w==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/tailwind@1.0.5': + resolution: {integrity: sha512-BH00cZSeFfP9HiDASl+sPHi7Hh77W5nzDgdnxtsVr/m3uQD9g180UwxcE3PhOfx0vRdLzQUU8PtmvvDfbztKQg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@react-email/text@0.1.5': + resolution: {integrity: sha512-o5PNHFSE085VMXayxH+SJ1LSOtGsTv+RpNKnTiJDrJUwoBu77G3PlKOsZZQHCNyD28WsQpl9v2WcJLbQudqwPg==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^18.0 || ^19.0 || ^19.0.0-rc + + '@reduxjs/toolkit@2.8.2': + resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.50.0': + resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.50.0': + resolution: {integrity: sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.50.0': + resolution: {integrity: sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.50.0': + resolution: {integrity: sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.50.0': + resolution: {integrity: sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.50.0': + resolution: {integrity: sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.50.0': + resolution: {integrity: sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.50.0': + resolution: {integrity: sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.50.0': + resolution: {integrity: sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.50.0': + resolution: {integrity: sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.50.0': + resolution: {integrity: sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.50.0': + resolution: {integrity: sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.50.0': + resolution: {integrity: sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.50.0': + resolution: {integrity: sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.50.0': + resolution: {integrity: sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.50.0': + resolution: {integrity: sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.50.0': + resolution: {integrity: sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.50.0': + resolution: {integrity: sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.50.0': + resolution: {integrity: sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.50.0': + resolution: {integrity: sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.50.0': + resolution: {integrity: sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==} + cpu: [x64] + os: [win32] + + '@selderee/plugin-htmlparser2@0.11.0': + resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + + '@sentry/core@8.55.0': + resolution: {integrity: sha512-6g7jpbefjHYs821Z+EBJ8r4Z7LT5h80YSWRJaylGS4nW5W5Z2KXzpdnyFarv37O7QjauzVC2E+PABmpkw5/JGA==} + engines: {node: '>=14.18'} + + '@sentry/core@9.46.0': + resolution: {integrity: sha512-it7JMFqxVproAgEtbLgCVBYtQ9fIb+Bu0JD+cEplTN/Ukpe6GaolyYib5geZqslVxhp2sQgT+58aGvfd/k0N8Q==} + engines: {node: '>=18'} + + '@sentry/node-core@9.46.0': + resolution: {integrity: sha512-XRVu5pqoklZeh4wqhxCLZkz/ipoKhitctgEFXX9Yh1e1BoHM2pIxT52wf+W6hHM676TFmFXW3uKBjsmRM3AjgA==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.0.0 + '@opentelemetry/core': ^1.30.1 || ^2.0.0 + '@opentelemetry/instrumentation': '>=0.57.1 <1' + '@opentelemetry/resources': ^1.30.1 || ^2.0.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.0.0 + '@opentelemetry/semantic-conventions': ^1.34.0 + + '@sentry/node@9.46.0': + resolution: {integrity: sha512-pRLqAcd7GTGvN8gex5FtkQR5Mcol8gOy1WlyZZFq4rBbVtMbqKOQRhohwqnb+YrnmtFpj7IZ7KNDo077MvNeOQ==} + engines: {node: '>=18'} + + '@sentry/opentelemetry@9.46.0': + resolution: {integrity: sha512-w2zTxqrdmwRok0cXBoh+ksXdGRUHUZhlpfL/H2kfTodOL+Mk8rW72qUmfqQceXoqgbz8UyK8YgJbyt+XS5H4Qg==} + engines: {node: '>=18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.0.0 + '@opentelemetry/core': ^1.30.1 || ^2.0.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.0.0 + '@opentelemetry/semantic-conventions': ^1.34.0 + + '@sentry/types@8.55.0': + resolution: {integrity: sha512-6LRT0+r6NWQ+RtllrUW2yQfodST0cJnkOmdpHA75vONgBUhpKwiJ4H7AmgfoTET8w29pU6AnntaGOe0LJbOmog==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.55.0': + resolution: {integrity: sha512-cYcl39+xcOivBpN9d8ZKbALl+DxZKo/8H0nueJZ0PO4JA+MJGhSm6oHakXxLPaiMoNLTX7yor8ndnQIuFg+vmQ==} + engines: {node: '>=14.18'} + + '@simplewebauthn/browser@13.1.2': + resolution: {integrity: sha512-aZnW0KawAM83fSBUgglP5WofbrLbLyr7CoPqYr66Eppm7zO86YX6rrCjRB3hQKPrL7ATvY4FVXlykZ6w6FwYYw==} + + '@simplewebauthn/server@13.1.2': + resolution: {integrity: sha512-VwoDfvLXSCaRiD+xCIuyslU0HLxVggeE5BL06+GbsP2l1fGf5op8e0c3ZtKoi+vSg1q4ikjtAghC23ze2Q3H9g==} + engines: {node: '>=20.0.0'} + + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@supabase/auth-js@2.71.1': + resolution: {integrity: sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q==} + + '@supabase/functions-js@2.4.5': + resolution: {integrity: sha512-v5GSqb9zbosquTo6gBwIiq7W9eQ7rE5QazsK/ezNiQXdCbY+bH8D9qEaBIkhVvX4ZRW5rP03gEfw5yw9tiq4EQ==} + + '@supabase/node-fetch@2.6.15': + resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} + engines: {node: 4.x || >=6.0.0} + + '@supabase/postgrest-js@1.21.3': + resolution: {integrity: sha512-rg3DmmZQKEVCreXq6Am29hMVe1CzemXyIWVYyyua69y6XubfP+DzGfLxME/1uvdgwqdoaPbtjBDpEBhqxq1ZwA==} + + '@supabase/realtime-js@2.15.4': + resolution: {integrity: sha512-e/FYIWjvQJHOCNACWehnKvg26zosju3694k0NMUNb+JGLdvHJzEa29ZVVLmawd2kvx4hdbv8mxSqfttRnH3+DA==} + + '@supabase/ssr@0.5.2': + resolution: {integrity: sha512-n3plRhr2Bs8Xun1o4S3k1CDv17iH5QY9YcoEvXX3bxV1/5XSasA0mNXYycFmADIdtdE6BG9MRjP5CGIs8qxC8A==} + peerDependencies: + '@supabase/supabase-js': ^2.43.4 + + '@supabase/storage-js@2.11.0': + resolution: {integrity: sha512-Y+kx/wDgd4oasAgoAq0bsbQojwQ+ejIif8uczZ9qufRHWFLMU5cODT+ApHsSrDufqUcVKt+eyxtOXSkeh2v9ww==} + + '@supabase/supabase-js@2.56.1': + resolution: {integrity: sha512-cb/kS0d6G/qbcmUFItkqVrQbxQHWXzfRZuoiSDv/QiU6RbGNTn73XjjvmbBCZ4MMHs+5teihjhpEVluqbXISEg==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.1.12': + resolution: {integrity: sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==} + + '@tailwindcss/oxide-android-arm64@4.1.12': + resolution: {integrity: sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.12': + resolution: {integrity: sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.12': + resolution: {integrity: sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.12': + resolution: {integrity: sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + resolution: {integrity: sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + resolution: {integrity: sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + resolution: {integrity: sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + resolution: {integrity: sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.12': + resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.12': + resolution: {integrity: sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@tanstack/query-core@5.87.1': + resolution: {integrity: sha512-HOFHVvhOCprrWvtccSzc7+RNqpnLlZ5R6lTmngb8aq7b4rc2/jDT0w+vLdQ4lD9bNtQ+/A4GsFXy030Gk4ollA==} + + '@tanstack/query-devtools@5.86.0': + resolution: {integrity: sha512-/JDw9BP80eambEK/EsDMGAcsL2VFT+8F5KCOwierjPU7QP8Wt1GT32yJpn3qOinBM8/zS3Jy36+F0GiyJp411A==} + + '@tanstack/react-query-devtools@5.87.1': + resolution: {integrity: sha512-YPuEub8RQrrsXOxoiMJn33VcGPIeuVINWBgLu9RLSQB8ueXaKlGLZ3NJkahGpbt2AbWf749FQ6R+1jBFk3kdCA==} + peerDependencies: + '@tanstack/react-query': ^5.87.1 + react: ^18 || ^19 + + '@tanstack/react-query@5.87.1': + resolution: {integrity: sha512-YKauf8jfMowgAqcxj96AHs+Ux3m3bWT1oSVKamaRPXSnW2HqSznnTCEkAVqctF1e/W9R/mPcyzzINIgpOH94qg==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-table@8.21.3': + resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} + + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/aws-lambda@8.10.147': + resolution: {integrity: sha512-nD0Z9fNIZcxYX5Mai2CTmFD7wX7UldCkW2ezCF8D1T5hdiLsnTWDGRpfRYntU6VjTdLQjOvyszru7I1c1oCQew==} + + '@types/aws-lambda@8.10.150': + resolution: {integrity: sha512-AX+AbjH/rH5ezX1fbK8onC/a+HyQHo7QGmvoxAE42n22OsciAxvZoZNEr22tbXs8WfP1nIsBjKDpgPm3HjOZbA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/bun@1.2.21': + resolution: {integrity: sha512-NiDnvEqmbfQ6dmZ3EeUO577s4P5bf4HCTXtI6trMc6f6RzirY5IrF3aIookuSpyslFzrnvv2lmEWv5HyC1X79A==} + + '@types/bunyan@1.8.11': + resolution: {integrity: sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==} + + '@types/chai-http@3.0.5': + resolution: {integrity: sha512-nJ/oIvYley9+1Fec8xzDHsWrWSu5VTuhuF8iLabnRTQwRfSbAke6VuI7qBXvBN9Qqo/+gwsSaGyYndofTpiJ9A==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/d3-array@3.2.1': + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/express-serve-static-core@5.0.7': + resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==} + + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/history@4.7.11': + resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.20': + resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + + '@types/luxon@3.4.2': + resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/memcached@2.2.10': + resolution: {integrity: sha512-AM9smvZN55Gzs2wRrqeMHVP7KE8KWgCJO/XL5yCly2xF6EKa4YlbpK+cLSAH4NG/Ah64HrlegmGqW8kYws7Vxg==} + + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/mocha@10.0.10': + resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + + '@types/mysql@2.15.26': + resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} + + '@types/mysql@2.15.27': + resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} + + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node@18.19.123': + resolution: {integrity: sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==} + + '@types/node@22.18.0': + resolution: {integrity: sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==} + + '@types/node@24.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} + + '@types/oracledb@6.5.2': + resolution: {integrity: sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==} + + '@types/pg-pool@2.0.6': + resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} + + '@types/pg@8.15.4': + resolution: {integrity: sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==} + + '@types/pg@8.15.5': + resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==} + + '@types/pg@8.6.1': + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + + '@types/phoenix@1.6.6': + resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==} + + '@types/prismjs@1.26.5': + resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react-router-dom@5.3.3': + resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} + + '@types/react-router@5.1.20': + resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} + + '@types/react@18.3.24': + resolution: {integrity: sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==} + + '@types/recharts@2.0.1': + resolution: {integrity: sha512-/cFs7oiafzByUwBSWA1IzE6FW+ppPwQAWsDTadSgVOwzveY9MESpyLHyyHY0SfPPKLW4+4qVNYHPXd0rFiC8vg==} + deprecated: This is a stub types definition. recharts provides its own type definitions, so you do not need this installed. + + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + + '@types/stack-trace@0.0.33': + resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==} + + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/tedious@4.0.14': + resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@types/yauzl@2.10.3': + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + + '@typescript-eslint/eslint-plugin@8.41.0': + resolution: {integrity: sha512-8fz6oa6wEKZrhXWro/S3n2eRJqlRcIa6SlDh59FXJ5Wp5XRZ8B9ixpJDcjadHq47hMx0u+HW6SNa6LjJQ6NLtw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.41.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.41.0': + resolution: {integrity: sha512-gTtSdWX9xiMPA/7MV9STjJOOYtWwIJIYxkQxnSV1U3xcE+mnJSH3f6zI0RYP+ew66WSlZ5ed+h0VCxsvdC1jJg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.41.0': + resolution: {integrity: sha512-b8V9SdGBQzQdjJ/IO3eDifGpDBJfvrNTp2QD9P2BeqWTGrRibgfgIlBSw6z3b6R7dPzg752tOs4u/7yCLxksSQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.41.0': + resolution: {integrity: sha512-n6m05bXn/Cd6DZDGyrpXrELCPVaTnLdPToyhBoFkLIMznRUQUEQdSp96s/pcWSQdqOhrgR1mzJ+yItK7T+WPMQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.41.0': + resolution: {integrity: sha512-TDhxYFPUYRFxFhuU5hTIJk+auzM/wKvWgoNYOPcOf6i4ReYlOoYN8q1dV5kOTjNQNJgzWN3TUUQMtlLOcUgdUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.41.0': + resolution: {integrity: sha512-63qt1h91vg3KsjVVonFJWjgSK7pZHSQFKH6uwqxAH9bBrsyRhO6ONoKyXxyVBzG1lJnFAJcKAcxLS54N1ee1OQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.41.0': + resolution: {integrity: sha512-9EwxsWdVqh42afLbHP90n2VdHaWU/oWgbH2P0CfcNfdKL7CuKpwMQGjwev56vWu9cSKU7FWSu6r9zck6CVfnag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.41.0': + resolution: {integrity: sha512-D43UwUYJmGhuwHfY7MtNKRZMmfd8+p/eNSfFe6tH5mbVDto+VQCayeAt35rOx3Cs6wxD16DQtIKw/YXxt5E0UQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.41.0': + resolution: {integrity: sha512-udbCVstxZ5jiPIXrdH+BZWnPatjlYwJuJkDA4Tbo3WyYLh8NvB+h/bKeSZHDOFKfphsZYJQqaFtLeXEqurQn1A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.41.0': + resolution: {integrity: sha512-+GeGMebMCy0elMNg67LRNoVnUFPIm37iu5CmHESVx56/9Jsfdpsvbv605DQ81Pi/x11IdKUsS5nzgTYbCQU9fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-x5fzycDS72mJyekZR2oITvBUJ7eBV3xNh21smMNE/OpkhdNTcMPSj9TqXRk7kmIPKu+d6f+WaQvuV4xp6BekFw==} + engines: {node: '>=20.6.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/native-preview-darwin-x64@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-6Wn/FoTeEFPLPYk3BsYlWtcu8bbHu85W51K1kMmuLaAwc4OWlT3xcY72uz1QThZK3EDF457n7hvO5TQIE64ycg==} + engines: {node: '>=20.6.0'} + cpu: [x64] + os: [darwin] + + '@typescript/native-preview-linux-arm64@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-WF2MRR5miHDk93UT8Nz/sdpDjyrpYk3cwidPgYLY4hwXa4s4D0JUY9vN0Rh0eAHZjHM1oWnPfk7Tuzl77d+ytA==} + engines: {node: '>=20.6.0'} + cpu: [arm64] + os: [linux] + + '@typescript/native-preview-linux-arm@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-qBOfntj8fT3L4xk3JEPj1c6oIgJ9Kf2hsHX8+zKScnXkZFIJZtnssa0EIcsjQoCbwp75bEmrglbVLXydrjz9xA==} + engines: {node: '>=20.6.0'} + cpu: [arm] + os: [linux] + + '@typescript/native-preview-linux-x64@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-9eAb4fHCBmo0pZNu3sziV6g+ohgwUk/z/TSvLDNBaZHMOTWR3iRhTzDWRCjOrxA6A4IPWTgVacnRoPFFnfbrUg==} + engines: {node: '>=20.6.0'} + cpu: [x64] + os: [linux] + + '@typescript/native-preview-win32-arm64@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-XfLw5OMWLg5VByYm/Gj0iU/rFFMgrzT6bL4zr2jw/gJkJv0Mi9dOAUsQbBlF4yCFFNb9lscgAPUDI1lIT63CVg==} + engines: {node: '>=20.6.0'} + cpu: [arm64] + os: [win32] + + '@typescript/native-preview-win32-x64@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-mK9SK17t7yr4QO8L+tB6n/iMRvQkiD7ypCdD07n1i8VapFOJrlQhDX0NLQueTJJRwwHWcUXHUWdz75tKdJdDhA==} + engines: {node: '>=20.6.0'} + cpu: [x64] + os: [win32] + + '@typescript/native-preview@7.0.0-dev.20250901.1': + resolution: {integrity: sha512-J3VKeImoPYF7kGHWXoJHk8BonWBj8R6DlQT/CXOvN7YnYH8CoOZXOysGlPhAg+S3SfF7BtU9HjvjlnhTGENn8g==} + engines: {node: '>=20.6.0'} + hasBin: true + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@upstash/redis@1.35.3': + resolution: {integrity: sha512-hSjv66NOuahW3MisRGlSgoszU2uONAY2l5Qo3Sae8OT3/Tng9K+2/cBRuyPBX8egwEGcNNCF9+r0V6grNnhL+w==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@wooorm/starry-night@3.8.0': + resolution: {integrity: sha512-BWRm0tCzWCmv1ucBh6frL2uFRvYFj/LWmJzr+rJsdF/JsJ1+bBkeiyExH1iWQS18IH22HFu7f4QgG99OU1nklA==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ag-charts-community@12.1.2: + resolution: {integrity: sha512-Z3oSsbIyAqaiB4PdnMQ2gt3s+oY19xTHJUee5TKjsTThX8wVX8PXRGMOZaFWbN/HNaZCWkUTgkuLrasw2w+x/w==} + + ag-charts-core@12.1.2: + resolution: {integrity: sha512-9AfMX+HSpr4zmp88PBWcOqO9jW1HTKxC+b/7B/xYOE6K1hBFvnuLbqdeNGfGoiyWqw5TUlY1lirr/9xrFyZFsQ==} + + ag-charts-locale@12.1.2: + resolution: {integrity: sha512-GFAq4KZit3CHT3Br/LGruas75HqsvV+VZV6qz5hLrmiovJSSVfzPirzf/hUGDTCHqIOS7CdxmpdKUu8pKv1LGw==} + + ag-charts-react@12.1.2: + resolution: {integrity: sha512-ungoxsE/sLaJ+bYWK2/cXts83jNRMogvTHysjvoxxFcU7vLat6FM01f4+NCE8szDSg+d6em2xI5NAcOROapBUg==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + + ag-charts-types@12.1.2: + resolution: {integrity: sha512-B5IEMzaDxpJRko9sREIBEZOn44tlMX353tMJ1RGYHZrjDFx4on/p6q3YxBvway3nViAM2POmFOS45+1fF9YC8g==} + + ag-grid-community@34.1.2: + resolution: {integrity: sha512-PN79zG/tigZaVKkQZXaithLO3j4JWp+9CjqEFth6kFUwMJ0E6OcNzNtn4NFEKM38fEdpGsr+k+gFk5KUE2h5fA==} + + ag-grid-react@34.1.2: + resolution: {integrity: sha512-6l5+hp3O4VSZ1nc2IZSQyhNeUOZ4TDg4UVrpxsqV3xXBO1TRqmOhcsYBDJ95JZ8zSiAEvy7XKT3MSI7sJG+MFg==} + 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 + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ai@4.3.19: + resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.0: + resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + array-flatten@1.1.1: + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + asn1js@3.0.6: + resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + engines: {node: '>=12.0.0'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + + async-listen@3.1.0: + resolution: {integrity: sha512-TkOhqze98lP+6e7SPbrBpyhTpfvqqX8VYKGn4uckrgPan4WQIHnTaUD2zZzZS18eVVDj4rHPcIZa1PGgvo1DfA==} + engines: {node: '>= 14'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + attr-accept@2.2.5: + resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} + engines: {node: '>=4'} + + autumn-js@0.1.28: + resolution: {integrity: sha512-f40FLvmZtY/3t41HZtUZmi5/191DhEE7IATI+8388yGPBSa4Pk46J5zeExfhBJEiphYirFoDxh4jxwpw5cJQdw==} + peerDependencies: + better-auth: ^1.3.4 + better-call: ^1.0.12 + convex: ^1.25.4 + react: '*' + peerDependenciesMeta: + better-auth: + optional: true + better-call: + optional: true + react: + optional: true + + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.6.1: + resolution: {integrity: sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==} + + bare-fs@4.2.1: + resolution: {integrity: sha512-mELROzV0IhqilFgsl1gyp48pnZsaV9xhQapHLDsvn4d4ZTfbFhcghQezl7FTEDNBcGqLUnNI3lUlm6ecrLWdFA==} + engines: {bare: '>=1.16.0'} + peerDependencies: + bare-buffer: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + + bare-os@3.6.2: + resolution: {integrity: sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==} + engines: {bare: '>=1.14.0'} + + bare-path@3.0.0: + resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} + + bare-stream@2.7.0: + resolution: {integrity: sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==} + peerDependencies: + bare-buffer: '*' + bare-events: '*' + peerDependenciesMeta: + bare-buffer: + optional: true + bare-events: + optional: true + + base-convert-int-array@1.0.1: + resolution: {integrity: sha512-NWqzaoXx8L/SS32R+WmKqnQkVXVYl2PwNJ68QV3RAlRRL1uV+yxJT66abXI1cAvqCXQTyXr7/9NN4Af90/zDVw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + engines: {node: '>=10.0.0'} + + better-auth@1.3.7: + resolution: {integrity: sha512-/1fEyx2SGgJQM5ujozDCh9eJksnVkNU/J7Fk/tG5Y390l8nKbrPvqiFlCjlMM+scR+UABJbQzA6An7HT50LHyQ==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + better-call@1.0.16: + resolution: {integrity: sha512-42dgJ1rOtc0anOoxjXPOWuel/Z/4aeO7EJ2SiXNwvlkySSgjXhNjAjTMWa8DL1nt6EXS3jl3VKC3mPsU/lUgVA==} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@1.20.3: + resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + browserslist@4.25.4: + resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bullmq@5.58.4: + resolution: {integrity: sha512-1EE7pbEw2YNc64nVW+RYVvA6I1ijkhCacsZ/zWbjhDtN8sxZ+Wy3WQ/rUSMOOk/PGAGEQRTgngbTpYbvGYijHQ==} + + bun-types@1.2.21: + resolution: {integrity: sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw==} + peerDependencies: + '@types/react': ^19 + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001739: + resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai-http@5.1.2: + resolution: {integrity: sha512-UFup7mUGkkjmi9bGA7F6vfp3lzGQZjtL//CEd+a4C+vlynSv756XHDUK8PoYk/UpTBBXqSghjQaJOUMUxJXNaA==} + engines: {node: '>=18.20.0'} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.0: + resolution: {integrity: sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chardet@2.1.0: + resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + + charset@1.0.1: + resolution: {integrity: sha512-6dVyOOYjpfFcL1Y4qChrAoQLRHvj2ziyhcm0QJlhOcAhykL/k1kTUPbeo+87MNRTRdk2OIIsIXbuF3x2wi5EXg==} + engines: {node: '>=4.0.0'} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + chromium-bidi@8.0.0: + resolution: {integrity: sha512-d1VmE0FD7lxZQHzcDUCKZSNRtRwISXDsdg4HjdTR5+Ll5nQ/vzU12JeNmupD6VWffrPSlrnGhEWlLESKH3VO+g==} + peerDependencies: + devtools-protocol: '*' + + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone-regexp@3.0.0: + resolution: {integrity: sha512-ujdnoq2Kxb8s3ItNBtnYeXdm07FcU0u8ARAT1lQ2YdMwQC+cdiXX8KoqMVuglztILivceTtp4ivqGSmEmhBUJw==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + cloudflare@4.5.0: + resolution: {integrity: sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + cmdk@1.1.1: + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} + engines: {node: '>=18'} + hasBin: true + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-hrtime@5.0.0: + resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} + engines: {node: '>=12'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + convex@1.26.2: + resolution: {integrity: sha512-QG3zvZ9GTTeeBS+N5PJj73TULHYu99eVSOI3KU2hMnB9q40fHxkMFJ+AYox+K81roTakciBvNTvGlhfz+M10fQ==} + engines: {node: '>=18.0.0', npm: '>=7.0.0'} + hasBin: true + peerDependencies: + '@auth0/auth0-react': ^2.0.1 + '@clerk/clerk-react': ^4.12.8 || ^5.0.0 + react: ^18.0.0 || ^19.0.0-0 || ^19.0.0 + peerDependenciesMeta: + '@auth0/auth0-react': + optional: true + '@clerk/clerk-react': + optional: true + react: + optional: true + + cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + + core-js@3.45.1: + resolution: {integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cron-parser@4.9.0: + resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} + engines: {node: '>=12.0.0'} + + cron@3.5.0: + resolution: {integrity: sha512-0eYZqCnapmxYcV06uktql93wNWdlTmmBFP2iYz+JPVcQqlyFYcn1lFuIk4R54pkOmE7mcldTAPZv6X5XA4Q46A==} + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-jss@10.10.0: + resolution: {integrity: sha512-YyMIS/LsSKEGXEaVJdjonWe18p4vXLo8CMA4FrW/kcaEyqdIGKCFXao31gbJddXEdIxSXFFURWrenBJPlKTgAA==} + + css-vendor@2.0.8: + resolution: {integrity: sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + csv-parse@5.6.0: + resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + + csv-parser@3.2.0: + resolution: {integrity: sha512-fgKbp+AJbn1h2dcAHKIdKNSSjfp43BZZykXsCjzALjKy80VXQNHPFJ6T9Afwdzoj24aMkq8GwDS7KGcDPpejrA==} + engines: {node: '>= 10'} + hasBin: true + + currency-symbol-map@5.1.0: + resolution: {integrity: sha512-LO/lzYRw134LMDVnLyAf1dHE5tyO6axEFkR3TXjQIOmMkAM9YL6QsiUwuXzZAmFnuDJcs4hayOgyIYtViXFrLw==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + data-uri-to-buffer@6.0.2: + resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} + engines: {node: '>= 14'} + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + + debounce@2.2.0: + resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} + engines: {node: '>=18'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-uri-component@0.4.1: + resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} + engines: {node: '>=14.16'} + + 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==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + degenerator@5.0.1: + resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} + engines: {node: '>= 14'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-content-type@1.2.0: + resolution: {integrity: sha512-YCBxuqJLY9rMxV44Ict2kNgjYFN3v1dnsn6sJvd6sUwwU1TWP3D+K2dr/S9AF/fio2/RsAKYdRiEOtNoRbmiag==} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + devtools-protocol@0.0.1475386: + resolution: {integrity: sha512-RQ809ykTfJ+dgj9bftdeL2vRVxASAuGU+I9LEx9Ij5TXU5HrgAQVmzi72VA+mkzscE12uzlRv5/tWWv9R9J1SA==} + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + drizzle-kit@0.31.4: + resolution: {integrity: sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==} + hasBin: true + + drizzle-orm@0.43.1: + resolution: {integrity: sha512-dUcDaZtE/zN4RV/xqGrVSMpnEczxd5cIaoDeor7Zst9wOe/HzC/7eAaulywWGYXdDEc9oBPMjayVEDg0ziTLJA==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + drizzle-zod@0.8.3: + resolution: {integrity: sha512-66yVOuvGhKJnTdiqj1/Xaaz9/qzOdRJADpDa68enqS6g3t0kpNkwNYjUuaeXgZfO/UWuIM9HIhSlJ6C5ZraMww==} + peerDependencies: + drizzle-orm: '>=0.36.0' + zod: ^3.25.0 || ^4.0.0 + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.211: + resolution: {integrity: sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==} + + emoji-regex@10.5.0: + resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.4: + resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} + engines: {node: '>=10.2.0'} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-toolkit@1.39.10: + resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} + + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.25.9: + resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.4.20: + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} + peerDependencies: + eslint: '>=8.40' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.34.0: + resolution: {integrity: sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} + engines: {node: '>= 0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + extract-zip@2.0.1: + resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} + engines: {node: '>= 10.17.0'} + hasBin: true + + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + + fast-deep-equal@2.0.1: + resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-retry@6.0.0: + resolution: {integrity: sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==} + + fflate@0.4.8: + resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-selector@2.1.2: + resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==} + engines: {node: '>= 12'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + filter-obj@5.1.0: + resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} + engines: {node: '>=14.16'} + + finalhandler@1.3.1: + resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} + engines: {node: '>= 0.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'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + framer-motion@12.23.12: + resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function-timeout@0.1.1: + resolution: {integrity: sha512-0NVVC0TaP7dSTvn1yMiy6d6Q8gifzbvQafO46RtLG/kHJUBNd+pVRGOBoK44wNBvtSPUJRfdVvkFdD3p0xvyZg==} + engines: {node: '>=14.16'} + + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + + gcp-metadata@6.1.1: + resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} + engines: {node: '>=14'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.1: + resolution: {integrity: sha512-R1QfovbPsKmosqTnPoRFiJ7CF9MLRgb53ChvMZm+r4p76/+8yKDy17qLL2PKInORy2RkZZekuK0efYgmzTkXyQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} + engines: {node: '>= 14'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@11.0.3: + resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + engines: {node: 20 || >=22} + hasBin: true + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + goober@2.1.16: + resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==} + peerDependencies: + csstype: ^3.0.10 + + google-logging-utils@0.0.2: + resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==} + engines: {node: '>=14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + html-to-text@9.0.5: + resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} + engines: {node: '>=14'} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + http-status-codes@2.3.0: + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + iframe-resizer-react@1.1.1: + resolution: {integrity: sha512-s0EUUekv58FGbuWBanSCVsEKmmyBdWmdwQ8qx8g04jlNlCR7pNuDz7fw7zo5T5sdssl6yRMDl97NBdwD1gfDyQ==} + engines: {node: '>=16', npm: '>=5'} + peerDependencies: + prop-types: ^15.7.2 + react: ^16.13.1 || ^18.0.0 + react-dom: ^16.13.1 || ^18.0.0 + + iframe-resizer@4.4.5: + resolution: {integrity: sha512-U8bCywf/Gh07O69RXo6dXAzTtODQrxaHGHRI7Nt4ipXsuq6EMxVsOP/jjaP43YtXz/ibESS0uSVDN3sOGCzSmw==} + engines: {node: '>=0.8.0'} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-in-the-middle@1.14.2: + resolution: {integrity: sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw==} + + import-in-the-middle@1.7.4: + resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + input-otp@1.4.2: + resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + inquirer@12.9.4: + resolution: {integrity: sha512-5bV3LOgLtMAiJq1QpaUddfRrvaX59wiMYppS7z2jNRSQ64acI0yqx7WMxWhgymenSXOyD657g9tlsTjqGYM8sg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + ioredis@5.7.0: + resolution: {integrity: sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==} + engines: {node: '>=12.22.0'} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + ip-regex@5.0.0: + resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-in-browser@1.1.3: + resolution: {integrity: sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-ip@5.0.1: + resolution: {integrity: sha512-FCsGHdlrOnZQcp0+XT5a+pYowf33itBalCl+7ovNXC/7o5BhIpG14M3OrpPPdBSIQJCm+0M5+9mO7S9VVTTCFw==} + engines: {node: '>=14.16'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-regexp@3.1.0: + resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} + engines: {node: '>=12'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + + jiti@2.5.1: + resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + hasBin: true + + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + jss-plugin-camel-case@10.10.0: + resolution: {integrity: sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==} + + jss-plugin-compose@10.10.0: + resolution: {integrity: sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==} + + jss-plugin-default-unit@10.10.0: + resolution: {integrity: sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==} + + jss-plugin-expand@10.10.0: + resolution: {integrity: sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==} + + jss-plugin-extend@10.10.0: + resolution: {integrity: sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==} + + jss-plugin-global@10.10.0: + resolution: {integrity: sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==} + + jss-plugin-nested@10.10.0: + resolution: {integrity: sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==} + + jss-plugin-props-sort@10.10.0: + resolution: {integrity: sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==} + + jss-plugin-rule-value-function@10.10.0: + resolution: {integrity: sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==} + + jss-plugin-rule-value-observable@10.10.0: + resolution: {integrity: sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==} + + jss-plugin-template@10.10.0: + resolution: {integrity: sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==} + + jss-plugin-vendor-prefixer@10.10.0: + resolution: {integrity: sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==} + + jss-preset-default@10.10.0: + resolution: {integrity: sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==} + + jss@10.10.0: + resolution: {integrity: sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==} + + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + ksuid@3.0.0: + resolution: {integrity: sha512-81CkBGn/06ZVAjGvFZi6fVG8VcPeMH0JpJ4V1Z9VwrMMaGIeAjY4jrVdrIcxhL9I2ZUU6t5uiyswcmkk+KZegA==} + + kysely@0.28.5: + resolution: {integrity: sha512-rlB0I/c6FBDWPcQoDtkxi9zIvpmnV5xoIalfCMSMCa7nuA6VGA3F54TW9mEgX4DVf10sXAWCF5fDbamI/5ZpKA==} + engines: {node: '>=20.0.0'} + + leac@0.6.0: + resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + + lodash.isobject@3.0.2: + resolution: {integrity: sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} + engines: {node: '>=18'} + + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + loops@5.0.1: + resolution: {integrity: sha512-xM1c9mnlr8Hr4cHW944TQoK6ApynjinUWOgYZd9/B0/3lwTThq24BQ7+XLjgbFAP5kJzqDTRDQi3t+Diy51Udw==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.1.0: + resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lucide-react@0.525.0: + resolution: {integrity: sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + luxon@3.5.0: + resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} + engines: {node: '>=12'} + + luxon@3.7.1: + resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==} + engines: {node: '>=12'} + + magic-string@0.30.18: + resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + marked@7.0.4: + resolution: {integrity: sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ==} + engines: {node: '>= 16'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + md-to-react-email@5.0.5: + resolution: {integrity: sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==} + peerDependencies: + react: ^18.0 || ^19.0 + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-descriptors@1.0.3: + resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-detect@1.3.0: + resolution: {integrity: sha512-SFFkIWONYQdDoiA3iq2abd68kCy+ifzsgWyytMDa/v8Jiu5UhVhgb4upoKDVKTlCz663jqM9PksyM99m+MnSaQ==} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mocha@11.7.1: + resolution: {integrity: sha512-5EK+Cty6KheMS/YLPPMJC64g5V61gIR25KsRItHw6x4hEKT6Njp1n9LOlH4gpevuwMVS66SXaBBpg+RWZkza4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + + motion-dom@12.23.12: + resolution: {integrity: sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + motion@12.23.12: + resolution: {integrity: sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.5: + resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} + + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + + mylas@2.1.13: + resolution: {integrity: sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==} + engines: {node: '>=12.0.0'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.5: + resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} + engines: {node: ^18 || >=20} + hasBin: true + + nanostores@0.11.4: + resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} + engines: {node: ^18.0.0 || >=20.0.0} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + + next@15.5.2: + resolution: {integrity: sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@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': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-abort-controller@3.1.1: + resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + nodemon@3.1.10: + resolution: {integrity: sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==} + engines: {node: '>=10'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nuqs@2.5.2: + resolution: {integrity: sha512-vzKeoYlMRmNYPWECdn53Nmh/jM+r/iSezEin342EVXPogT6KzALwdnYbZxASE5vTdXRUtOymtPkgsarLipKetg==} + peerDependencies: + '@remix-run/react': '>=2' + '@tanstack/react-router': ^1 + next: '>=14.2.0' + react: '>=18.2.0 || ^19.0.0-0' + react-router: ^6 || ^7 + react-router-dom: ^6 || ^7 + peerDependenciesMeta: + '@remix-run/react': + optional: true + '@tanstack/react-router': + optional: true + next: + optional: true + react-router: + optional: true + react-router-dom: + optional: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + openai@4.104.0: + resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + 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'} + + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} + engines: {node: '>= 14'} + + pac-resolver@7.0.1: + resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} + engines: {node: '>= 14'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parseley@0.12.1: + resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + peberminta@0.9.0: + resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} + + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + + pg-cloudflare@1.2.7: + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} + + pg-connection-string@2.9.1: + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.10.1: + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.16.3: + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-pretty@13.1.1: + resolution: {integrity: sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==} + hasBin: true + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@9.9.0: + resolution: {integrity: sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==} + hasBin: true + + plimit-lit@1.6.1: + resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} + engines: {node: '>=12'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + + posthog-js@1.261.0: + resolution: {integrity: sha512-jyiXqyrCU+VlpbNNVRA6OQYAVut0XZMYNELCZH+XvTd981VqbE4jXn4XCBreo7XCL2gdPgDVxUVOuzNvEuKcmw==} + peerDependencies: + '@rrweb/types': 2.0.0-alpha.17 + rrweb-snapshot: 2.0.0-alpha.17 + peerDependenciesMeta: + '@rrweb/types': + optional: true + rrweb-snapshot: + optional: true + + posthog-node@4.18.0: + resolution: {integrity: sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==} + engines: {node: '>=15.0.0'} + + preact@10.27.1: + resolution: {integrity: sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + prism-themes@1.9.0: + resolution: {integrity: sha512-tX2AYsehKDw1EORwBps+WhBFKc2kxfoFpQAjxBndbZKr4fRmMkv47XN0BghC/K1qwodB1otbe4oF23vUTFDokw==} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + protobufjs@7.5.4: + resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} + engines: {node: '>=12.0.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} + engines: {node: '>= 14'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + pump@3.0.3: + resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + puppeteer-core@24.18.0: + resolution: {integrity: sha512-As0BvfXxek2MbV0m7iqBmQKFnfSrzSvTM4zGipjd4cL+9f2Ccgut6RvHlc8+qBieKHqCMFy9BSI4QyveoYXTug==} + engines: {node: '>=18'} + + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + query-string@9.2.2: + resolution: {integrity: sha512-pDSIZJ9sFuOp6VnD+5IkakSVf+rICAuuU88Hcsr6AKL0QtxSIfVuKiVP2oahFI7tk3CRSexwV+Ya6MOoTxzg9g==} + engines: {node: '>=18'} + + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + + queue-lit@1.5.2: + resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} + engines: {node: '>=12'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@2.5.2: + resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} + engines: {node: '>= 0.8'} + + react-confetti-explosion@2.1.2: + resolution: {integrity: sha512-4UzDFBajAGXmF9TSJoRMO2QOBCIXc66idTxH8l7Mkul48HLGtk+tMzK9HYDYsy7Zmw5sEGchi2fbn4AJUuLrZw==} + peerDependencies: + react: ^18.x + react-dom: ^18.x + + react-day-picker@8.10.1: + resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} + peerDependencies: + date-fns: ^2.28.0 || ^3.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + react-display-name@0.2.5: + resolution: {integrity: sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-dropzone@14.3.8: + resolution: {integrity: sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==} + engines: {node: '>= 10.13'} + peerDependencies: + react: '>= 16.8 || 18.0.0' + + react-email@4.0.16: + resolution: {integrity: sha512-auhFU+nQxAkKkP6lQhPyGsa9exwfUEzp2BwZnjHokCwphZlg30tu4t1LgdKRwGPYsi7XNGy6asbVLAUhOVpzzg==} + engines: {node: '>=18.0.0'} + hasBin: true + + react-hot-toast@2.6.0: + resolution: {integrity: sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + + react-hotkeys-hook@4.6.2: + resolution: {integrity: sha512-FmP+ZriY3EG59Ug/lxNfrObCnW9xQShgk7Nb83+CkpfkcCpfS95ydv+E9JuXA5cp8KtskU7LGlIARpkc92X22Q==} + peerDependencies: + react: '>=16.8.1' + react-dom: '>=16.8.1' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-jss@10.10.0: + resolution: {integrity: sha512-WLiq84UYWqNBF6579/uprcIUnM1TSywYq6AIjKTTTG5ziJl9Uy+pwuvpN3apuyVwflMbD60PraeTKT7uWH9XEQ==} + peerDependencies: + react: '>=16.8.6' + + react-promise-suspense@0.3.4: + resolution: {integrity: sha512-I42jl7L3Ze6kZaq+7zXWSunBa3b1on5yfvUW6Eo/3fFOj6dZ5Bqmcd264nJbTK/gn1HjjILAjSwnZbV4RpSaNQ==} + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-router-dom@7.8.2: + resolution: {integrity: sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.8.2: + resolution: {integrity: sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + recaseai@0.0.37: + resolution: {integrity: sha512-cKVMWTGBnGtm8K+uD2vfMXOzxdHj1U3++vvTePpOoZABI/SY/jjDpW997vM5xeDnzz7Vk/qqVkLQUQ/ZMDXpsQ==} + hasBin: true + + recharts@3.1.2: + resolution: {integrity: sha512-vhNbYwaxNbk/IATK0Ki29k3qvTkGqwvCgyQAQ9MavvvBwjvKnMTswdbklJpcOAoMPN/qxF3Lyqob0zO+ZXkZ4g==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.5.2: + resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} + engines: {node: '>=8.6.0'} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resend@4.8.0: + resolution: {integrity: sha512-R8eBOFQDO6dzRTDmaMEdpqrkmgSjPpVXt4nGfWsZdYOet0kqra0xgbvTES6HmCriZEXbmGk3e0DiGIaLFTFSHA==} + engines: {node: '>=18'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.50.0: + resolution: {integrity: sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rou3@0.5.1: + resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} + + rou3@0.6.3: + resolution: {integrity: sha512-1HSG1ENTj7Kkm5muMnXuzzfdDOf7CFnbSYFA+H3Fp/rB9lOCxCPgy1jlZxTKyFoC5jJay8Mmc+VbPLYRjzYLrA==} + + run-async@4.0.6: + resolution: {integrity: sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + secure-json-parse@4.0.0: + resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} + + selderee@0.11.0: + resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serialize-error@8.1.0: + resolution: {integrity: sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==} + engines: {node: '>=10'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + + sharp@0.34.3: + resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + 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'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + + sonner@2.0.7: + 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'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + split-on-first@3.0.0: + resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} + engines: {node: '>=12'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + streamx@2.22.1: + resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + + stripe@18.5.0: + resolution: {integrity: sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA==} + engines: {node: '>=12.*'} + peerDependencies: + '@types/node': '>=12.x.x' + peerDependenciesMeta: + '@types/node': + optional: true + + styled-jsx@5.1.6: + 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' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + super-regex@0.2.0: + resolution: {integrity: sha512-WZzIx3rC1CvbMDloLsVw0lkZVKJWbrkJ0k1ghKFmcnPrW1+jWbgTkTEWVtD9lMdmI4jZEz40+naBxl1dCUhXXw==} + engines: {node: '>=14.16'} + + superagent@10.2.3: + resolution: {integrity: sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==} + engines: {node: '>=14.18.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svix-react@1.13.6: + resolution: {integrity: sha512-6WACy36/UNAkpYQp6N6lWH7tNAwBzNSMcv1OCXmTMcWyq5CdTZ1IYdMEqElUObeDf67iIGf/HdZ0rmDhg3NgDA==} + peerDependencies: + react: '>=16' + react-dom: '>=16' + svix: '>=1.26.0' + + svix@1.75.0: + resolution: {integrity: sha512-3GLI1DN4Pfd8Ze+7yxoV3Ocp8hW07/WRDsoIBdjDvR916Fx3gRHaYSoa0WWRdKSqJNj+OTCeDSSv47mSp9ibqw==} + + swr@2.3.4: + resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + swr@2.3.6: + resolution: {integrity: sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + symbol-observable@1.2.0: + resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==} + engines: {node: '>=0.10.0'} + + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwind-scrollbar-hide@4.0.0: + resolution: {integrity: sha512-gobtvVcThB2Dxhy0EeYSS1RKQJ5baDFkamkhwBvzvevwX6L4XQfpZ3me9s25Ss1ecFVT5jPYJ50n+7xTBJG9WQ==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 4.0.0 || >= 4.0.0-beta.8 || >= 4.0.0-alpha.20' + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + tailwindcss@4.1.12: + resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} + + tapable@2.2.3: + resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + engines: {node: '>=6'} + + tar-fs@3.1.0: + resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + theming@3.3.0: + resolution: {integrity: sha512-u6l4qTJRDaWZsqa8JugaNt7Xd8PPl9+gonZaIe28vAhqgHMIG/DOyFPqiKN/gQLQYj05tHv+YQdNILL4zoiAVA==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.3' + + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + + time-span@5.1.0: + resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} + engines: {node: '>=12'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-alias@1.8.16: + resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==} + engines: {node: '>=16.20.2'} + hasBin: true + + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.5: + resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + typed-query-selector@2.12.0: + resolution: {integrity: sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==} + + typescript-eslint@8.41.0: + resolution: {integrity: sha512-n66rzs5OBXW3SFSnZHr2T685q1i4ODm2nulFJhMZBotaTavsS8TrI3d7bDlRSs9yWo7HmyWrN9qDu14Qv7Y0Dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} + engines: {node: '>=14.17'} + hasBin: true + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-oniguruma@2.0.1: + resolution: {integrity: sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==} + + vscode-textmate@9.2.0: + resolution: {integrity: sha512-rkvG4SraZQaPSN/5XjwKswdU0OP9MF28QjrYzUBbhb8QyG3ljB1Ky996m++jiI7KdiAP2CkBiQZd9pqEDTClqA==} + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + web-vitals@4.2.4: + resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workerpool@9.3.3: + resolution: {integrity: sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yauzl@2.10.0: + resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} + engines: {node: '>=18'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.1.5: + resolution: {integrity: sha512-rcUUZqlLJgBC33IT3PNMgsCq6TzLQEG/Ei/KTCU0PedSWRMAXoOUN+4t/0H+Q8bdnLPdqUYnvboJT0bn/229qg==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@ai-sdk/anthropic@1.2.12(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.76 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@18.3.1)(zod@3.25.76)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + react: 18.3.1 + swr: 2.3.6(react@18.3.1) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.76 + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@anthropic-ai/sdk@0.32.1': + dependencies: + '@types/node': 18.19.123 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@axiomhq/js@1.3.1': + dependencies: + fetch-retry: 6.0.0 + uuid: 11.1.0 + + '@axiomhq/pino@1.3.1': + dependencies: + '@axiomhq/js': 1.3.1 + pino-abstract-transport: 1.2.0 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.0': {} + + '@babel/core@7.28.3': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/helpers': 7.28.3 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1(supports-color@5.5.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.2 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.4 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.2 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.3 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.3 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + + '@babel/parser@7.28.3': + dependencies: + '@babel/types': 7.28.2 + + '@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.3) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.3)': + dependencies: + '@babel/core': 7.28.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/runtime@7.28.3': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.3': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.3 + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + debug: 4.4.1(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@better-auth/stripe@1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76)': + dependencies: + better-auth: 1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - react + - react-dom + + '@better-auth/utils@0.2.6': + dependencies: + uncrypto: 0.1.3 + + '@better-fetch/fetch@1.1.18': {} + + '@browserbasehq/sdk@2.6.0': + dependencies: + '@types/node': 18.19.123 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@clerk/backend@2.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/shared': 3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.83.0 + cookie: 1.0.2 + standardwebhooks: 1.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - react + - react-dom + + '@clerk/clerk-react@5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/shared': 3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.83.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + + '@clerk/express@1.7.26(express@4.21.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/backend': 2.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/shared': 3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.83.0 + express: 4.21.2 + tslib: 2.8.1 + transitivePeerDependencies: + - react + - react-dom + + '@clerk/shared@3.23.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/types': 4.83.0 + dequal: 2.0.3 + glob-to-regexp: 0.4.1 + js-cookie: 3.0.5 + std-env: 3.9.0 + swr: 2.3.4(react@18.3.1) + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@clerk/types@4.83.0': + dependencies: + csstype: 3.1.3 + + '@clickhouse/client-common@1.12.1': {} + + '@clickhouse/client@1.12.1': + dependencies: + '@clickhouse/client-common': 1.12.1 + + '@colors/colors@1.6.0': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@date-fns/tz@1.4.1': {} + + '@date-fns/utc@2.1.1': {} + + '@drizzle-team/brocli@0.10.2': {} + + '@emnapi/runtime@1.5.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/is-prop-valid@0.7.3': + dependencies: + '@emotion/memoize': 0.7.1 + + '@emotion/memoize@0.7.1': {} + + '@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': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.10.1 + + '@esbuild/aix-ppc64@0.25.4': + optional: true + + '@esbuild/aix-ppc64@0.25.9': + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.25.4': + optional: true + + '@esbuild/android-arm64@0.25.9': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.25.4': + optional: true + + '@esbuild/android-arm@0.25.9': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.25.4': + optional: true + + '@esbuild/android-x64@0.25.9': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.25.4': + optional: true + + '@esbuild/darwin-arm64@0.25.9': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.25.4': + optional: true + + '@esbuild/darwin-x64@0.25.9': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.25.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.9': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.25.4': + optional: true + + '@esbuild/freebsd-x64@0.25.9': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.25.4': + optional: true + + '@esbuild/linux-arm64@0.25.9': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.25.4': + optional: true + + '@esbuild/linux-arm@0.25.9': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.25.4': + optional: true + + '@esbuild/linux-ia32@0.25.9': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.25.4': + optional: true + + '@esbuild/linux-loong64@0.25.9': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.25.4': + optional: true + + '@esbuild/linux-mips64el@0.25.9': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.25.4': + optional: true + + '@esbuild/linux-ppc64@0.25.9': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.25.4': + optional: true + + '@esbuild/linux-riscv64@0.25.9': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.25.4': + optional: true + + '@esbuild/linux-s390x@0.25.9': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.25.4': + optional: true + + '@esbuild/linux-x64@0.25.9': + optional: true + + '@esbuild/netbsd-arm64@0.25.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.9': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.25.4': + optional: true + + '@esbuild/netbsd-x64@0.25.9': + optional: true + + '@esbuild/openbsd-arm64@0.25.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.9': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.25.4': + optional: true + + '@esbuild/openbsd-x64@0.25.9': + optional: true + + '@esbuild/openharmony-arm64@0.25.9': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.25.4': + optional: true + + '@esbuild/sunos-x64@0.25.9': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.25.4': + optional: true + + '@esbuild/win32-arm64@0.25.9': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.25.4': + optional: true + + '@esbuild/win32-ia32@0.25.9': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.25.4': + optional: true + + '@esbuild/win32-x64@0.25.9': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.34.0(jiti@2.5.1))': + dependencies: + eslint: 9.34.0(jiti@2.5.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.21.0': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1(supports-color@5.5.0) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.3.1': {} + + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1(supports-color@5.5.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.34.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.5': + dependencies: + '@eslint/core': 0.15.2 + levn: 0.4.1 + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/utils@0.2.10': {} + + '@fortawesome/fontawesome-common-types@6.7.2': {} + + '@fortawesome/fontawesome-common-types@7.0.0': {} + + '@fortawesome/fontawesome-svg-core@7.0.0': + dependencies: + '@fortawesome/fontawesome-common-types': 7.0.0 + + '@fortawesome/free-brands-svg-icons@6.7.2': + dependencies: + '@fortawesome/fontawesome-common-types': 6.7.2 + + '@fortawesome/react-fontawesome@0.2.6(@fortawesome/fontawesome-svg-core@7.0.0)(react@18.3.1)': + dependencies: + '@fortawesome/fontawesome-svg-core': 7.0.0 + prop-types: 15.8.1 + react: 18.3.1 + + '@grpc/grpc-js@1.13.4': + dependencies: + '@grpc/proto-loader': 0.7.15 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + + '@heroicons/react@2.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@hexagon/base64@1.1.28': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@hyperbrowser/sdk@0.54.0': + dependencies: + form-data: 4.0.4 + node-fetch: 2.7.0 + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + transitivePeerDependencies: + - encoding + + '@hyperdx/instrumentation-exception@0.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@hyperdx/instrumentation-sentry-node': 0.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@sentry/core': 8.55.0 + '@sentry/types': 8.55.0 + '@sentry/utils': 8.55.0 + json-stringify-safe: 5.0.1 + shimmer: 1.2.1 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@hyperdx/instrumentation-sentry-node@0.1.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + json-stringify-safe: 5.0.1 + shimmer: 1.2.1 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color + + '@hyperdx/node-opentelemetry@0.8.2': + dependencies: + '@hyperdx/instrumentation-exception': 0.1.0(@opentelemetry/api@1.9.0) + '@hyperdx/instrumentation-sentry-node': 0.1.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/auto-instrumentations-node': 0.56.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-runtime-node': 0.12.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + cli-spinners: 2.9.2 + json-stringify-safe: 5.0.1 + lodash.isobject: 3.0.2 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + node-fetch: 2.7.0 + open: 8.4.2 + ora: 5.4.1 + pino-abstract-transport: 1.2.0 + semver: 7.7.2 + shimmer: 1.2.1 + tslib: 2.8.1 + winston-transport: 4.9.0 + transitivePeerDependencies: + - encoding + - supports-color + + '@img/sharp-darwin-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.0 + optional: true + + '@img/sharp-darwin-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.0 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.0': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.0': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.0': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.0': + optional: true + + '@img/sharp-linux-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.0 + optional: true + + '@img/sharp-linux-arm@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.0 + optional: true + + '@img/sharp-linux-ppc64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.0 + optional: true + + '@img/sharp-linux-s390x@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.0 + optional: true + + '@img/sharp-linux-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.3': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + optional: true + + '@img/sharp-wasm32@0.34.3': + dependencies: + '@emnapi/runtime': 1.5.0 + optional: true + + '@img/sharp-win32-arm64@0.34.3': + optional: true + + '@img/sharp-win32-ia32@0.34.3': + optional: true + + '@img/sharp-win32-x64@0.34.3': + optional: true + + '@inquirer/checkbox@4.2.2(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/checkbox@4.2.2(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/confirm@5.1.16(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/confirm@5.1.16(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/core@10.2.0(@types/node@22.18.0)': + dependencies: + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.0) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/core@10.2.0(@types/node@24.3.0)': + dependencies: + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/editor@4.2.18(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/external-editor': 1.0.1(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/editor@4.2.18(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/external-editor': 1.0.1(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/expand@4.0.18(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/expand@4.0.18(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/external-editor@1.0.1(@types/node@22.18.0)': + dependencies: + chardet: 2.1.0 + iconv-lite: 0.6.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/external-editor@1.0.1(@types/node@24.3.0)': + dependencies: + chardet: 2.1.0 + iconv-lite: 0.6.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/figures@1.0.13': {} + + '@inquirer/input@4.2.2(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/input@4.2.2(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/number@3.0.18(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/number@3.0.18(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/password@4.0.18(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/password@4.0.18(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/prompts@7.8.4(@types/node@22.18.0)': + dependencies: + '@inquirer/checkbox': 4.2.2(@types/node@22.18.0) + '@inquirer/confirm': 5.1.16(@types/node@22.18.0) + '@inquirer/editor': 4.2.18(@types/node@22.18.0) + '@inquirer/expand': 4.0.18(@types/node@22.18.0) + '@inquirer/input': 4.2.2(@types/node@22.18.0) + '@inquirer/number': 3.0.18(@types/node@22.18.0) + '@inquirer/password': 4.0.18(@types/node@22.18.0) + '@inquirer/rawlist': 4.1.6(@types/node@22.18.0) + '@inquirer/search': 3.1.1(@types/node@22.18.0) + '@inquirer/select': 4.3.2(@types/node@22.18.0) + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/prompts@7.8.4(@types/node@24.3.0)': + dependencies: + '@inquirer/checkbox': 4.2.2(@types/node@24.3.0) + '@inquirer/confirm': 5.1.16(@types/node@24.3.0) + '@inquirer/editor': 4.2.18(@types/node@24.3.0) + '@inquirer/expand': 4.0.18(@types/node@24.3.0) + '@inquirer/input': 4.2.2(@types/node@24.3.0) + '@inquirer/number': 3.0.18(@types/node@24.3.0) + '@inquirer/password': 4.0.18(@types/node@24.3.0) + '@inquirer/rawlist': 4.1.6(@types/node@24.3.0) + '@inquirer/search': 3.1.1(@types/node@24.3.0) + '@inquirer/select': 4.3.2(@types/node@24.3.0) + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/rawlist@4.1.6(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/rawlist@4.1.6(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/search@3.1.1(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/search@3.1.1(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/select@4.3.2(@types/node@22.18.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@22.18.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/select@4.3.2(@types/node@24.3.0)': + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.3.0 + + '@inquirer/type@3.0.8(@types/node@22.18.0)': + optionalDependencies: + '@types/node': 22.18.0 + + '@inquirer/type@3.0.8(@types/node@24.3.0)': + optionalDependencies: + '@types/node': 24.3.0 + + '@ioredis/commands@1.3.1': {} + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.30 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.30': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@js-sdsl/ordered-map@4.4.2': {} + + '@levischuck/tiny-cbor@0.2.11': {} + + '@logtail/core@0.5.4': + dependencies: + '@logtail/tools': 0.5.4 + '@logtail/types': 0.5.3 + serialize-error: 8.1.0 + + '@logtail/node@0.5.5': + dependencies: + '@logtail/core': 0.5.4 + '@logtail/types': 0.5.3 + '@msgpack/msgpack': 2.8.0 + '@types/stack-trace': 0.0.33 + minimatch: 9.0.5 + stack-trace: 0.0.10 + + '@logtail/tools@0.5.4': + dependencies: + '@logtail/types': 0.5.3 + + '@logtail/types@0.5.3': {} + + '@msgpack/msgpack@2.8.0': {} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@next/env@15.5.2': {} + + '@next/swc-darwin-arm64@15.5.2': + optional: true + + '@next/swc-darwin-x64@15.5.2': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.2': + optional: true + + '@next/swc-linux-arm64-musl@15.5.2': + optional: true + + '@next/swc-linux-x64-gnu@15.5.2': + optional: true + + '@next/swc-linux-x64-musl@15.5.2': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.2': + optional: true + + '@next/swc-win32-x64-msvc@15.5.2': + optional: true + + '@noble/ciphers@0.6.0': {} + + '@noble/hashes@1.8.0': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@opentelemetry/api-logs@0.202.0': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api-logs@0.51.1': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api-logs@0.57.2': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/auto-instrumentations-node@0.56.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-aws-lambda': 0.50.3(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-aws-sdk': 0.49.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-bunyan': 0.45.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-cassandra-driver': 0.45.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-cucumber': 0.14.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.16.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dns': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.44.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.19.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-grpc': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.45.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-memcached': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.45.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.45.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-net': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.51.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pino': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-restify': 0.45.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-router': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-socket.io': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.18.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.10.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-winston': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-alibaba-cloud': 0.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-aws': 1.12.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-azure': 0.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-container': 0.6.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-gcp': 0.33.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - encoding + - supports-color + + '@opentelemetry/auto-instrumentations-node@0.60.1(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.49.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-aws-lambda': 0.53.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-aws-sdk': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-bunyan': 0.48.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-cassandra-driver': 0.48.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-cucumber': 0.17.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.19.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dns': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.51.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.22.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.50.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-grpc': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.49.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.50.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.11.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.50.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-memcached': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.55.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.49.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.48.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.48.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.48.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-net': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-oracledb': 0.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.54.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pino': 0.49.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.49.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.49.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-restify': 0.48.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-router': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-runtime-node': 0.16.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-socket.io': 0.49.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.21.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.13.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-winston': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-alibaba-cloud': 0.31.3(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-aws': 2.3.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-azure': 0.9.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-container': 0.7.3(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-gcp': 0.36.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - encoding + - supports-color + + '@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/exporter-logs-otlp-grpc@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.202.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-grpc@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-http@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.202.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-http@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-proto@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-logs-otlp-proto@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-grpc@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-grpc@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-http@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-http@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-proto@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-metrics-otlp-proto@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-prometheus@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-prometheus@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-grpc@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-grpc@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-http@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-http@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-proto@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-trace-otlp-proto@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/exporter-zipkin@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/exporter-zipkin@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/instrumentation-amqplib@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-amqplib@0.49.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-aws-lambda@0.50.3(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/aws-lambda': 8.10.147 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-aws-lambda@0.53.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/aws-lambda': 8.10.150 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-aws-sdk@0.49.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/propagation-utils': 0.30.16(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-aws-sdk@0.54.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/propagation-utils': 0.31.3(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-bunyan@0.45.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@types/bunyan': 1.8.11 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-bunyan@0.48.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@types/bunyan': 1.8.11 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-cassandra-driver@0.45.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-cassandra-driver@0.48.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-connect@0.43.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/connect': 3.4.38 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-connect@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/connect': 3.4.38 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-cucumber@0.14.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-cucumber@0.17.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dataloader@0.16.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dataloader@0.19.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dns@0.43.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-dns@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-express@0.47.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-express@0.51.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fastify@0.44.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fastify@0.47.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fs@0.19.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-fs@0.22.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-generic-pool@0.43.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-generic-pool@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-graphql@0.47.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-graphql@0.50.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-grpc@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-grpc@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-hapi@0.45.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-hapi@0.49.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + forwarded-parse: 2.1.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + forwarded-parse: 2.1.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.47.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-ioredis@0.50.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.38.0 + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-kafkajs@0.11.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-kafkajs@0.7.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-knex@0.44.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-knex@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-koa@0.47.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-koa@0.50.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-lru-memoizer@0.44.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-lru-memoizer@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-memcached@0.43.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/memcached': 2.2.10 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-memcached@0.46.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/memcached': 2.2.10 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongodb@0.52.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongodb@0.55.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongoose@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mongoose@0.49.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql2@0.45.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql2@0.48.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@opentelemetry/sql-common': 0.41.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql@0.45.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/mysql': 2.15.26 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-mysql@0.48.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/mysql': 2.15.27 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.44.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-nestjs-core@0.48.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-net@0.43.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-net@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-oracledb@0.28.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/oracledb': 6.5.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.51.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) + '@types/pg': 8.6.1 + '@types/pg-pool': 2.0.6 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pg@0.54.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@opentelemetry/sql-common': 0.41.0(@opentelemetry/api@1.9.0) + '@types/pg': 8.15.4 + '@types/pg-pool': 2.0.6 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pino@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-pino@0.49.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis-4@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis-4@0.49.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.37.0 + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.36.2 + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-redis@0.49.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/redis-common': 0.37.0 + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-restify@0.45.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-restify@0.48.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-router@0.44.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-router@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-runtime-node@0.12.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-runtime-node@0.16.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-socket.io@0.46.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-socket.io@0.49.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-tedious@0.18.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/tedious': 4.0.14 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-tedious@0.21.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@types/tedious': 4.0.14 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-undici@0.10.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-undici@0.13.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-winston@0.44.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-winston@0.47.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + import-in-the-middle: 1.14.2 + require-in-the-middle: 7.5.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.51.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.7.4 + require-in-the-middle: 7.5.2 + semver: 7.7.2 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.14.2 + require-in-the-middle: 7.5.2 + semver: 7.7.2 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/otlp-exporter-base@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-exporter-base@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-grpc-exporter-base@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.202.0(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-grpc-exporter-base@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@grpc/grpc-js': 1.13.4 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.57.2(@opentelemetry/api@1.9.0) + + '@opentelemetry/otlp-transformer@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/otlp-transformer@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + + '@opentelemetry/propagation-utils@0.30.16(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/propagation-utils@0.31.3(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/propagator-b3@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/propagator-b3@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/propagator-jaeger@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/propagator-jaeger@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/redis-common@0.36.2': {} + + '@opentelemetry/redis-common@0.37.0': {} + + '@opentelemetry/redis-common@0.38.0': {} + + '@opentelemetry/resource-detector-alibaba-cloud@0.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-alibaba-cloud@0.31.3(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-aws@1.12.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-aws@2.3.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-azure@0.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-azure@0.9.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-container@0.6.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-container@0.7.3(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/resource-detector-gcp@0.33.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + gcp-metadata: 6.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@opentelemetry/resource-detector-gcp@0.36.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + gcp-metadata: 6.1.1 + transitivePeerDependencies: + - encoding + - supports-color + + '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/resources@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/sdk-logs@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-metrics@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sdk-node@0.202.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.202.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-grpc': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-proto': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-prometheus': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-zipkin': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-b3': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-jaeger': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.202.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/sdk-node@0.57.2(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.57.2 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-grpc': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-proto': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-metrics-otlp-proto': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-prometheus': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-grpc': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-trace-otlp-proto': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-zipkin': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.28.0 + + '@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + + '@opentelemetry/sdk-trace-node@1.30.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + semver: 7.7.2 + + '@opentelemetry/sdk-trace-node@2.0.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/semantic-conventions@1.28.0': {} + + '@opentelemetry/semantic-conventions@1.36.0': {} + + '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + + '@opentelemetry/sql-common@0.41.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) + + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + + '@peculiar/asn1-android@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/asn1-x509': 2.4.0 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/asn1-x509': 2.4.0 + asn1js: 3.0.6 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.4.0': + dependencies: + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.4.0': + dependencies: + '@peculiar/asn1-schema': 2.4.0 + asn1js: 3.0.6 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@posthog/core@1.0.2': {} + + '@prisma/instrumentation@6.11.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@puppeteer/browsers@2.10.8': + dependencies: + debug: 4.4.1(supports-color@5.5.0) + extract-zip: 2.0.1 + progress: 2.0.3 + proxy-agent: 6.5.0 + semver: 7.7.2 + tar-fs: 3.1.0 + yargs: 17.7.2 + transitivePeerDependencies: + - bare-buffer + - supports-color + + '@radix-ui/number@1.1.1': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-accordion@1.2.12(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-checkbox@1.3.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-context@1.1.2(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-direction@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-id@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-popover@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-select@2.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-slot@1.2.3(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-switch@1.2.6(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.24)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.24)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.24 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + '@types/react-dom': 18.3.7(@types/react@18.3.24) + + '@radix-ui/rect@1.1.1': {} + + '@react-email/body@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/button@0.0.19(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/code-block@0.0.13(react@18.3.1)': + dependencies: + prismjs: 1.30.0 + react: 18.3.1 + + '@react-email/code-inline@0.0.5(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/column@0.0.13(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/components@0.0.42(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@react-email/body': 0.0.11(react@18.3.1) + '@react-email/button': 0.0.19(react@18.3.1) + '@react-email/code-block': 0.0.13(react@18.3.1) + '@react-email/code-inline': 0.0.5(react@18.3.1) + '@react-email/column': 0.0.13(react@18.3.1) + '@react-email/container': 0.0.15(react@18.3.1) + '@react-email/font': 0.0.9(react@18.3.1) + '@react-email/head': 0.0.12(react@18.3.1) + '@react-email/heading': 0.0.15(react@18.3.1) + '@react-email/hr': 0.0.11(react@18.3.1) + '@react-email/html': 0.0.11(react@18.3.1) + '@react-email/img': 0.0.11(react@18.3.1) + '@react-email/link': 0.0.12(react@18.3.1) + '@react-email/markdown': 0.0.15(react@18.3.1) + '@react-email/preview': 0.0.13(react@18.3.1) + '@react-email/render': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-email/row': 0.0.12(react@18.3.1) + '@react-email/section': 0.0.16(react@18.3.1) + '@react-email/tailwind': 1.0.5(react@18.3.1) + '@react-email/text': 0.1.5(react@18.3.1) + react: 18.3.1 + transitivePeerDependencies: + - react-dom + + '@react-email/container@0.0.15(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/font@0.0.9(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/head@0.0.12(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/heading@0.0.15(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/hr@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/html@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/img@0.0.11(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/link@0.0.12(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/markdown@0.0.15(react@18.3.1)': + dependencies: + md-to-react-email: 5.0.5(react@18.3.1) + react: 18.3.1 + + '@react-email/preview@0.0.13(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/render@1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + html-to-text: 9.0.5 + prettier: 3.6.2 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-promise-suspense: 0.3.4 + + '@react-email/row@0.0.12(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/section@0.0.16(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/tailwind@1.0.5(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@react-email/text@0.1.5(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@reduxjs/toolkit@2.8.2(react-redux@9.2.0(@types/react@18.3.24)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 10.1.1 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 18.3.1 + react-redux: 9.2.0(@types/react@18.3.24)(react@18.3.1)(redux@5.0.1) + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.50.0': + optional: true + + '@rollup/rollup-android-arm64@4.50.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.50.0': + optional: true + + '@rollup/rollup-darwin-x64@4.50.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.50.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.50.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.50.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.50.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.50.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.50.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.50.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.50.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.50.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.50.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.50.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.50.0': + optional: true + + '@selderee/plugin-htmlparser2@0.11.0': + dependencies: + domhandler: 5.0.3 + selderee: 0.11.0 + + '@sentry/core@8.55.0': {} + + '@sentry/core@9.46.0': {} + + '@sentry/node-core@9.46.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.36.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@sentry/core': 9.46.0 + '@sentry/opentelemetry': 9.46.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.36.0) + import-in-the-middle: 1.14.2 + + '@sentry/node@9.46.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.16.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.19.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.43.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.45.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.7.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.47.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.44.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.45.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.45.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.51.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.46.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.18.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.10.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@prisma/instrumentation': 6.11.1(@opentelemetry/api@1.9.0) + '@sentry/core': 9.46.0 + '@sentry/node-core': 9.46.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.36.0) + '@sentry/opentelemetry': 9.46.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.36.0) + import-in-the-middle: 1.14.2 + minimatch: 9.0.5 + transitivePeerDependencies: + - supports-color + + '@sentry/opentelemetry@9.46.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.36.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.36.0 + '@sentry/core': 9.46.0 + + '@sentry/types@8.55.0': + dependencies: + '@sentry/core': 8.55.0 + + '@sentry/utils@8.55.0': + dependencies: + '@sentry/core': 8.55.0 + + '@simplewebauthn/browser@13.1.2': {} + + '@simplewebauthn/server@13.1.2': + dependencies: + '@hexagon/base64': 1.1.28 + '@levischuck/tiny-cbor': 0.2.11 + '@peculiar/asn1-android': 2.4.0 + '@peculiar/asn1-ecc': 2.4.0 + '@peculiar/asn1-rsa': 2.4.0 + '@peculiar/asn1-schema': 2.4.0 + '@peculiar/asn1-x509': 2.4.0 + + '@socket.io/component-emitter@3.1.2': {} + + '@stablelib/base64@1.0.1': {} + + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@supabase/auth-js@2.71.1': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/functions-js@2.4.5': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/node-fetch@2.6.15': + dependencies: + whatwg-url: 5.0.0 + + '@supabase/postgrest-js@1.21.3': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/realtime-js@2.15.4': + dependencies: + '@supabase/node-fetch': 2.6.15 + '@types/phoenix': 1.6.6 + '@types/ws': 8.18.1 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@supabase/ssr@0.5.2(@supabase/supabase-js@2.56.1)': + dependencies: + '@supabase/supabase-js': 2.56.1 + '@types/cookie': 0.6.0 + cookie: 0.7.2 + + '@supabase/storage-js@2.11.0': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/supabase-js@2.56.1': + dependencies: + '@supabase/auth-js': 2.71.1 + '@supabase/functions-js': 2.4.5 + '@supabase/node-fetch': 2.6.15 + '@supabase/postgrest-js': 1.21.3 + '@supabase/realtime-js': 2.15.4 + '@supabase/storage-js': 2.11.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.1.12': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + magic-string: 0.30.18 + source-map-js: 1.2.1 + tailwindcss: 4.1.12 + + '@tailwindcss/oxide-android-arm64@4.1.12': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.12': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.12': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + optional: true + + '@tailwindcss/oxide@4.1.12': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-x64': 4.1.12 + '@tailwindcss/oxide-freebsd-x64': 4.1.12 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.12 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.12 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-x64-musl': 4.1.12 + '@tailwindcss/oxide-wasm32-wasi': 4.1.12 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 + + '@tailwindcss/vite@4.1.12(vite@6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + dependencies: + '@tailwindcss/node': 4.1.12 + '@tailwindcss/oxide': 4.1.12 + tailwindcss: 4.1.12 + vite: 6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + + '@tanstack/query-core@5.87.1': {} + + '@tanstack/query-devtools@5.86.0': {} + + '@tanstack/react-query-devtools@5.87.1(@tanstack/react-query@5.87.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/query-devtools': 5.86.0 + '@tanstack/react-query': 5.87.1(react@18.3.1) + react: 18.3.1 + + '@tanstack/react-query@5.87.1(react@18.3.1)': + dependencies: + '@tanstack/query-core': 5.87.1 + react: 18.3.1 + + '@tanstack/react-table@8.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/table-core': 8.21.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@tanstack/table-core@8.21.3': {} + + '@tootallnate/quickjs-emscripten@0.23.0': {} + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/aws-lambda@8.10.147': {} + + '@types/aws-lambda@8.10.150': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.3 + '@babel/types': 7.28.2 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.18.0 + + '@types/bun@1.2.21(@types/react@18.3.24)': + dependencies: + bun-types: 1.2.21(@types/react@18.3.24) + transitivePeerDependencies: + - '@types/react' + + '@types/bunyan@1.8.11': + dependencies: + '@types/node': 22.18.0 + + '@types/chai-http@3.0.5': + dependencies: + '@types/chai': 5.2.2 + '@types/node': 22.18.0 + '@types/superagent': 8.1.9 + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.18.0 + + '@types/cookie@0.6.0': {} + + '@types/cookiejar@2.1.5': {} + + '@types/cors@2.8.19': + dependencies: + '@types/node': 22.18.0 + + '@types/d3-array@3.2.1': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/deep-eql@4.0.2': {} + + '@types/diff-match-patch@1.0.36': {} + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@5.0.7': + dependencies: + '@types/node': 22.18.0 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.0.7 + '@types/serve-static': 1.15.8 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/history@4.7.11': {} + + '@types/http-errors@2.0.5': {} + + '@types/json-schema@7.0.15': {} + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.20 + + '@types/lodash@4.17.20': {} + + '@types/luxon@3.4.2': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/memcached@2.2.10': + dependencies: + '@types/node': 22.18.0 + + '@types/methods@1.1.4': {} + + '@types/mime@1.3.5': {} + + '@types/mocha@10.0.10': {} + + '@types/mysql@2.15.26': + dependencies: + '@types/node': 22.18.0 + + '@types/mysql@2.15.27': + dependencies: + '@types/node': 22.18.0 + + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 22.18.0 + form-data: 4.0.4 + + '@types/node@18.19.123': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.18.0': + dependencies: + undici-types: 6.21.0 + + '@types/node@24.3.0': + dependencies: + undici-types: 7.10.0 + + '@types/oracledb@6.5.2': + dependencies: + '@types/node': 22.18.0 + + '@types/pg-pool@2.0.6': + dependencies: + '@types/pg': 8.15.5 + + '@types/pg@8.15.4': + dependencies: + '@types/node': 22.18.0 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + + '@types/pg@8.15.5': + dependencies: + '@types/node': 22.18.0 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + + '@types/pg@8.6.1': + dependencies: + '@types/node': 22.18.0 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + + '@types/phoenix@1.6.6': {} + + '@types/prismjs@1.26.5': {} + + '@types/prop-types@15.7.15': {} + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@18.3.7(@types/react@18.3.24)': + dependencies: + '@types/react': 18.3.24 + + '@types/react-router-dom@5.3.3': + dependencies: + '@types/history': 4.7.11 + '@types/react': 18.3.24 + '@types/react-router': 5.1.20 + + '@types/react-router@5.1.20': + dependencies: + '@types/history': 4.7.11 + '@types/react': 18.3.24 + + '@types/react@18.3.24': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.1.3 + + '@types/recharts@2.0.1(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(redux@5.0.1)': + dependencies: + recharts: 3.1.2(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(redux@5.0.1) + transitivePeerDependencies: + - '@types/react' + - react + - react-dom + - react-is + - redux + + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 22.18.0 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.18.0 + '@types/send': 0.17.5 + + '@types/shimmer@1.2.0': {} + + '@types/stack-trace@0.0.33': {} + + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 22.18.0 + form-data: 4.0.4 + + '@types/tedious@4.0.14': + dependencies: + '@types/node': 22.18.0 + + '@types/triple-beam@1.3.5': {} + + '@types/unist@3.0.3': {} + + '@types/use-sync-external-store@0.0.6': {} + + '@types/ws@8.18.1': + dependencies: + '@types/node': 22.18.0 + + '@types/yauzl@2.10.3': + dependencies: + '@types/node': 22.18.0 + optional: true + + '@typescript-eslint/eslint-plugin@8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3))(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + '@typescript-eslint/scope-manager': 8.41.0 + '@typescript-eslint/type-utils': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + '@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.41.0 + eslint: 9.34.0(jiti@2.5.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.41.0 + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.7.3) + '@typescript-eslint/visitor-keys': 8.41.0 + debug: 4.4.1(supports-color@5.5.0) + eslint: 9.34.0(jiti@2.5.1) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.41.0(typescript@5.7.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.7.3) + '@typescript-eslint/types': 8.41.0 + debug: 4.4.1(supports-color@5.5.0) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.41.0': + dependencies: + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/visitor-keys': 8.41.0 + + '@typescript-eslint/tsconfig-utils@8.41.0(typescript@5.7.3)': + dependencies: + typescript: 5.7.3 + + '@typescript-eslint/type-utils@8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3)': + dependencies: + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + debug: 4.4.1(supports-color@5.5.0) + eslint: 9.34.0(jiti@2.5.1) + ts-api-utils: 2.1.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.41.0': {} + + '@typescript-eslint/typescript-estree@8.41.0(typescript@5.7.3)': + dependencies: + '@typescript-eslint/project-service': 8.41.0(typescript@5.7.3) + '@typescript-eslint/tsconfig-utils': 8.41.0(typescript@5.7.3) + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/visitor-keys': 8.41.0 + debug: 4.4.1(supports-color@5.5.0) + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.7.3) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.5.1)) + '@typescript-eslint/scope-manager': 8.41.0 + '@typescript-eslint/types': 8.41.0 + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.7.3) + eslint: 9.34.0(jiti@2.5.1) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.41.0': + dependencies: + '@typescript-eslint/types': 8.41.0 + eslint-visitor-keys: 4.2.1 + + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview-darwin-x64@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview-linux-arm64@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview-linux-arm@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview-linux-x64@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview-win32-arm64@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview-win32-x64@7.0.0-dev.20250901.1': + optional: true + + '@typescript/native-preview@7.0.0-dev.20250901.1': + optionalDependencies: + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20250901.1 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20250901.1 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20250901.1 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20250901.1 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20250901.1 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20250901.1 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20250901.1 + + '@ungap/structured-clone@1.3.0': {} + + '@upstash/redis@1.35.3': + dependencies: + uncrypto: 0.1.3 + + '@vitejs/plugin-react@4.7.0(vite@6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5))': + dependencies: + '@babel/core': 7.28.3 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.3) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5) + transitivePeerDependencies: + - supports-color + + '@wooorm/starry-night@3.8.0': + dependencies: + '@types/hast': 3.0.4 + import-meta-resolve: 4.2.0 + vscode-oniguruma: 2.0.1 + vscode-textmate: 9.2.0 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ag-charts-community@12.1.2: + dependencies: + ag-charts-core: 12.1.2 + ag-charts-locale: 12.1.2 + ag-charts-types: 12.1.2 + + ag-charts-core@12.1.2: + dependencies: + ag-charts-types: 12.1.2 + + ag-charts-locale@12.1.2: {} + + ag-charts-react@12.1.2(react@18.3.1): + dependencies: + ag-charts-community: 12.1.2 + react: 18.3.1 + + ag-charts-types@12.1.2: {} + + ag-grid-community@34.1.2: + dependencies: + ag-charts-types: 12.1.2 + + ag-grid-react@34.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + ag-grid-community: 34.1.2 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + agent-base@7.1.4: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + ai@4.3.19(react@18.3.1)(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/react': 1.2.12(react@18.3.1)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.76 + optionalDependencies: + react: 18.3.1 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@4.1.3: {} + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + array-flatten@1.1.1: {} + + array-union@2.1.0: {} + + asap@2.0.6: {} + + asn1js@3.0.6: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.3 + tslib: 2.8.1 + + assertion-error@2.0.1: {} + + ast-types@0.13.4: + dependencies: + tslib: 2.8.1 + + async-listen@3.1.0: {} + + asynckit@0.4.0: {} + + atomic-sleep@1.0.0: {} + + attr-accept@2.2.5: {} + + autumn-js@0.1.28(better-auth@1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76))(better-call@1.0.16)(convex@1.26.2(@clerk/clerk-react@5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1))(react@18.3.1): + dependencies: + convex: 1.26.2(@clerk/clerk-react@5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + query-string: 9.2.2 + rou3: 0.6.3 + swr: 2.3.6(react@18.3.1) + zod: 4.1.5 + optionalDependencies: + better-auth: 1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76) + better-call: 1.0.16 + react: 18.3.1 + + axios@1.11.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + b4a@1.6.7: {} + + balanced-match@1.0.2: {} + + bare-events@2.6.1: + optional: true + + bare-fs@4.2.1: + dependencies: + bare-events: 2.6.1 + bare-path: 3.0.0 + bare-stream: 2.7.0(bare-events@2.6.1) + optional: true + + bare-os@3.6.2: + optional: true + + bare-path@3.0.0: + dependencies: + bare-os: 3.6.2 + optional: true + + bare-stream@2.7.0(bare-events@2.6.1): + dependencies: + streamx: 2.22.1 + optionalDependencies: + bare-events: 2.6.1 + optional: true + + base-convert-int-array@1.0.1: {} + + base64-js@1.5.1: {} + + base64id@2.0.0: {} + + basic-ftp@5.0.5: {} + + better-auth@1.3.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod@3.25.76): + dependencies: + '@better-auth/utils': 0.2.6 + '@better-fetch/fetch': 1.1.18 + '@noble/ciphers': 0.6.0 + '@noble/hashes': 1.8.0 + '@simplewebauthn/browser': 13.1.2 + '@simplewebauthn/server': 13.1.2 + better-call: 1.0.16 + defu: 6.1.4 + jose: 5.10.0 + kysely: 0.28.5 + nanostores: 0.11.4 + zod: 3.25.76 + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + better-call@1.0.16: + dependencies: + '@better-fetch/fetch': 1.1.18 + rou3: 0.5.1 + set-cookie-parser: 2.7.1 + uncrypto: 0.1.3 + + bignumber.js@9.3.1: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@1.20.3: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.13.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-stdout@1.3.1: {} + + browserslist@4.25.4: + dependencies: + caniuse-lite: 1.0.30001739 + electron-to-chromium: 1.5.211 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.4) + + buffer-crc32@0.2.13: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bullmq@5.58.4: + dependencies: + cron-parser: 4.9.0 + ioredis: 5.7.0 + msgpackr: 1.11.5 + node-abort-controller: 3.1.1 + semver: 7.7.2 + tslib: 2.8.1 + uuid: 9.0.1 + transitivePeerDependencies: + - supports-color + + bun-types@1.2.21(@types/react@18.3.24): + dependencies: + '@types/node': 24.3.0 + '@types/react': 18.3.24 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001739: {} + + ccount@2.0.1: {} + + chai-http@5.1.2: + dependencies: + '@types/superagent': 8.1.9 + charset: 1.0.1 + cookiejar: 2.1.4 + is-ip: 5.0.1 + methods: 1.1.2 + qs: 6.14.0 + superagent: 10.2.3 + transitivePeerDependencies: + - supports-color + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.0: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chardet@2.1.0: {} + + charset@1.0.1: {} + + check-error@2.1.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@3.0.0: {} + + chromium-bidi@8.0.0(devtools-protocol@0.0.1475386): + dependencies: + devtools-protocol: 0.0.1475386 + mitt: 3.0.1 + zod: 3.25.76 + + cjs-module-lexer@1.4.3: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cli-width@4.1.0: {} + + client-only@0.0.1: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone-regexp@3.0.0: + dependencies: + is-regexp: 3.1.0 + + clone@1.0.4: {} + + cloudflare@4.5.0: + dependencies: + '@types/node': 18.19.123 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + clsx@2.1.1: {} + + cluster-key-slot@1.1.2: {} + + cmdk@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.24)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + optional: true + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + optional: true + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@9.5.0: {} + + component-emitter@1.3.1: {} + + concat-map@0.0.1: {} + + concurrently@9.2.1: + dependencies: + chalk: 4.1.2 + rxjs: 7.8.2 + shell-quote: 1.8.3 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-hrtime@5.0.0: {} + + convert-source-map@2.0.0: {} + + convex@1.26.2(@clerk/clerk-react@5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + dependencies: + esbuild: 0.25.4 + jwt-decode: 4.0.0 + prettier: 3.6.2 + optionalDependencies: + '@clerk/clerk-react': 5.45.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + + cookie-signature@1.0.6: {} + + cookie@0.7.1: {} + + cookie@0.7.2: {} + + cookie@1.0.2: {} + + cookiejar@2.1.4: {} + + core-js@3.45.1: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + create-require@1.1.1: {} + + cron-parser@4.9.0: + dependencies: + luxon: 3.7.1 + + cron@3.5.0: + dependencies: + '@types/luxon': 3.4.2 + luxon: 3.5.0 + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-jss@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + jss-preset-default: 10.10.0 + + css-vendor@2.0.8: + dependencies: + '@babel/runtime': 7.28.3 + is-in-browser: 1.1.3 + + csstype@3.1.3: {} + + csv-parse@5.6.0: {} + + csv-parser@3.2.0: {} + + currency-symbol-map@5.1.0: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + data-uri-to-buffer@6.0.2: {} + + date-fns@3.6.0: {} + + date-fns@4.1.0: {} + + dateformat@4.6.3: {} + + debounce@2.2.0: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + debug@4.4.1(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + debug@4.4.1(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + decamelize@4.0.0: {} + + decimal.js-light@2.5.1: {} + + decimal.js@10.6.0: {} + + decode-uri-component@0.4.1: {} + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-lazy-prop@2.0.0: {} + + defu@6.1.4: {} + + degenerator@5.0.1: + dependencies: + ast-types: 0.13.4 + escodegen: 2.1.0 + esprima: 4.0.1 + + delayed-stream@1.0.0: {} + + denque@2.1.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + destroy@1.2.0: {} + + detect-content-type@1.2.0: {} + + detect-libc@2.0.4: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + devtools-protocol@0.0.1475386: {} + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + diff-match-patch@1.0.5: {} + + diff@4.0.2: {} + + diff@7.0.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dotenv@16.6.1: {} + + drizzle-kit@0.31.4: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.9 + esbuild-register: 3.6.0(esbuild@0.25.9) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7): + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/pg': 8.15.5 + bun-types: 1.2.21(@types/react@18.3.24) + kysely: 0.28.5 + pg: 8.16.3 + postgres: 3.4.7 + + drizzle-zod@0.8.3(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7))(zod@3.25.76): + dependencies: + drizzle-orm: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.15.5)(bun-types@1.2.21(@types/react@18.3.24))(kysely@0.28.5)(pg@8.16.3)(postgres@3.4.7) + zod: 3.25.76 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.211: {} + + emoji-regex@10.5.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + engine.io-parser@5.2.3: {} + + engine.io@6.6.4: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 22.18.0 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.3 + + entities@4.5.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-toolkit@1.39.10: {} + + es6-promise@4.2.8: {} + + esbuild-register@3.6.0(esbuild@0.25.9): + dependencies: + debug: 4.4.1(supports-color@5.5.0) + esbuild: 0.25.9 + transitivePeerDependencies: + - supports-color + + 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@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + esbuild@0.25.9: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-plugin-react-hooks@5.2.0(eslint@9.34.0(jiti@2.5.1)): + dependencies: + eslint: 9.34.0(jiti@2.5.1) + + eslint-plugin-react-refresh@0.4.20(eslint@9.34.0(jiti@2.5.1)): + dependencies: + eslint: 9.34.0(jiti@2.5.1) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.34.0(jiti@2.5.1): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.34.0(jiti@2.5.1)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.34.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1(supports-color@5.5.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.5.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + express-rate-limit@7.5.1(express@4.21.2): + dependencies: + express: 4.21.2 + + express@4.21.2: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.3 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.0.6 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.1 + fresh: 0.5.2 + http-errors: 2.0.0 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.13.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.0 + serve-static: 1.16.2 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + extract-zip@2.0.1: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + get-stream: 5.2.0 + yauzl: 2.10.0 + optionalDependencies: + '@types/yauzl': 2.10.3 + transitivePeerDependencies: + - supports-color + + fast-copy@3.0.2: {} + + fast-deep-equal@2.0.1: {} + + fast-deep-equal@3.1.3: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-redact@3.5.0: {} + + fast-safe-stringify@2.1.1: {} + + fast-sha256@1.3.0: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fd-slicer@1.1.0: + dependencies: + pend: 1.2.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fecha@4.2.3: {} + + fetch-retry@6.0.0: {} + + fflate@0.4.8: {} + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-selector@2.1.2: + dependencies: + tslib: 2.8.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + filter-obj@5.1.0: {} + + finalhandler@1.3.1: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flat@5.0.2: {} + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data-encoder@1.7.2: {} + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + + forwarded-parse@2.1.2: {} + + forwarded@0.2.0: {} + + framer-motion@12.23.12(@emotion/is-prop-valid@0.7.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + motion-dom: 12.23.12 + motion-utils: 12.23.6 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.7.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + fresh@0.5.2: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function-timeout@0.1.1: {} + + gaxios@6.7.1: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@6.1.1: + dependencies: + gaxios: 6.7.1 + google-logging-utils: 0.0.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@5.2.0: + dependencies: + pump: 3.0.3 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-uri@6.0.5: + dependencies: + basic-ftp: 5.0.5 + data-uri-to-buffer: 6.0.2 + debug: 4.4.1(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.0.3: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.0.3 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + + globals@14.0.0: {} + + globals@15.15.0: {} + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + goober@2.1.16(csstype@3.1.3): + dependencies: + csstype: 3.1.3 + + google-logging-utils@0.0.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + he@1.2.0: {} + + help-me@5.0.0: {} + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + html-to-text@9.0.5: + dependencies: + '@selderee/plugin-htmlparser2': 0.11.0 + deepmerge: 4.3.1 + dom-serializer: 2.0.0 + htmlparser2: 8.0.2 + selderee: 0.11.0 + + html-void-elements@3.0.0: {} + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + http-status-codes@2.3.0: {} + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + hyphenate-style-name@1.1.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + iframe-resizer-react@1.1.1(@babel/core@7.28.3)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.28.3) + iframe-resizer: 4.4.5 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + warning: 4.0.3 + transitivePeerDependencies: + - '@babel/core' + - supports-color + + iframe-resizer@4.4.5: {} + + ignore-by-default@1.0.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immer@10.1.1: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-in-the-middle@1.14.2: + dependencies: + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + + import-in-the-middle@1.7.4: + dependencies: + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + input-otp@1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + inquirer@12.9.4(@types/node@22.18.0): + dependencies: + '@inquirer/core': 10.2.0(@types/node@22.18.0) + '@inquirer/prompts': 7.8.4(@types/node@22.18.0) + '@inquirer/type': 3.0.8(@types/node@22.18.0) + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 4.0.6 + rxjs: 7.8.2 + optionalDependencies: + '@types/node': 22.18.0 + + inquirer@12.9.4(@types/node@24.3.0): + dependencies: + '@inquirer/core': 10.2.0(@types/node@24.3.0) + '@inquirer/prompts': 7.8.4(@types/node@24.3.0) + '@inquirer/type': 3.0.8(@types/node@24.3.0) + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 4.0.6 + rxjs: 7.8.2 + optionalDependencies: + '@types/node': 24.3.0 + + internmap@2.0.3: {} + + ioredis@5.7.0: + dependencies: + '@ioredis/commands': 1.3.1 + cluster-key-slot: 1.1.2 + debug: 4.4.1(supports-color@5.5.0) + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + ip-address@10.0.1: {} + + ip-regex@5.0.0: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.3.2: + optional: true + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-browser@1.1.3: {} + + is-interactive@1.0.0: {} + + is-interactive@2.0.0: {} + + is-ip@5.0.1: + dependencies: + ip-regex: 5.0.0 + super-regex: 0.2.0 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-regexp@3.1.0: {} + + is-stream@2.0.1: {} + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + + jiti@2.5.1: {} + + jose@5.10.0: {} + + joycon@3.1.1: {} + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json-stringify-safe@5.0.1: {} + + json5@2.2.3: {} + + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.6.0 + diff-match-patch: 1.0.5 + + jss-plugin-camel-case@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + hyphenate-style-name: 1.1.0 + jss: 10.10.0 + + jss-plugin-compose@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-default-unit@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + + jss-plugin-expand@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + + jss-plugin-extend@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-global@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + + jss-plugin-nested@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-props-sort@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + + jss-plugin-rule-value-function@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-rule-value-observable@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + symbol-observable: 1.2.0 + + jss-plugin-template@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + tiny-warning: 1.0.3 + + jss-plugin-vendor-prefixer@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + css-vendor: 2.0.8 + jss: 10.10.0 + + jss-preset-default@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + jss: 10.10.0 + jss-plugin-camel-case: 10.10.0 + jss-plugin-compose: 10.10.0 + jss-plugin-default-unit: 10.10.0 + jss-plugin-expand: 10.10.0 + jss-plugin-extend: 10.10.0 + jss-plugin-global: 10.10.0 + jss-plugin-nested: 10.10.0 + jss-plugin-props-sort: 10.10.0 + jss-plugin-rule-value-function: 10.10.0 + jss-plugin-rule-value-observable: 10.10.0 + jss-plugin-template: 10.10.0 + jss-plugin-vendor-prefixer: 10.10.0 + + jss@10.10.0: + dependencies: + '@babel/runtime': 7.28.3 + csstype: 3.1.3 + is-in-browser: 1.1.3 + tiny-warning: 1.0.3 + + jwt-decode@4.0.0: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + ksuid@3.0.0: + dependencies: + base-convert-int-array: 1.0.1 + + kysely@0.28.5: {} + + leac@0.6.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.0.4 + optionalDependencies: + lightningcss-darwin-arm64: 1.30.1 + lightningcss-darwin-x64: 1.30.1 + lightningcss-freebsd-x64: 1.30.1 + lightningcss-linux-arm-gnueabihf: 1.30.1 + lightningcss-linux-arm64-gnu: 1.30.1 + lightningcss-linux-arm64-musl: 1.30.1 + lightningcss-linux-x64-gnu: 1.30.1 + lightningcss-linux-x64-musl: 1.30.1 + lightningcss-win32-arm64-msvc: 1.30.1 + lightningcss-win32-x64-msvc: 1.30.1 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash-es@4.17.21: {} + + lodash.camelcase@4.3.0: {} + + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + + lodash.isobject@3.0.2: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.6.0 + is-unicode-supported: 1.3.0 + + log-symbols@7.0.1: + dependencies: + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 + + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + + long@5.3.2: {} + + loops@5.0.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lru-cache@10.4.3: {} + + lru-cache@11.1.0: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@7.18.3: {} + + lucide-react@0.525.0(react@18.3.1): + dependencies: + react: 18.3.1 + + luxon@3.5.0: {} + + luxon@3.7.1: {} + + magic-string@0.30.18: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + make-error@1.3.6: {} + + marked@7.0.4: {} + + math-intrinsics@1.1.0: {} + + md-to-react-email@5.0.5(react@18.3.1): + dependencies: + marked: 7.0.4 + react: 18.3.1 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + media-typer@0.3.0: {} + + merge-descriptors@1.0.3: {} + + merge2@1.4.1: {} + + methods@1.1.2: {} + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-detect@1.3.0: + dependencies: + mime-db: 1.54.0 + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: {} + + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mitt@3.0.1: {} + + mkdirp@3.0.1: {} + + mocha@11.7.1: + dependencies: + browser-stdout: 1.3.1 + chokidar: 4.0.3 + debug: 4.4.1(supports-color@8.1.1) + diff: 7.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 10.4.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 9.0.5 + ms: 2.1.3 + picocolors: 1.1.1 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 9.3.3 + yargs: 17.7.2 + yargs-parser: 21.1.1 + yargs-unparser: 2.0.0 + + module-details-from-path@1.0.4: {} + + motion-dom@12.23.12: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + motion@12.23.12(@emotion/is-prop-valid@0.7.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + framer-motion: 12.23.12(@emotion/is-prop-valid@0.7.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 0.7.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + ms@2.0.0: {} + + ms@2.1.3: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.5: + optionalDependencies: + msgpackr-extract: 3.0.3 + + mute-stream@2.0.0: {} + + mylas@2.1.13: {} + + nanoid@3.3.11: {} + + nanoid@5.1.5: {} + + nanostores@0.11.4: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + netmask@2.0.2: {} + + next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + next@15.5.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 15.5.2 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001739 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.28.3)(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.2 + '@next/swc-darwin-x64': 15.5.2 + '@next/swc-linux-arm64-gnu': 15.5.2 + '@next/swc-linux-arm64-musl': 15.5.2 + '@next/swc-linux-x64-gnu': 15.5.2 + '@next/swc-linux-x64-musl': 15.5.2 + '@next/swc-win32-arm64-msvc': 15.5.2 + '@next/swc-win32-x64-msvc': 15.5.2 + '@opentelemetry/api': 1.9.0 + sharp: 0.34.3 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-abort-controller@3.1.1: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.0.4 + optional: true + + node-releases@2.0.19: {} + + nodemon@3.1.10: + dependencies: + chokidar: 3.6.0 + debug: 4.4.1(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 7.7.2 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + normalize-path@3.0.0: {} + + nuqs@2.5.2(next@15.5.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router-dom@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-router@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + dependencies: + '@standard-schema/spec': 1.0.0 + react: 18.3.1 + optionalDependencies: + next: 15.5.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router-dom: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-exit-leak-free@2.1.2: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + openai@4.104.0(ws@8.18.3)(zod@3.25.76): + dependencies: + '@types/node': 18.19.123 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + ws: 8.18.3 + zod: 3.25.76 + transitivePeerDependencies: + - encoding + + 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 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ora@8.2.0: + dependencies: + chalk: 5.6.0 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + pac-proxy-agent@7.2.0: + dependencies: + '@tootallnate/quickjs-emscripten': 0.23.0 + agent-base: 7.1.4 + debug: 4.4.1(supports-color@5.5.0) + get-uri: 6.0.5 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + pac-resolver: 7.0.1 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + pac-resolver@7.0.1: + dependencies: + degenerator: 5.0.1 + netmask: 2.0.2 + + package-json-from-dist@1.0.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parseley@0.12.1: + dependencies: + leac: 0.6.0 + peberminta: 0.9.0 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-scurry@2.0.0: + dependencies: + lru-cache: 11.1.0 + minipass: 7.1.2 + + path-to-regexp@0.1.12: {} + + path-type@4.0.0: {} + + pathval@2.0.1: {} + + peberminta@0.9.0: {} + + pend@1.2.0: {} + + pg-cloudflare@1.2.7: + optional: true + + pg-connection-string@2.9.1: {} + + pg-int8@1.0.1: {} + + pg-pool@3.10.1(pg@8.16.3): + dependencies: + pg: 8.16.3 + + pg-protocol@1.10.3: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.16.3: + dependencies: + pg-connection-string: 2.9.1 + pg-pool: 3.10.1(pg@8.16.3) + pg-protocol: 1.10.3 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.2.7 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pino-abstract-transport@1.2.0: + dependencies: + readable-stream: 4.7.0 + split2: 4.2.0 + + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.1: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pump: 3.0.3 + secure-json-parse: 4.0.0 + sonic-boom: 4.2.0 + strip-json-comments: 5.0.3 + + pino-std-serializers@7.0.0: {} + + pino@9.9.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + + plimit-lit@1.6.1: + dependencies: + queue-lit: 1.5.2 + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres@3.4.7: {} + + posthog-js@1.261.0: + dependencies: + '@posthog/core': 1.0.2 + core-js: 3.45.1 + fflate: 0.4.8 + preact: 10.27.1 + web-vitals: 4.2.4 + + posthog-node@4.18.0: + dependencies: + axios: 1.11.0 + transitivePeerDependencies: + - debug + + preact@10.27.1: {} + + prelude-ls@1.2.1: {} + + prettier@3.6.2: {} + + prism-themes@1.9.0: {} + + prismjs@1.30.0: {} + + process-warning@5.0.0: {} + + process@0.11.10: {} + + progress@2.0.3: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-information@7.1.0: {} + + protobufjs@7.5.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.18.0 + long: 5.3.2 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-agent@6.5.0: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@5.5.0) + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 7.18.3 + pac-proxy-agent: 7.2.0 + proxy-from-env: 1.1.0 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + proxy-from-env@1.1.0: {} + + pstree.remy@1.1.8: {} + + pump@3.0.3: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + punycode@2.3.1: {} + + puppeteer-core@24.18.0: + dependencies: + '@puppeteer/browsers': 2.10.8 + chromium-bidi: 8.0.0(devtools-protocol@0.0.1475386) + debug: 4.4.1(supports-color@5.5.0) + devtools-protocol: 0.0.1475386 + typed-query-selector: 2.12.0 + ws: 8.18.3 + transitivePeerDependencies: + - bare-buffer + - bufferutil + - supports-color + - utf-8-validate + + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.3: {} + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + query-string@9.2.2: + dependencies: + decode-uri-component: 0.4.1 + filter-obj: 5.1.0 + split-on-first: 3.0.0 + + querystringify@2.2.0: {} + + queue-lit@1.5.2: {} + + queue-microtask@1.2.3: {} + + quick-format-unescaped@4.0.4: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + range-parser@1.2.1: {} + + raw-body@2.5.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + react-confetti-explosion@2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-jss: 10.10.0(react@18.3.1) + + react-day-picker@8.10.1(date-fns@3.6.0)(react@18.3.1): + dependencies: + date-fns: 3.6.0 + react: 18.3.1 + + react-display-name@0.2.5: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-dropzone@14.3.8(react@18.3.1): + dependencies: + attr-accept: 2.2.5 + file-selector: 2.1.2 + prop-types: 15.8.1 + react: 18.3.1 + + react-email@4.0.16(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/parser': 7.28.3 + '@babel/traverse': 7.28.3 + chalk: 5.6.0 + chokidar: 4.0.3 + commander: 13.1.0 + debounce: 2.2.0 + esbuild: 0.25.9 + glob: 11.0.3 + log-symbols: 7.0.1 + mime-types: 3.0.1 + next: 15.5.2(@babel/core@7.28.3)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + normalize-path: 3.0.0 + ora: 8.2.0 + socket.io: 4.8.1 + transitivePeerDependencies: + - '@babel/core' + - '@opentelemetry/api' + - '@playwright/test' + - babel-plugin-macros + - babel-plugin-react-compiler + - bufferutil + - react + - react-dom + - sass + - supports-color + - utf-8-validate + + react-hot-toast@2.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + csstype: 3.1.3 + goober: 2.1.16(csstype@3.1.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-hotkeys-hook@4.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-is@16.13.1: {} + + react-jss@10.10.0(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.3 + '@emotion/is-prop-valid': 0.7.3 + css-jss: 10.10.0 + hoist-non-react-statics: 3.3.2 + is-in-browser: 1.1.3 + jss: 10.10.0 + jss-preset-default: 10.10.0 + prop-types: 15.8.1 + react: 18.3.1 + shallow-equal: 1.2.1 + theming: 3.3.0(react@18.3.1) + tiny-warning: 1.0.3 + + react-promise-suspense@0.3.4: + dependencies: + fast-deep-equal: 2.0.1 + + react-redux@9.2.0(@types/react@18.3.24)(react@18.3.1)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + redux: 5.0.1 + + react-refresh@0.17.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@18.3.24)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.3.24)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.24 + + react-remove-scroll@2.7.1(@types/react@18.3.24)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.24)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.24)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.3.24)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.24)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.24 + + react-router-dom@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-router@7.8.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.0.2 + react: 18.3.1 + set-cookie-parser: 2.7.1 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react-style-singleton@2.2.3(@types/react@18.3.24)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.24 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + real-require@0.2.0: {} + + recaseai@0.0.37(ws@8.18.3)(zod@3.25.76): + dependencies: + '@anthropic-ai/sdk': 0.32.1 + '@supabase/supabase-js': 2.56.1 + '@types/cors': 2.8.19 + '@types/express': 5.0.3 + '@types/node': 22.18.0 + async-listen: 3.1.0 + axios: 1.11.0 + commander: 12.1.0 + cors: 2.8.5 + dotenv: 16.6.1 + express: 4.21.2 + figures: 6.1.0 + inquirer: 12.9.4(@types/node@22.18.0) + ksuid: 3.0.0 + nanoid: 5.1.5 + openai: 4.104.0(ws@8.18.3)(zod@3.25.76) + ora: 8.2.0 + picocolors: 1.1.1 + pino: 9.9.0 + tsx: 4.20.5 + typescript: 5.7.3 + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - supports-color + - utf-8-validate + - ws + - zod + + recharts@3.1.2(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react-is@16.13.1)(react@18.3.1)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.8.2(react-redux@9.2.0(@types/react@18.3.24)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.39.10 + eventemitter3: 5.0.1 + immer: 10.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 16.13.1 + react-redux: 9.2.0(@types/react@18.3.24)(react@18.3.1)(redux@5.0.1) + reselect: 5.1.1 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.5.0(react@18.3.1) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + + require-directory@2.1.1: {} + + require-in-the-middle@7.5.2: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + module-details-from-path: 1.0.4 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + requires-port@1.0.0: {} + + reselect@5.1.1: {} + + resend@4.8.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@react-email/render': 1.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + transitivePeerDependencies: + - react + - react-dom + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rollup@4.50.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.50.0 + '@rollup/rollup-android-arm64': 4.50.0 + '@rollup/rollup-darwin-arm64': 4.50.0 + '@rollup/rollup-darwin-x64': 4.50.0 + '@rollup/rollup-freebsd-arm64': 4.50.0 + '@rollup/rollup-freebsd-x64': 4.50.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.50.0 + '@rollup/rollup-linux-arm-musleabihf': 4.50.0 + '@rollup/rollup-linux-arm64-gnu': 4.50.0 + '@rollup/rollup-linux-arm64-musl': 4.50.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.50.0 + '@rollup/rollup-linux-ppc64-gnu': 4.50.0 + '@rollup/rollup-linux-riscv64-gnu': 4.50.0 + '@rollup/rollup-linux-riscv64-musl': 4.50.0 + '@rollup/rollup-linux-s390x-gnu': 4.50.0 + '@rollup/rollup-linux-x64-gnu': 4.50.0 + '@rollup/rollup-linux-x64-musl': 4.50.0 + '@rollup/rollup-openharmony-arm64': 4.50.0 + '@rollup/rollup-win32-arm64-msvc': 4.50.0 + '@rollup/rollup-win32-ia32-msvc': 4.50.0 + '@rollup/rollup-win32-x64-msvc': 4.50.0 + fsevents: 2.3.3 + + rou3@0.5.1: {} + + rou3@0.6.3: {} + + run-async@4.0.6: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + secure-json-parse@2.7.0: {} + + secure-json-parse@4.0.0: {} + + selderee@0.11.0: + dependencies: + parseley: 0.12.1 + + semver@6.3.1: {} + + semver@7.7.2: {} + + send@0.19.0: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serialize-error@8.1.0: + dependencies: + type-fest: 0.20.2 + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 0.19.0 + transitivePeerDependencies: + - supports-color + + set-cookie-parser@2.7.1: {} + + setprototypeof@1.2.0: {} + + shallow-equal@1.2.1: {} + + sharp@0.34.3: + dependencies: + color: 4.2.3 + detect-libc: 2.0.4 + semver: 7.7.2 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.3 + '@img/sharp-darwin-x64': 0.34.3 + '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-linux-arm': 0.34.3 + '@img/sharp-linux-arm64': 0.34.3 + '@img/sharp-linux-ppc64': 0.34.3 + '@img/sharp-linux-s390x': 0.34.3 + '@img/sharp-linux-x64': 0.34.3 + '@img/sharp-linuxmusl-arm64': 0.34.3 + '@img/sharp-linuxmusl-x64': 0.34.3 + '@img/sharp-wasm32': 0.34.3 + '@img/sharp-win32-arm64': 0.34.3 + '@img/sharp-win32-ia32': 0.34.3 + '@img/sharp-win32-x64': 0.34.3 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + shimmer@1.2.1: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + optional: true + + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.2 + + slash@3.0.0: {} + + smart-buffer@4.2.0: {} + + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.7 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.4 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@5.5.0) + socks: 2.8.7 + transitivePeerDependencies: + - supports-color + + socks@2.8.7: + dependencies: + ip-address: 10.0.1 + smart-buffer: 4.2.0 + + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + + sonner@2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + space-separated-tokens@2.0.2: {} + + split-on-first@3.0.0: {} + + split2@4.2.0: {} + + stack-trace@0.0.10: {} + + standard-as-callback@2.1.0: {} + + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + + statuses@2.0.1: {} + + std-env@3.9.0: {} + + stdin-discarder@0.2.2: {} + + streamx@2.22.1: + dependencies: + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + optionalDependencies: + bare-events: 2.6.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.5.0 + get-east-asian-width: 1.3.1 + strip-ansi: 7.1.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.2.0 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-json-comments@5.0.3: {} + + stripe@18.5.0(@types/node@22.18.0): + dependencies: + qs: 6.14.0 + optionalDependencies: + '@types/node': 22.18.0 + + styled-jsx@5.1.6(@babel/core@7.28.3)(react@18.3.1): + dependencies: + client-only: 0.0.1 + react: 18.3.1 + optionalDependencies: + '@babel/core': 7.28.3 + + super-regex@0.2.0: + dependencies: + clone-regexp: 3.0.0 + function-timeout: 0.1.1 + time-span: 5.1.0 + + superagent@10.2.3: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.1(supports-color@5.5.0) + fast-safe-stringify: 2.1.1 + form-data: 4.0.4 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.0 + transitivePeerDependencies: + - supports-color + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svix-react@1.13.6(@babel/core@7.28.3)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(svix@1.75.0): + dependencies: + iframe-resizer-react: 1.1.1(@babel/core@7.28.3)(prop-types@15.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + svix: 1.75.0 + transitivePeerDependencies: + - '@babel/core' + - prop-types + - supports-color + + svix@1.75.0: + dependencies: + '@stablelib/base64': 1.0.1 + '@types/node': 22.18.0 + es6-promise: 4.2.8 + fast-sha256: 1.3.0 + url-parse: 1.5.10 + uuid: 10.0.0 + + swr@2.3.4(react@18.3.1): + dependencies: + dequal: 2.0.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + + swr@2.3.6(react@18.3.1): + dependencies: + dequal: 2.0.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + + symbol-observable@1.2.0: {} + + tailwind-merge@3.3.1: {} + + tailwind-scrollbar-hide@4.0.0(tailwindcss@4.1.12): + dependencies: + tailwindcss: 4.1.12 + + tailwindcss-animate@1.0.7(tailwindcss@4.1.12): + dependencies: + tailwindcss: 4.1.12 + + tailwindcss@4.1.12: {} + + tapable@2.2.3: {} + + tar-fs@3.1.0: + dependencies: + pump: 3.0.3 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 4.2.1 + bare-path: 3.0.0 + transitivePeerDependencies: + - bare-buffer + + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.22.1 + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + text-decoder@1.2.3: + dependencies: + b4a: 1.6.7 + + theming@3.3.0(react@18.3.1): + dependencies: + hoist-non-react-statics: 3.3.2 + prop-types: 15.8.1 + react: 18.3.1 + react-display-name: 0.2.5 + tiny-warning: 1.0.3 + + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + + throttleit@2.1.0: {} + + time-span@5.1.0: + dependencies: + convert-hrtime: 5.0.0 + + tiny-invariant@1.3.3: {} + + tiny-warning@1.0.3: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + touch@3.1.1: {} + + tr46@0.0.3: {} + + tree-kill@1.2.2: {} + + trim-lines@3.0.1: {} + + triple-beam@1.4.1: {} + + ts-api-utils@2.1.0(typescript@5.7.3): + dependencies: + typescript: 5.7.3 + + ts-node@10.9.2(@types/node@22.18.0)(typescript@5.7.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.18.0 + acorn: 8.15.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.7.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tsc-alias@1.8.16: + dependencies: + chokidar: 3.6.0 + commander: 9.5.0 + get-tsconfig: 4.10.1 + globby: 11.1.0 + mylas: 2.1.13 + normalize-path: 3.0.0 + plimit-lit: 1.6.1 + + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tsx@4.20.5: + dependencies: + esbuild: 0.25.9 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + typed-query-selector@2.12.0: {} + + typescript-eslint@8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.41.0(@typescript-eslint/parser@8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3))(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + '@typescript-eslint/parser': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + '@typescript-eslint/typescript-estree': 8.41.0(typescript@5.7.3) + '@typescript-eslint/utils': 8.41.0(eslint@9.34.0(jiti@2.5.1))(typescript@5.7.3) + eslint: 9.34.0(jiti@2.5.1) + typescript: 5.7.3 + transitivePeerDependencies: + - supports-color + + typescript@5.7.3: {} + + uncrypto@0.1.3: {} + + undefsafe@2.0.5: {} + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + undici-types@7.10.0: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + unpipe@1.0.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.4): + dependencies: + browserslist: 4.25.4 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + + use-callback-ref@1.3.3(@types/react@18.3.24)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.24 + + use-sidecar@1.1.3(@types/react@18.3.24)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.24 + + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + + util-deprecate@1.0.2: {} + + utils-merge@1.0.1: {} + + uuid@10.0.0: {} + + uuid@11.1.0: {} + + uuid@9.0.1: {} + + v8-compile-cache-lib@3.0.1: {} + + vary@1.1.2: {} + + vaul@1.1.2(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.24))(@types/react@18.3.24)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + vite@6.3.5(@types/node@22.18.0)(jiti@2.5.1)(lightningcss@1.30.1)(tsx@4.20.5): + dependencies: + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.50.0 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.18.0 + fsevents: 2.3.3 + jiti: 2.5.1 + lightningcss: 1.30.1 + tsx: 4.20.5 + + vscode-oniguruma@2.0.1: {} + + vscode-textmate@9.2.0: {} + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + web-streams-polyfill@4.0.0-beta.3: {} + + web-vitals@4.2.4: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + word-wrap@1.2.5: {} + + workerpool@9.3.3: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + ws@8.17.1: {} + + ws@8.18.3: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@5.0.0: {} + + yargs-parser@21.1.1: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yauzl@2.10.0: + dependencies: + buffer-crc32: 0.2.13 + fd-slicer: 1.1.0 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yoctocolors-cjs@2.1.3: {} + + yoctocolors@2.1.2: {} + + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zod@4.1.5: {} + + zwitch@2.0.4: {} diff --git a/server/biome.json b/server/biome.json index aa8a3e2cd..76170ffa5 100644 --- a/server/biome.json +++ b/server/biome.json @@ -26,6 +26,9 @@ "noExportsInTest": "off", "noExplicitAny": "off", "noImplicitAnyLet": "off" + }, + "complexity": { + "noStaticOnlyClass": "off" } } }, diff --git a/server/shell/g5.sh b/server/shell/g5.sh index 1ca91d668..6396f2ab2 100755 --- a/server/shell/g5.sh +++ b/server/shell/g5.sh @@ -7,16 +7,13 @@ source "$(dirname "$0")/config.sh" if [[ "$1" == *"setup"* ]]; then MOCHA_PARALLEL=true $MOCHA_SETUP fi -# $MOCHA_CMD 'tests/advanced/multiFeature/*.ts' +# $MOCHA_CMD 'tests/advanced/referrals/*.ts' 'tests/advanced/coupons/*.ts' $MOCHA_CMD 'tests/advanced/multiFeature/*.ts' \ 'tests/advanced/coupons/*.ts' \ 'tests/attach/updateQuantity/*.ts' \ 'tests/advanced/referrals/*.ts' \ - 'tests/advanced/rollovers/*.ts' \ - 'tests/advanced/customInterval/*.ts' - -$MOCHA_CMD 'tests/advanced/referrals/*.ts' \ + 'tests/advanced/referrals/paid/*.ts' \ 'tests/advanced/rollovers/*.ts' \ 'tests/advanced/customInterval/*.ts' diff --git a/server/src/check.ts b/server/src/check.ts index 74ac1dac0..a1d74644d 100644 --- a/server/src/check.ts +++ b/server/src/check.ts @@ -42,7 +42,8 @@ const skipIds = [ ]; orgSlugs = ["supermemory"]; -const customerId = "co1VPgUU59q43d5P2rFt4c"; +let customerId = null; +// customerId = "co1VPgUU59q43d5P2rFt4c"; const getSingleCustomer = async ({ stripeCli, @@ -81,11 +82,15 @@ const getSingleCustomer = async ({ // ), // }); + let scheduleIds = customers[0].customer_products.flatMap( + (cp) => cp.scheduled_ids || [] + ); + + scheduleIds = Array.from(new Set(scheduleIds)); + const stripeSchedules = await getStripeSchedules({ stripeCli, - scheduleIds: customers[0].customer_products.flatMap( - (cp) => cp.scheduled_ids || [] - ), + scheduleIds, }); const entities = await EntityService.list({ @@ -122,12 +127,6 @@ const checkCustomerCorrect = async ({ // console.log(`Checking ${fullCus.email} (${fullCus.id})`); const cusProducts = fullCus.customer_products; - // await expectSubToBeCorrect({ - // db, - // customerId: fullCus.id!, - // org, - // env: AppEnv.Live, - // }); await checkCusSubCorrect({ db, fullCus, diff --git a/server/src/check2.ts b/server/src/check2.ts new file mode 100644 index 000000000..ba9ed94af --- /dev/null +++ b/server/src/check2.ts @@ -0,0 +1,67 @@ +import { config } from "dotenv"; +config(); + +import { + getAllEntities, + getAllFullCustomers, +} from "@/utils/scriptUtils/getAll/getAllAutumnCustomers.js"; +import { initDrizzle } from "@/db/initDrizzle.js"; +import { + AppEnv, + CusProductStatus, + FullCusProduct, + FullCustomer, + Organization, + Entity, +} from "@autumn/shared"; +import Stripe from "stripe"; +import assert from "assert"; +import { cusProductToPrices } from "@autumn/shared"; +import { notNullish } from "@/utils/genUtils.js"; +import { + getAllStripeSchedules, + getAllStripeSubscriptions, +} from "@/utils/scriptUtils/getAll/getAllStripeSubs.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { getStripeSchedules } from "@/external/stripe/stripeSubUtils.js"; +import { createSupabaseClient } from "@/external/supabaseUtils.js"; +import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js"; +import { getRelatedCusPrice } from "./internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { checkCusSubCorrect } from "./utils/checkUtils/checkCustomerCorrect.js"; +import { EntityService } from "./internal/api/entities/EntityService.js"; + +const { db } = initDrizzle({ maxConnections: 5 }); + +export const check = async () => { + const env = AppEnv.Live; + const orgId = "99XYziU2vChNNpdeEpvse09b8UF6BPME"; + + let fullCustomers = await getAllFullCustomers({ + db, + orgId, + env, + }); + + const checkCustomers = ["9bafd636-0c52-46b3-8ecd-1708d6faa373"]; + + fullCustomers = fullCustomers.filter((customer) => + checkCustomers.includes(customer.id || "") + ); + + for (const customer of fullCustomers) { + console.log(`Checking ${customer.name} (${customer.id})`); + const cusProducts = customer.customer_products; + const cusEnts = cusProducts.flatMap((cp) => cp.customer_entitlements); + } +}; + +check() + .catch((error) => { + console.error(error); + process.exit(1); + }) + .finally(() => { + process.exit(0); + }); diff --git a/server/src/external/autumn/autumnCli.ts b/server/src/external/autumn/autumnCli.ts index 7a9393f9c..ea6dd3300 100644 --- a/server/src/external/autumn/autumnCli.ts +++ b/server/src/external/autumn/autumnCli.ts @@ -1,497 +1,497 @@ import dotenv from "dotenv"; + dotenv.config(); -import { toSnakeCase } from "@/utils/genUtils.js"; + import { - APIVersion, - CreateEntity, - CreateRewardProgram, - CusExpand, - EntityExpand, - ErrCode, - Invoice, - OrgConfig, + type APIVersion, + type AttachBody, + type CreateEntity, + type CreateRewardProgram, + CusExpand, + EntityExpand, + ErrCode, + type OrgConfig, + type RewardRedemption, } from "@autumn/shared"; -import { - CancelParams, - CheckoutParams, - CheckoutResult, - CheckParams, - CheckResult, - Customer, - TrackParams, - TransferProductParams, - UsageParams, +import type { + CancelParams, + CheckoutParams, + CheckoutResult, + CheckParams, + CheckResult, + Customer, + TrackParams, + UsageParams, } from "autumn-js"; -import { AttachBody } from "@autumn/shared"; export default class AutumnError extends Error { - message: string; - code: string; + message: string; + code: string; - constructor({ message, code }: { message: string; code: string }) { - super(message); - this.message = message; - this.code = code; - } + constructor({ message, code }: { message: string; code: string }) { + super(message); + this.message = message; + this.code = code; + } - toString(): string { - return `${this.message} (code: ${this.code})`; - } + toString(): string { + return `${this.message} (code: ${this.code})`; + } } export class AutumnInt { - private apiKey: string; - public headers: Record; - public baseUrl: string; + private apiKey: string; + public headers: Record; + public baseUrl: string; - constructor({ - apiKey, - secretKey, - baseUrl, - version, - orgConfig, - }: { - apiKey?: string; - secretKey?: string; - baseUrl?: string; - version?: string | APIVersion; - orgConfig?: Partial; - } = {}) { - // this.apiKey = apiKey || process.env.AUTUMN_API_KEY || ""; - this.apiKey = - apiKey || secretKey || process.env.UNIT_TEST_AUTUMN_SECRET_KEY || ""; + constructor({ + apiKey, + secretKey, + baseUrl, + version, + orgConfig, + }: { + apiKey?: string; + secretKey?: string; + baseUrl?: string; + version?: string | APIVersion; + orgConfig?: Partial; + } = {}) { + // this.apiKey = apiKey || process.env.AUTUMN_API_KEY || ""; + this.apiKey = + apiKey || secretKey || process.env.UNIT_TEST_AUTUMN_SECRET_KEY || ""; - this.headers = { - Authorization: `Bearer ${this.apiKey}`, - "Content-Type": "application/json", - }; + this.headers = { + Authorization: `Bearer ${this.apiKey}`, + "Content-Type": "application/json", + }; - if (version) { - this.headers["x-api-version"] = version.toString(); - } + if (version) { + this.headers["x-api-version"] = version.toString(); + } - if (orgConfig) { - this.headers["org-config"] = JSON.stringify(orgConfig); - } + if (orgConfig) { + this.headers["org-config"] = JSON.stringify(orgConfig); + } - this.baseUrl = baseUrl || "http://localhost:8080/v1"; - } + this.baseUrl = baseUrl || "http://localhost:8080/v1"; + } - async get(path: string) { - const response = await fetch(`${this.baseUrl}${path}`, { - headers: this.headers, - }); - return response.json(); - } + async get(path: string) { + const response = await fetch(`${this.baseUrl}${path}`, { + headers: this.headers, + }); + return response.json(); + } - async post(path: string, body: any) { - const response = await fetch(`${this.baseUrl}${path}`, { - method: "POST", - headers: this.headers, - body: JSON.stringify(body), - }); + async post(path: string, body: any) { + const response = await fetch(`${this.baseUrl}${path}`, { + method: "POST", + headers: this.headers, + body: JSON.stringify(body), + }); - if (response.status != 200) { - let error: any; - try { - error = await response.json(); - } catch (error) { - throw new AutumnError({ - message: "Failed to parse Autumn API error response", - code: ErrCode.InternalError, - }); - } + if (response.status != 200) { + let error: any; + try { + error = await response.json(); + } catch (error) { + throw new AutumnError({ + message: "Failed to parse Autumn API error response", + code: ErrCode.InternalError, + }); + } - throw new AutumnError({ - message: error.message, - code: error.code, - }); - } + throw new AutumnError({ + message: error.message, + code: error.code, + }); + } - return response.json(); - } + return response.json(); + } - async delete( - path: string, - { - deleteInStripe = false, - }: { - deleteInStripe?: boolean; - } = {} - ) { - const response = await fetch( - `${this.baseUrl}${path}?${deleteInStripe ? "delete_in_stripe=true" : ""}`, - { - method: "DELETE", - headers: this.headers, - } - ); + async delete( + path: string, + { + deleteInStripe = false, + }: { + deleteInStripe?: boolean; + } = {}, + ) { + const response = await fetch( + `${this.baseUrl}${path}?${deleteInStripe ? "delete_in_stripe=true" : ""}`, + { + method: "DELETE", + headers: this.headers, + }, + ); - if (response.status != 200) { - let error: any; - try { - error = await response.json(); - } catch (error) { - throw new AutumnError({ - message: "Failed to parse Autumn API error response", - code: ErrCode.InternalError, - }); - } + if (response.status != 200) { + let error: any; + try { + error = await response.json(); + } catch (error) { + throw new AutumnError({ + message: "Failed to parse Autumn API error response", + code: ErrCode.InternalError, + }); + } - throw new AutumnError({ - message: error.message, - code: error.code, - }); - } + throw new AutumnError({ + message: error.message, + code: error.code, + }); + } - return response.json(); - } + return response.json(); + } - async createCustomer({ - id, - email, - name, - fingerprint, - }: { - id: string; - email: string; - name: string; - fingerprint?: string; - }) { - const data = await this.post("/customers", { - id, - email, - name, - fingerprint, - }); + async createCustomer({ + id, + email, + name, + fingerprint, + }: { + id: string; + email: string; + name: string; + fingerprint?: string; + }) { + const data = await this.post("/customers", { + id, + email, + name, + fingerprint, + }); - return data; - } + return data; + } - async attach(params: AttachBody) { - // const data = await this.post(`/attach`, { - // customer_id: customerId, - // product_id: productId, - // options: toSnakeCase(options), - // }); - const data = await this.post(`/attach`, params); + async attach(params: AttachBody) { + // const data = await this.post(`/attach`, { + // customer_id: customerId, + // product_id: productId, + // options: toSnakeCase(options), + // }); + const data = await this.post(`/attach`, params); - return data; - } - async checkout( - params: CheckoutParams & { invoice?: boolean; force_checkout?: boolean } - ) { - // const data = await this.post(`/attach`, { - // customer_id: customerId, - // product_id: productId, - // options: toSnakeCase(options), - // }); - const data = await this.post(`/checkout`, params); + return data; + } + async checkout( + params: CheckoutParams & { invoice?: boolean; force_checkout?: boolean }, + ) { + // const data = await this.post(`/attach`, { + // customer_id: customerId, + // product_id: productId, + // options: toSnakeCase(options), + // }); + const data = await this.post(`/checkout`, params); - return data as CheckoutResult; - } - async transfer( - customerId: string, - params: { - from_entity_id?: string; - to_entity_id: string; - product_id: string; - } - ) { - const data = await this.post(`/customers/${customerId}/transfer`, params); + return data as CheckoutResult; + } + async transfer( + customerId: string, + params: { + from_entity_id?: string; + to_entity_id: string; + product_id: string; + }, + ) { + const data = await this.post(`/customers/${customerId}/transfer`, params); - return data as CheckoutResult; - } + return data as CheckoutResult; + } - async sendEvent({ - customerId, - eventName, - properties, - customer_data, - idempotency_key, - }: { - customerId: string; - eventName: string; - properties?: any; - customer_data?: any; - idempotency_key?: string; - }) { - const data = await this.post(`/events`, { - customer_id: customerId, - event_name: eventName, - properties, - customer_data, - idempotency_key, - }); + async sendEvent({ + customerId, + eventName, + properties, + customer_data, + idempotency_key, + }: { + customerId: string; + eventName: string; + properties?: any; + customer_data?: any; + idempotency_key?: string; + }) { + const data = await this.post(`/events`, { + customer_id: customerId, + event_name: eventName, + properties, + customer_data, + idempotency_key, + }); - return data; - } + return data; + } - async entitled({ - customerId, - featureId, - quantity, - customer_data, - }: { - customerId: string; - featureId: string; - quantity?: number; - customer_data?: any; - }) { - const data = await this.post(`/entitled`, { - customer_id: customerId, - feature_id: featureId, - quantity, - customer_data, - }); + async entitled({ + customerId, + featureId, + quantity, + customer_data, + }: { + customerId: string; + featureId: string; + quantity?: number; + customer_data?: any; + }) { + const data = await this.post(`/entitled`, { + customer_id: customerId, + feature_id: featureId, + quantity, + customer_data, + }); - return data; - } + return data; + } - customers = { - get: async ( - customerId: string, - params?: { - expand?: CusExpand[]; - } - ): Promise< - Customer & { - invoices: any[]; - } - > => { - const queryParams = new URLSearchParams(); - const defaultParams = { - expand: [CusExpand.Invoices], - }; + customers = { + get: async ( + customerId: string, + params?: { + expand?: CusExpand[]; + }, + ): Promise< + Customer & { + invoices: any[]; + } + > => { + const queryParams = new URLSearchParams(); + const defaultParams = { + expand: [CusExpand.Invoices], + }; - const finalParams = { ...defaultParams, ...params }; - if (finalParams.expand) { - queryParams.append("expand", finalParams.expand.join(",")); - } + const finalParams = { ...defaultParams, ...params }; + if (finalParams.expand) { + queryParams.append("expand", finalParams.expand.join(",")); + } - const data = await this.get( - `/customers/${customerId}?${queryParams.toString()}` - ); - return data; - }, + const data = await this.get( + `/customers/${customerId}?${queryParams.toString()}`, + ); + return data; + }, - create: async (customer: { id: string; email: string; name?: string }) => { - const data = await this.post(`/customers?with_autumn_id=true`, customer); - return data; - }, - delete: async ( - customerId: string, - { - deleteInStripe = false, - }: { - deleteInStripe?: boolean; - } = {} - ) => { - const data = await this.delete(`/customers/${customerId}`, { - deleteInStripe, - }); - return data; - }, - }; + create: async (customer: { id: string; email: string; name?: string }) => { + const data = await this.post(`/customers?with_autumn_id=true`, customer); + return data; + }, + delete: async ( + customerId: string, + { + deleteInStripe = false, + }: { + deleteInStripe?: boolean; + } = {}, + ) => { + const data = await this.delete(`/customers/${customerId}`, { + deleteInStripe, + }); + return data; + }, + }; - entities = { - get: async (customerId: string, entityId: string) => { - const data = await this.get( - `/customers/${customerId}/entities/${entityId}?expand=${EntityExpand.Invoices}` - ); - return data; - }, + entities = { + get: async (customerId: string, entityId: string) => { + const data = await this.get( + `/customers/${customerId}/entities/${entityId}?expand=${EntityExpand.Invoices}`, + ); + return data; + }, - create: async ( - customerId: string, - entity: CreateEntity | CreateEntity[] - ) => { - // let entities = Array.isArray(entity) ? entity : [entity]; - const data = await this.post( - `/customers/${customerId}/entities?with_autumn_id=true`, - entity - ); + create: async ( + customerId: string, + entity: CreateEntity | CreateEntity[], + ) => { + // let entities = Array.isArray(entity) ? entity : [entity]; + const data = await this.post( + `/customers/${customerId}/entities?with_autumn_id=true`, + entity, + ); - return data; - }, + return data; + }, - list: async (customerId: string) => { - const data = await this.get(`/customers/${customerId}/entities`); - return data; - }, + list: async (customerId: string) => { + const data = await this.get(`/customers/${customerId}/entities`); + return data; + }, - delete: async (customerId: string, entityId: string) => { - const data = await this.delete( - `/customers/${customerId}/entities/${entityId}` - ); - return data; - }, - }; + delete: async (customerId: string, entityId: string) => { + const data = await this.delete( + `/customers/${customerId}/entities/${entityId}`, + ); + return data; + }, + }; - products = { - update: async (productId: string, product: any) => { - // if (product.items && typeof product.items === "object") { - // product.items = Object.values(product.items); - // } - const data = await this.post(`/products/${productId}`, product); - return data; - }, + products = { + update: async (productId: string, product: any) => { + // if (product.items && typeof product.items === "object") { + // product.items = Object.values(product.items); + // } + const data = await this.post(`/products/${productId}`, product); + return data; + }, - get: async ( - productId: string, - { v1Schema = false }: { v1Schema?: boolean } = {} - ) => { - const data = await this.get( - `/products/${productId}?${v1Schema ? "schemaVersion=1" : ""}` - ); - return data; - }, + get: async ( + productId: string, + { v1Schema = false }: { v1Schema?: boolean } = {}, + ) => { + const data = await this.get( + `/products/${productId}?${v1Schema ? "schemaVersion=1" : ""}`, + ); + return data; + }, - create: async (product: any) => { - const data = await this.post(`/products`, product); - return data; - }, + create: async (product: any) => { + const data = await this.post(`/products`, product); + return data; + }, - delete: async (productId: string) => { - const data = await this.delete(`/products/${productId}`); - return data; - }, - }; + delete: async (productId: string) => { + const data = await this.delete(`/products/${productId}`); + return data; + }, + }; - rewards = { - get: async (rewardId: string) => { - const data = await this.get(`/rewards/${rewardId}`); - return data; - }, + rewards = { + get: async (rewardId: string) => { + const data = await this.get(`/rewards/${rewardId}`); + return data; + }, - create: async (reward: any) => { - const data = await this.post(`/rewards?legacyStripe=true`, reward); - return data; - }, + create: async (reward: any) => { + const data = await this.post(`/rewards?legacyStripe=true`, reward); + return data; + }, - delete: async (rewardId: string) => { - const data = await this.delete(`/rewards/${rewardId}`); - return data; - }, - }; + delete: async (rewardId: string) => { + const data = await this.delete(`/rewards/${rewardId}`); + return data; + }, + }; - rewardPrograms = { - create: async (rewardProgram: CreateRewardProgram) => { - const data = await this.post(`/reward_programs`, rewardProgram); - return data; - }, - }; + rewardPrograms = { + create: async (rewardProgram: CreateRewardProgram) => { + const data = await this.post(`/reward_programs`, rewardProgram); + return data; + }, + }; - referrals = { - createCode: async ({ - customerId, - referralId, - }: { - customerId: string; - referralId: string; - }) => { - const data = await this.post(`/referrals/code`, { - customer_id: customerId, - program_id: referralId, - }); - return data; - }, - redeem: async ({ - customerId, - code, - }: { - customerId: string; - code: string; - }) => { - const data = await this.post(`/referrals/redeem`, { - customer_id: customerId, - code, - }); - return data; - }, - }; + referrals = { + createCode: async ({ + customerId, + referralId, + }: { + customerId: string; + referralId: string; + }) => { + const data = await this.post(`/referrals/code`, { + customer_id: customerId, + program_id: referralId, + }); + return data; + }, + redeem: async ({ + customerId, + code, + }: { + customerId: string; + code: string; + }) => { + const data = await this.post(`/referrals/redeem`, { + customer_id: customerId, + code, + }); + return data; + }, + }; - redemptions = { - get: async (redemptionId: string) => { - const data = await this.get(`/redemptions/${redemptionId}`); - return data; - }, - }; + redemptions = { + get: async (redemptionId: string) => { + const data = await this.get(`/redemptions/${redemptionId}`); + return data as RewardRedemption; + }, + }; - events = { - send: async ({ - customerId, - featureId, - value, - properties, - }: { - customerId: string; - featureId: string; - value: number; - properties?: any; - }) => { - const data = await this.post(`/events`, { - customer_id: customerId, - feature_id: featureId, - value, - properties, - }); - return data; - }, - }; + events = { + send: async ({ + customerId, + featureId, + value, + properties, + }: { + customerId: string; + featureId: string; + value: number; + properties?: any; + }) => { + const data = await this.post(`/events`, { + customer_id: customerId, + feature_id: featureId, + value, + properties, + }); + return data; + }, + }; - stripe = { - connect: async (params: { - secret_key: string; - success_url: string; - default_currency: string; - }) => { - const data = await this.post(`/organization/stripe`, params); - return data; - }, + stripe = { + connect: async (params: { + secret_key: string; + success_url: string; + default_currency: string; + }) => { + const data = await this.post(`/organization/stripe`, params); + return data; + }, - delete: async () => { - const data = await this.delete(`/organization/stripe`); - return data; - }, - }; + delete: async () => { + const data = await this.delete(`/organization/stripe`); + return data; + }, + }; - track = async (params: TrackParams & { timestamp?: number }) => { - const data = await this.post(`/track`, params); - return data; - }; + track = async (params: TrackParams & { timestamp?: number }) => { + const data = await this.post(`/track`, params); + return data; + }; - usage = async (params: UsageParams) => { - const data = await this.post(`/usage`, params); - return data; - }; + usage = async (params: UsageParams) => { + const data = await this.post(`/usage`, params); + return data; + }; - check = async (params: CheckParams): Promise => { - const data = await this.post(`/check`, params); - return data; - }; + check = async (params: CheckParams): Promise => { + const data = await this.post(`/check`, params); + return data; + }; - attachPreview = async (params: AttachBody) => { - const data = await this.post(`/attach/preview`, params); - return data; - }; + attachPreview = async (params: AttachBody) => { + const data = await this.post(`/attach/preview`, params); + return data; + }; - cancel = async (params: CancelParams) => { - const data = await this.post(`/cancel`, params); - return data; - }; + cancel = async (params: CancelParams) => { + const data = await this.post(`/cancel`, params); + return data; + }; - migrate = async (params: { - from_product_id: string; - to_product_id: string; - from_version: number; - to_version: number; - }) => { - const data = await this.post(`/migrations`, params); - return data; - }; + migrate = async (params: { + from_product_id: string; + to_product_id: string; + from_version: number; + to_version: number; + }) => { + const data = await this.post(`/migrations`, params); + return data; + }; - initStripe = async () => { - await this.post(`/products/all/init_stripe`, {}); - }; + initStripe = async () => { + await this.post(`/products/all/init_stripe`, {}); + }; } diff --git a/server/src/external/stripe/createStripePrice/createStripePrice.ts b/server/src/external/stripe/createStripePrice/createStripePrice.ts index dbf8c9bb7..d8a2762f1 100644 --- a/server/src/external/stripe/createStripePrice/createStripePrice.ts +++ b/server/src/external/stripe/createStripePrice/createStripePrice.ts @@ -34,8 +34,6 @@ export const checkCurStripePrice = async ({ stripeCli: Stripe; currency: string; }) => { - let priceValid = false; - let config = price.config! as UsagePriceConfig; let stripePrice: Stripe.Price | null = null; @@ -48,7 +46,9 @@ export const checkCurStripePrice = async ({ }); if (!stripePrice.active) { - stripePrice = null; + stripePrice = await stripeCli.prices.update(config.stripe_price_id!, { + active: true, + }); } if ( diff --git a/server/src/external/stripe/priceToStripeItem/priceToUsageInAdvance.ts b/server/src/external/stripe/priceToStripeItem/priceToUsageInAdvance.ts index 2794f4103..b50dfe932 100644 --- a/server/src/external/stripe/priceToStripeItem/priceToUsageInAdvance.ts +++ b/server/src/external/stripe/priceToStripeItem/priceToUsageInAdvance.ts @@ -20,11 +20,12 @@ export const priceToOneOffAndTiered = ({ }) => { const config = price.config as UsagePriceConfig; let quantity = options?.quantity!; - let overage = quantity * config.billing_units! - relatedEnt.allowance!; + let overage = new Decimal(quantity).mul(config.billing_units!).toNumber(); + // let overage = quantity * config.billing_units! - relatedEnt.allowance!; - if (overage <= 0) { - return null; - } + // if (overage <= 0) { + // return null; + // } const amount = getPriceForOverage(price, overage); if (!config.stripe_product_id) { diff --git a/server/src/external/stripe/stripeCouponUtils/extendCouponDuration.ts b/server/src/external/stripe/stripeCouponUtils/extendCouponDuration.ts new file mode 100644 index 000000000..53af611d0 --- /dev/null +++ b/server/src/external/stripe/stripeCouponUtils/extendCouponDuration.ts @@ -0,0 +1,130 @@ +// import Stripe from "stripe"; + +// /** +// * Extends an existing coupon's duration by replacing it with a new coupon that has +// * the remaining duration + additional months. This avoids concurrent stacking issues. +// */ +// export const extendCouponDuration = async ({ +// stripeCli, +// sub, +// existingCouponId, +// additionalMonths, +// logger, +// }: { +// stripeCli: Stripe; +// sub: Stripe.Subscription; +// existingCouponId: string; +// additionalMonths: number; +// logger: any; +// }): Promise<{ +// success: boolean; +// newCouponId?: string; +// error?: string; +// }> => { +// try { +// logger.info( +// `Extending coupon ${existingCouponId} by ${additionalMonths} months` +// ); + +// // Get current subscription and coupon details +// const existingCoupon = await stripeCli.coupons.retrieve(existingCouponId); +// const currentDiscounts = (sub.discounts as Stripe.Discount[]) || []; +// const existingDiscount = currentDiscounts.find((d: any) => +// d.coupon?.id.startsWith(existingCouponId) +// ); + +// if (!existingDiscount) { +// return { +// success: false, +// error: "Original coupon not found on subscription", +// }; +// } + +// // Calculate remaining months using the discount's actual start and end times +// const originalDurationMonths = existingCoupon.duration_in_months || 0; + +// // Use discount start and end times for accurate calculation +// const discountStart = new Date(existingDiscount.start * 1000); +// const discountEnd = existingDiscount.end +// ? new Date(existingDiscount.end * 1000) +// : null; +// const now = new Date(); + +// let remainingMonths: number; + +// if (discountEnd) { +// // Calculate remaining time in months +// const remainingTimeMs = Math.max( +// 0, +// discountEnd.getTime() - now.getTime() +// ); +// remainingMonths = Math.ceil(remainingTimeMs / (30 * 24 * 60 * 60 * 1000)); +// } else { +// // If no end date (shouldn't happen for repeating coupons), fall back to original duration +// remainingMonths = originalDurationMonths; +// } + +// const totalNewDurationMonths = remainingMonths + additionalMonths; + +// logger.info( +// `Discount period: ${discountStart.toISOString()} to ${discountEnd?.toISOString() || "forever"}` +// ); +// logger.info( +// `Original: ${originalDurationMonths}m, Remaining: ${remainingMonths}m, Adding: ${additionalMonths}m, Total: ${totalNewDurationMonths}m` +// ); + +// // Create a new coupon with the extended duration +// const extendedCouponId = `${existingCouponId}_${Date.now()}`; +// const couponCreateParams: Stripe.CouponCreateParams = { +// id: extendedCouponId, +// duration: "repeating", +// duration_in_months: totalNewDurationMonths, +// name: `Extended ${existingCoupon.name || "Coupon"}`, +// // metadata: { +// // original_coupon_id: existingCouponId, +// // original_duration: originalDurationMonths.toString(), +// // remaining_months: remainingMonths.toString(), +// // additional_months: additionalMonths.toString(), +// // total_duration: totalNewDurationMonths.toString(), +// // extended_at: Date.now().toString(), +// // }, +// }; + +// // Copy discount value from the existing coupon +// if (existingCoupon.percent_off) { +// couponCreateParams.percent_off = existingCoupon.percent_off; +// } else if (existingCoupon.amount_off) { +// couponCreateParams.amount_off = existingCoupon.amount_off; +// if (existingCoupon.currency) { +// couponCreateParams.currency = existingCoupon.currency; +// } +// } + +// // Copy applies_to if it exists +// if (existingCoupon.applies_to) { +// couponCreateParams.applies_to = existingCoupon.applies_to; +// } + +// const extendedCoupon = await stripeCli.coupons.create(couponCreateParams); + +// // Replace the existing coupon with the extended one +// const otherDiscounts = currentDiscounts +// .filter((d: any) => d.coupon?.id !== existingCouponId) +// .map((d: Stripe.Discount) => ({ discount: d.id })); + +// await stripeCli.subscriptions.update(sub.id, { +// discounts: [...otherDiscounts, { coupon: extendedCoupon.id }], +// }); + +// logger.info( +// `Successfully extended coupon duration to ${totalNewDurationMonths} months. New coupon ID: ${extendedCouponId}` +// ); + +// await stripeCli.coupons.del(existingCouponId); + +// return { success: true, newCouponId: extendedCouponId }; +// } catch (error: any) { +// logger.error(`Failed to extend coupon duration: ${error.message}`, error); +// return { success: false, error: error.message }; +// } +// }; diff --git a/server/src/external/stripe/stripeCouponUtils/stripeCouponUtils.ts b/server/src/external/stripe/stripeCouponUtils/stripeCouponUtils.ts index 0da2c68b1..17a815763 100644 --- a/server/src/external/stripe/stripeCouponUtils/stripeCouponUtils.ts +++ b/server/src/external/stripe/stripeCouponUtils/stripeCouponUtils.ts @@ -1,23 +1,48 @@ -import RecaseError from "@/utils/errorUtils.js"; import { - Reward, + type AppEnv, CouponDurationType, ErrCode, - Organization, - Price, + type FixedPriceConfig, + type Organization, + type Price, PriceType, - Product, - UsagePriceConfig, + type Product, + type Reward, RewardType, - AppEnv, + type UsagePriceConfig, } from "@autumn/shared"; -import { Stripe } from "stripe"; +import { pricesOnlyOneOff } from "@/internal/products/prices/priceUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; import { createStripeCli } from "../utils.js"; -const couponToStripeDuration = (coupon: Reward) => { - let discountConfig = coupon.discount_config; +const couponToStripeDuration = ({ + coupon, + isOneOffProduct = false, +}: { + coupon: Reward; + isOneOffProduct: boolean; +}) => { + if (coupon.type === RewardType.FreeProduct) { + // For one-off products, the coupon should apply once, not repeat + if (isOneOffProduct) { + return { + duration: "once", + }; + } + console.log("--------------------------------"); + console.log("rewardName", coupon.name); + console.log("coupon.free_product_config", coupon.free_product_config); + console.log("isOneOffProduct", isOneOffProduct); + console.log("--------------------------------"); + return { + duration: "repeating", + duration_in_months: coupon.free_product_config?.duration_value, + }; + } + + const discountConfig = coupon.discount_config; if ( - coupon.type == RewardType.InvoiceCredits && + coupon.type === RewardType.InvoiceCredits && coupon.discount_config?.duration_type === CouponDurationType.Forever ) { return { @@ -25,6 +50,13 @@ const couponToStripeDuration = (coupon: Reward) => { }; } + // For one-off products, always use "once" duration regardless of config + if (isOneOffProduct) { + return { + duration: "once", + }; + } + switch (discountConfig!.duration_type) { case CouponDurationType.Forever: return { @@ -45,11 +77,28 @@ const couponToStripeDuration = (coupon: Reward) => { const couponToStripeValue = ({ reward, org, + prices, }: { reward: Reward; org: Organization; + prices?: (Price & { product: Product })[]; }) => { - let discountConfig = reward.discount_config; + if (reward.type === RewardType.FreeProduct) { + const amountOff = Math.round( + prices?.reduce( + (acc, price) => acc + (price.config as FixedPriceConfig).amount, + 0 + ) || 0 + ); + + console.log("amountOff in couponToStripeValue", amountOff); + return { + amount_off: Math.round(amountOff * 100), + currency: org.default_currency || "usd", + }; + } + + const discountConfig = reward.discount_config; if (reward.type === RewardType.PercentageDiscount) { return { percent_off: discountConfig!.discount_value, @@ -80,7 +129,7 @@ export const createStripeCoupon = async ({ logger: any; legacyVersion?: boolean; }) => { - let discountConfig = reward.discount_config; + const discountConfig = reward.discount_config; const stripeCli = createStripeCli({ org, @@ -90,9 +139,9 @@ export const createStripeCoupon = async ({ try { await stripeCli.coupons.del(reward.id); - } catch (error) {} + } catch (_) {} - let stripeProdIds = prices.map((price) => { + const stripeProdIds = prices.map((price) => { if (price.config!.type === PriceType.Fixed) { return price.product.processor?.id; } else { @@ -116,26 +165,32 @@ export const createStripeCoupon = async ({ promoCode.code ); throw new RecaseError({ - message: `Promo code ${promoCode.code} already exists in Stripe`, + message: `Promo code ${promoCode.code} (${stripePromoCode.id}) already exists in Stripe`, code: ErrCode.PromoCodeAlreadyExistsInStripe, }); - } catch (error) {} + } catch (_) {} } const stripeCoupon = await stripeCli.coupons.create({ // id: reward.internal_id, id: reward.id, - ...(couponToStripeDuration(reward) as any), - ...(couponToStripeValue({ reward, org }) as any), + ...(couponToStripeDuration({ + coupon: reward, + isOneOffProduct: pricesOnlyOneOff(prices), + }) as any), + ...(couponToStripeValue({ reward, org, prices }) as any), name: reward.name, metadata: { autumn_internal_id: reward.internal_id, }, - applies_to: !discountConfig!.apply_to_all - ? { - products: stripeProdIds, - } - : undefined, + applies_to: + reward.type === RewardType.FreeProduct + ? undefined + : !discountConfig!.apply_to_all + ? { + products: stripeProdIds, + } + : undefined, }); // Create promo codes diff --git a/server/src/external/stripe/stripeCusUtils.ts b/server/src/external/stripe/stripeCusUtils.ts index 73ce9d58b..36996b4b7 100644 --- a/server/src/external/stripe/stripeCusUtils.ts +++ b/server/src/external/stripe/stripeCusUtils.ts @@ -1,332 +1,338 @@ -import { AppEnv, Customer, Organization, ProcessorType } from "@autumn/shared"; -import { Stripe } from "stripe"; -import { createStripeCli } from "./utils.js"; -import RecaseError from "@/utils/errorUtils.js"; -import { ErrCode } from "@autumn/shared"; +import { + type AppEnv, + type Customer, + ErrCode, + type Organization, + ProcessorType, +} from "@autumn/shared"; import { StatusCodes } from "http-status-codes"; +import type { Stripe } from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { CusService } from "@/internal/customers/CusService.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { createStripeCli } from "./utils.js"; export const getStripeCus = async ({ - stripeCli, - stripeId, + stripeCli, + stripeId, }: { - stripeCli: Stripe; - stripeId: string; + stripeCli: Stripe; + stripeId: string; }) => { - try { - const stripeCus = await stripeCli.customers.retrieve(stripeId); - return stripeCus as Stripe.Customer; - } catch (error) { - return undefined; - } + try { + const stripeCus = await stripeCli.customers.retrieve(stripeId); + return stripeCus as Stripe.Customer; + } catch (error) { + return undefined; + } }; export const createStripeCusIfNotExists = async ({ - db, - org, - env, - customer, - logger, + db, + org, + env, + customer, + logger, }: { - db: DrizzleCli; - org: Organization; - env: AppEnv; - customer: Customer; - logger: any; + db: DrizzleCli; + org: Organization; + env: AppEnv; + customer: Customer; + logger: any; }) => { - let createNew = false; - const stripeCli = createStripeCli({ org, env }); - if (!customer.processor || !customer.processor.id) { - createNew = true; - } else { - try { - let stripeCus = await stripeCli.customers.retrieve( - customer.processor.id, - { - expand: ["test_clock", "invoice_settings.default_payment_method"], - } - ); - if (!stripeCus.deleted) { - return stripeCus as Stripe.Customer; - } else { - createNew = true; - } - } catch (error) { - createNew = true; - } - } + let createNew = false; + const stripeCli = createStripeCli({ org, env }); + if (!customer.processor || !customer.processor.id) { + createNew = true; + } else { + try { + const stripeCus = await stripeCli.customers.retrieve( + customer.processor.id, + { + expand: ["test_clock", "invoice_settings.default_payment_method"], + }, + ); + if (!stripeCus.deleted) { + return stripeCus as Stripe.Customer; + } else { + createNew = true; + } + } catch (error) { + createNew = true; + } + } - if (createNew) { - logger.info(`Creating new stripe customer for ${customer.id}`); - const stripeCustomer = await createStripeCustomer({ - org, - env, - customer, - }); + if (createNew) { + logger.info(`Creating new stripe customer for ${customer.id}`); + const stripeCustomer = await createStripeCustomer({ + org, + env, + customer, + }); - await CusService.update({ - db, - internalCusId: customer.internal_id, - update: { - processor: { - id: stripeCustomer.id, - type: ProcessorType.Stripe, - }, - }, - }); + await CusService.update({ + db, + internalCusId: customer.internal_id, + update: { + processor: { + id: stripeCustomer.id, + type: ProcessorType.Stripe, + }, + }, + }); - customer.processor = { - id: stripeCustomer.id, - type: ProcessorType.Stripe, - }; + customer.processor = { + id: stripeCustomer.id, + type: ProcessorType.Stripe, + }; - return stripeCustomer; - } + return stripeCustomer; + } }; export const createStripeCustomer = async ({ - org, - env, - customer, - testClockId, + org, + env, + customer, + testClockId, }: { - org: Organization; - env: AppEnv; - customer: Customer; - testClockId?: string; + org: Organization; + env: AppEnv; + customer: Customer; + testClockId?: string; }) => { - const stripeCli = createStripeCli({ org, env }); + const stripeCli = createStripeCli({ org, env }); - try { - const stripeCustomer = await stripeCli.customers.create({ - name: customer.name || undefined, - email: customer.email || undefined, - metadata: { - autumn_id: customer.id || null, - autumn_internal_id: customer.internal_id, - }, - test_clock: testClockId, - }); + try { + const stripeCustomer = await stripeCli.customers.create({ + name: customer.name || undefined, + email: customer.email || undefined, + metadata: { + autumn_id: customer.id || null, + autumn_internal_id: customer.internal_id, + }, + test_clock: testClockId, + }); - return stripeCustomer; - } catch (error: any) { - throw new RecaseError({ - message: `Error creating customer in Stripe. ${error.message}`, - code: ErrCode.StripeCreateCustomerFailed, - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - }); - } + return stripeCustomer; + } catch (error: any) { + throw new RecaseError({ + message: `Error creating customer in Stripe. ${error.message}`, + code: ErrCode.StripeCreateCustomerFailed, + statusCode: StatusCodes.INTERNAL_SERVER_ERROR, + }); + } }; export const deleteStripeCustomer = async ({ - org, - env, - stripeId, + org, + env, + stripeId, }: { - org: Organization; - env: AppEnv; - stripeId: string; + org: Organization; + env: AppEnv; + stripeId: string; }) => { - const stripeCli = createStripeCli({ org, env }); + const stripeCli = createStripeCli({ org, env }); - const stripeCustomer = await stripeCli.customers.del(stripeId); + const stripeCustomer = await stripeCli.customers.del(stripeId); - return stripeCustomer; + return stripeCustomer; }; export const listCusPaymentMethods = async ({ - stripeCli, - stripeId, + stripeCli, + stripeId, }: { - stripeCli: Stripe; - stripeId: string; + stripeCli: Stripe; + stripeId: string; }) => { - let res = await stripeCli.paymentMethods.list({ - customer: stripeId, - }); + const res = await stripeCli.paymentMethods.list({ + customer: stripeId, + }); - const paymentMethods = res.data; - paymentMethods.sort((a, b) => b.created - a.created); + const paymentMethods = res.data; + paymentMethods.sort((a, b) => b.created - a.created); - return paymentMethods; + return paymentMethods; }; export const getCusPaymentMethod = async ({ - stripeCli, - stripeId, - errorIfNone = false, + stripeCli, + stripeId, + errorIfNone = false, }: { - stripeCli: Stripe; - stripeId?: string; - errorIfNone?: boolean; + stripeCli: Stripe; + stripeId?: string; + errorIfNone?: boolean; }) => { - if (!stripeId) { - return null; - } + if (!stripeId) { + return null; + } - const stripeCustomer = (await stripeCli.customers.retrieve( - stripeId - )) as Stripe.Customer; + const stripeCustomer = (await stripeCli.customers.retrieve( + stripeId, + )) as Stripe.Customer; - let paymentMethodId = stripeCustomer.invoice_settings?.default_payment_method; + const paymentMethodId = + stripeCustomer.invoice_settings?.default_payment_method; - if (!paymentMethodId) { - let res = await stripeCli.paymentMethods.list({ - customer: stripeId, - }); + if (!paymentMethodId) { + const res = await stripeCli.paymentMethods.list({ + customer: stripeId, + }); - const paymentMethods = res.data; - paymentMethods.sort((a, b) => b.created - a.created); + const paymentMethods = res.data; + paymentMethods.sort((a, b) => b.created - a.created); - if (res.data.length === 0) { - if (errorIfNone) { - throw new RecaseError({ - code: ErrCode.StripeGetPaymentMethodFailed, - message: `No payment method found for customer ${stripeId}`, - statusCode: 500, - }); - } - return null; - } + if (res.data.length === 0) { + if (errorIfNone) { + throw new RecaseError({ + code: ErrCode.StripeGetPaymentMethodFailed, + message: `No payment method found for customer ${stripeId}`, + statusCode: 500, + }); + } + return null; + } - return paymentMethods[0]; - } else { - const paymentMethod = await stripeCli.paymentMethods.retrieve( - paymentMethodId as string - ); - return paymentMethod; - } + return paymentMethods[0]; + } else { + const paymentMethod = await stripeCli.paymentMethods.retrieve( + paymentMethodId as string, + ); + return paymentMethod; + } }; // 2. Create a payment method and attach to customer export const attachPmToCus = async ({ - db, - customer, - org, - env, - willFail = false, - testClockId, + db, + customer, + org, + env, + willFail = false, + testClockId, }: { - db: DrizzleCli; - customer: Customer; - org: Organization; - env: AppEnv; - willFail?: boolean; - testClockId?: string; + db: DrizzleCli; + customer: Customer; + org: Organization; + env: AppEnv; + willFail?: boolean; + testClockId?: string; }) => { - // 1. Create stripe customer if not exists + // 1. Create stripe customer if not exists - let stripeCusId = customer.processor?.id; - if (!stripeCusId) { - const stripeCustomer = await createStripeCustomer({ - org, - env, - customer, - testClockId, - }); + let stripeCusId = customer.processor?.id; + if (!stripeCusId) { + const stripeCustomer = await createStripeCustomer({ + org, + env, + customer, + testClockId, + }); - await CusService.update({ - db, - internalCusId: customer.internal_id, - update: { - processor: { - id: stripeCustomer.id, - type: ProcessorType.Stripe, - }, - }, - }); + await CusService.update({ + db, + internalCusId: customer.internal_id, + update: { + processor: { + id: stripeCustomer.id, + type: ProcessorType.Stripe, + }, + }, + }); - stripeCusId = stripeCustomer.id; - customer.processor = { - id: stripeCustomer.id, - type: "stripe", - }; - } + stripeCusId = stripeCustomer.id; + customer.processor = { + id: stripeCustomer.id, + type: "stripe", + }; + } - const stripeCli = createStripeCli({ org, env }); + const stripeCli = createStripeCli({ org, env }); - try { - let token = willFail ? "tok_chargeCustomerFail" : "tok_visa"; - const pm = await stripeCli.paymentMethods.create({ - type: "card", - card: { - token, - }, - }); - await stripeCli.paymentMethods.attach(pm.id, { - customer: stripeCusId, - }); + try { + const token = willFail ? "tok_chargeCustomerFail" : "tok_visa"; + const pm = await stripeCli.paymentMethods.create({ + type: "card", + card: { + token, + }, + }); + await stripeCli.paymentMethods.attach(pm.id, { + customer: stripeCusId, + }); - await stripeCli.customers.update(stripeCusId, { - invoice_settings: { - default_payment_method: pm.id, - }, - }); - // console.log(" - Payment method attached"); - } catch (error) { - console.log(" - Error attaching payment method", error); - } + await stripeCli.customers.update(stripeCusId, { + invoice_settings: { + default_payment_method: pm.id, + }, + }); + // console.log(" - Payment method attached"); + } catch (error) { + console.log(" - Error attaching payment method", error); + } }; export const attachFailedPaymentMethod = async ({ - stripeCli, - customer, + stripeCli, + customer, }: { - stripeCli: Stripe; - customer: Customer; + stripeCli: Stripe; + customer: Customer; }) => { - // Delete existing payment method - const paymentMethods = await stripeCli.paymentMethods.list({ - customer: customer.processor?.id, - }); - for (const pm of paymentMethods.data) { - await stripeCli.paymentMethods.detach(pm.id); - } + // Delete existing payment method + const paymentMethods = await stripeCli.paymentMethods.list({ + customer: customer.processor?.id, + }); + for (const pm of paymentMethods.data) { + await stripeCli.paymentMethods.detach(pm.id); + } - const pm = await stripeCli.paymentMethods.create({ - type: "card", - card: { - token: "tok_chargeCustomerFail", - }, - }); - await stripeCli.paymentMethods.attach(pm.id, { - customer: customer.processor?.id, - }); + const pm = await stripeCli.paymentMethods.create({ + type: "card", + card: { + token: "tok_chargeCustomerFail", + }, + }); + await stripeCli.paymentMethods.attach(pm.id, { + customer: customer.processor?.id, + }); }; export const deleteAllStripeCustomers = async ({ - org, - env, + org, + env, }: { - org: Organization; - env: AppEnv; + org: Organization; + env: AppEnv; }) => { - const stripeCli = createStripeCli({ org, env }); + const stripeCli = createStripeCli({ org, env }); - const stripeCustomers = await stripeCli.customers.list({ - limit: 100, - }); + const stripeCustomers = await stripeCli.customers.list({ + limit: 100, + }); - if (stripeCustomers.data.length === 0) { - return; - } + if (stripeCustomers.data.length === 0) { + return; + } - let firstCustomer = stripeCustomers.data[0]; - if (firstCustomer.livemode) { - throw new RecaseError({ - message: "Cannot delete livemode customers", - code: ErrCode.StripeDeleteCustomerFailed, - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - }); - } + const firstCustomer = stripeCustomers.data[0]; + if (firstCustomer.livemode) { + throw new RecaseError({ + message: "Cannot delete livemode customers", + code: ErrCode.StripeDeleteCustomerFailed, + statusCode: StatusCodes.INTERNAL_SERVER_ERROR, + }); + } - let batchSize = 10; - for (let i = 0; i < stripeCustomers.data.length; i += batchSize) { - let batch = stripeCustomers.data.slice(i, i + batchSize); - await Promise.all(batch.map((c) => stripeCli.customers.del(c.id))); - console.log( - `Deleted ${i + batch.length}/${stripeCustomers.data.length} customers` - ); - } + const batchSize = 10; + for (let i = 0; i < stripeCustomers.data.length; i += batchSize) { + const batch = stripeCustomers.data.slice(i, i + batchSize); + await Promise.all(batch.map((c) => stripeCli.customers.del(c.id))); + console.log( + `Deleted ${i + batch.length}/${stripeCustomers.data.length} customers`, + ); + } }; diff --git a/server/src/external/stripe/stripeSubUtils.ts b/server/src/external/stripe/stripeSubUtils.ts index e6f5f7626..7d494ebc3 100644 --- a/server/src/external/stripe/stripeSubUtils.ts +++ b/server/src/external/stripe/stripeSubUtils.ts @@ -216,7 +216,9 @@ export const getStripeSchedules = async ({ const batchPricesGet = []; for (const item of schedule.phases[0].items) { - batchPricesGet.push(stripeCli.prices.retrieve(item.price as string)); + batchPricesGet.push( + stripeCli.prices.retrieve((item.price as Stripe.Price).id as string) + ); } const prices = await Promise.all(batchPricesGet); const interval = prices[0].recurring?.interval; diff --git a/server/src/external/stripe/stripeWebhooks.ts b/server/src/external/stripe/stripeWebhooks.ts index fb695fccf..d05cc56c7 100644 --- a/server/src/external/stripe/stripeWebhooks.ts +++ b/server/src/external/stripe/stripeWebhooks.ts @@ -9,7 +9,11 @@ import { handleCheckoutSessionCompleted } from "./webhookHandlers/handleCheckout import { handleSubscriptionUpdated } from "./webhookHandlers/handleSubUpdated.js"; import { handleSubDeleted } from "./webhookHandlers/handleSubDeleted.js"; import { handleSubCreated } from "./webhookHandlers/handleSubCreated.js"; -import { getStripeWebhookSecret } from "@/internal/orgs/orgUtils.js"; +import { + getStripeWebhookSecret, + isStripeConnected, + unsetOrgStripeKeys, +} from "@/internal/orgs/orgUtils.js"; import { handleInvoicePaid } from "./webhookHandlers/handleInvoicePaid.js"; import { handleRequestError } from "@/utils/errorUtils.js"; import { handleInvoiceCreated } from "./webhookHandlers/handleInvoiceCreated/handleInvoiceCreated.js"; @@ -22,6 +26,7 @@ import { deleteCusCache } from "@/internal/customers/cusCache/updateCachedCus.js import { CusService } from "@/internal/customers/CusService.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; import { handleInvoiceUpdated } from "./webhookHandlers/handleInvoiceUpdated.js"; +import { disconnectStripe } from "@/internal/orgs/handlers/handleDeleteStripe.js"; export const stripeWebhookRouter: Router = express.Router(); @@ -66,9 +71,11 @@ stripeWebhookRouter.post( request.env = env; org = data.org; - if (!org.stripe_config) { - console.log(`Org ${orgId} does not have a stripe config`); - response.status(200).send(`Org ${orgId} does not have a stripe config`); + if (!isStripeConnected({ org, env })) { + console.log(`Org ${orgId} and env ${env} is not connected to stripe`); + response + .status(200) + .send(`Org ${orgId} and env ${env} is not connected to stripe`); return; } @@ -115,15 +122,6 @@ stripeWebhookRouter.post( let logger = request.logtail; logStripeWebhook({ req: request, event }); - // const logger = createLogtailWithContext({ - // action: LoggerAction.StripeWebhook, - // event_type: event.type, - // data: event.data, - // org_id: orgId, - // org_slug: org.slug, - // env, - // }); - try { const stripeCli = createStripeCli({ org, env }); switch (event.type) { @@ -244,6 +242,18 @@ stripeWebhookRouter.post( response.status(200).json({ message: "ok" }); return; } + + if (error.message.includes("Expired API Key provided")) { + // Disconnect Stripe + await unsetOrgStripeKeys({ + db, + org, + env, + }); + + response.status(200).json({ message: "ok" }); + return; + } } handleRequestError({ diff --git a/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted.ts b/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted.ts index f76c66aa4..79f760f64 100644 --- a/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted.ts +++ b/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted.ts @@ -1,29 +1,32 @@ -import { Stripe } from "stripe"; -import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js"; -import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js"; -import { getMetadataFromCheckoutSession } from "@/internal/metadata/metadataUtils.js"; import { - AppEnv, + type AppEnv, AttachScenario, CusProductStatus, - Organization, + notNullish, + type Organization, } from "@autumn/shared"; -import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; -import { createStripeCli } from "../utils.js"; - +import type { Stripe } from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; +import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js"; +import { insertInvoiceFromAttach } from "@/internal/invoices/invoiceUtils.js"; +import { getMetadataFromCheckoutSession } from "@/internal/metadata/metadataUtils.js"; import { attachToInsertParams } from "@/internal/products/productUtils.js"; import { JobName } from "@/queue/JobName.js"; import { addTaskToQueue } from "@/queue/queueUtils.js"; -import { insertInvoiceFromAttach } from "@/internal/invoices/invoiceUtils.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { ExtendedRequest } from "@/utils/models/Request.js"; + +import type { ExtendedRequest } from "@/utils/models/Request.js"; + +import { createStripeCli } from "../utils.js"; + import { handleCheckoutSub } from "./handleCheckoutCompleted/handleCheckoutSub.js"; import { handleRemainingSets } from "./handleCheckoutCompleted/handleRemainingSets.js"; + +import { handleSetupCheckout } from "./handleCheckoutCompleted/handleSetupCheckout.js"; import { getOptionsFromCheckoutSession } from "./handleCheckoutCompleted/getOptionsFromCheckout.js"; import { getEarliestPeriodEnd } from "../stripeSubUtils/convertSubUtils.js"; -import { notNullish } from "@/utils/genUtils.js"; -import { CusService } from "@/internal/customers/CusService.js"; -import { handleSetupCheckout } from "./handleCheckoutCompleted/handleSetupCheckout.js"; export const handleCheckoutSessionCompleted = async ({ req, @@ -187,6 +190,8 @@ export const handleCheckoutSessionCompleted = async ({ console.log("✅ checkout.completed: successfully inserted invoices"); for (const product of attachParams.products) { + console.log("Adding task to queue for trigger checkout reward"); + console.log("Adding task to queue for trigger checkout reward"); await addTaskToQueue({ jobName: JobName.TriggerCheckoutReward, payload: { @@ -224,72 +229,5 @@ export const handleCheckoutSessionCompleted = async ({ }); } - // if ( - // !attachParams.customer.name && - // notNullish(checkoutSession.customer_details?.name) - // ) { - // updates.push( - // CusService.update({ - // db, - // internalCusId: attachParams.customer.internal_id, - // update: { - // name: checkoutSession.customer_details?.name, - // }, - // }) - // ); - // } - - // if ( - // !attachParams.customer.email && - // notNullish(checkoutSession.customer_details?.email) - // ) { - // updates.push( - // CusService.update({ - // db, - // internalCusId: attachParams.customer.internal_id, - // update: { - // email: checkoutSession.customer_details?.email, - // }, - // }) - // ); - // } - - // // Let it fail silently if any of the updates fail. - // if (updates.length > 0) await Promise.allSettled(updates); - return; }; - -// for (const invoiceId of invoiceIds) { -// try { -// const invoice = await getStripeExpandedInvoice({ -// stripeCli, -// stripeInvoiceId: invoiceId, -// }); - -// let invoiceItems = await getInvoiceItems({ -// stripeInvoice: invoice, -// prices: attachParams.prices, -// logger, -// }); - -// await InvoiceService.createInvoiceFromStripe({ -// db, -// org, -// stripeInvoice: invoice, -// internalCustomerId: attachParams.customer.internal_id, -// productIds: products.map((p) => p.id), -// internalProductIds: products.map((p) => p.internal_id), -// internalEntityId: attachParams.internalEntityId, -// items: invoiceItems, -// }); - -// console.log(" ✅ checkout.completed: successfully created invoice"); -// } catch (error) { -// console.error("checkout.completed: error creating invoice", error); -// } -// } - -// subscriptionId: !isOneOff -// ? (checkoutSession.subscription as string) -// : undefined, diff --git a/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted/getOptionsFromCheckout.ts b/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted/getOptionsFromCheckout.ts index dee8b52b6..de863259a 100644 --- a/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted/getOptionsFromCheckout.ts +++ b/server/src/external/stripe/webhookHandlers/handleCheckoutCompleted/getOptionsFromCheckout.ts @@ -1,5 +1,6 @@ import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; import { + formatPrice, getBillingType, getPriceEntitlement, priceIsOneOffAndTiered, @@ -32,9 +33,7 @@ export const getOptionsFromCheckoutSession = async ({ for (const price of prices) { let config = price.config as UsagePriceConfig; - if (getBillingType(config) != BillingType.UsageInAdvance) { - continue; - } + if (getBillingType(config) != BillingType.UsageInAdvance) continue; const lineItem = findStripeItemForPrice({ price, @@ -47,7 +46,8 @@ export const getOptionsFromCheckoutSession = async ({ let relatedEnt = getPriceEntitlement(price, ents); if (priceIsOneOffAndTiered(price, relatedEnt)) { - quantity = (lineItem.quantity || 0) + (relatedEnt.allowance || 0); + // quantity = lineItem.quantity || 0; + continue; } else { quantity = lineItem.quantity || 0; } diff --git a/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts b/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts index 9b33514f4..9b292e000 100644 --- a/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts +++ b/server/src/external/stripe/webhookHandlers/handleCusDiscountDeleted.ts @@ -1,107 +1,170 @@ +import type Stripe from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { CusService } from "@/internal/customers/CusService.js"; import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; -import { createStripeCli } from "../utils.js"; -import Stripe from "stripe"; -import { notNullish, timeout } from "@/utils/genUtils.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; import { RewardService } from "@/internal/rewards/RewardService.js"; +import { notNullish } from "@/utils/genUtils.js"; +import { createStripeCli } from "../utils.js"; export async function handleCusDiscountDeleted({ - db, - org, - discount, - env, - logger, - res, + db, + org, + discount, + env, + logger, + res, }: { - db: DrizzleCli; - org: any; - discount: any; - env: any; - logger: any; - res: any; + db: DrizzleCli; + org: any; + discount: any; + env: any; + logger: any; + res: any; }) { - let customer = await CusService.getByStripeId({ - db, - stripeId: discount.customer, - }); + const customer = await CusService.getByStripeId({ + db, + stripeId: discount.customer, + }); - if (!customer) { - logger.warn(`discount.deleted: customer ${discount.customer} not found`); - return; - } + if (!customer) { + logger.warn(`discount.deleted: customer ${discount.customer} not found`); + return; + } - if (customer.env !== env || customer.org_id !== org.id) { - logger.info(`discount.deleted: env or org mismatch, skipping`); - return; - } + if (customer.env !== env || customer.org_id !== org.id) { + logger.info( + `discount.deleted: env or org mismatch, skipping, ${customer.env} !== ${env} || ${customer.org_id} !== ${org.id}`, + ); + return; + } - // Check if any redemptions available, and apply to customer if so - let redemptions = await RewardRedemptionService.getUnappliedRedemptions({ - db, - internalCustomerId: customer.internal_id, - }); + // Check if any redemptions available, and apply to customer if so + const redemptions = await RewardRedemptionService.getUnappliedRedemptions({ + db, + internalCustomerId: customer.internal_id, + }); - logger.info(`discount.deleted: found ${redemptions.length} redemptions`); + logger.info( + `discount.deleted:, discount ID: ${discount.id}, found ${redemptions.length} redemptions`, + ); - if (redemptions.length == 0) { - return; - } + if (redemptions.length == 0) return; - let redemption = redemptions[0]; + const paidProductRedemption = redemptions.find( + (r) => + r.reward_program.reward.id === + (typeof discount.coupon == "string" + ? discount.coupon + : discount.coupon.id), + ); - // Apply redemption to customer - let stripeCli = createStripeCli({ - org, - env, - }); + if (discount.subscription) { + logger.info( + `Discount is a subscription, paidProductRedemption: ${paidProductRedemption?.id}`, + ); - let stripeCus = (await stripeCli.customers.retrieve( - discount.customer - )) as Stripe.Customer; + if (!paidProductRedemption) return; - if (stripeCus && notNullish(stripeCus.discount)) { - logger.info( - `discount.deleted: stripe customer ${discount.customer} already has a discount` - ); - return; - } + // Re-apply coupon and mark applied / redeemer applied to true + const stripeCli = createStripeCli({ + org, + env, + }); - const reward = await RewardService.get({ - db, - orgId: org.id, - env, - idOrInternalId: redemption.reward_program.internal_reward_id!, - }); + // Mark reward redemption as applied / redeemer applied to true - if (!reward) { - logger.warn( - `discount.deleted: reward ${redemption.reward_program.internal_id} not found` - ); - return; - } + const sub = await stripeCli.subscriptions.retrieve(discount.subscription); - const legacyStripe = createStripeCli({ - org, - env, - legacyVersion: true, - }); + // can't really test because it modifies subscription affected by test clock... + try { + await stripeCli.subscriptions.update(discount.subscription, { + discounts: [ + ...(sub.discounts as string[]).map((d: string) => ({ + discount: d, + })), + { + coupon: paidProductRedemption.reward_program.reward.id as string, + }, + ], + }); + } catch (error: any) { + logger.error( + `Failed to update subscription ${discount.subscription} with paid product coupon, error: ${error.message}`, + ); + throw error; + } - await legacyStripe.customers.update(discount.customer, { - // @ts-ignore - coupon: reward.internal_id, - }); + // Mark reward redemption as applied / redeemer applied to true + const isReferrer = + paidProductRedemption.referral_code.internal_customer_id === + customer.internal_id; - await RewardRedemptionService.update({ - db, - id: redemption.id, - updates: { - applied: true, - }, - }); + await RewardRedemptionService.update({ + db, + id: paidProductRedemption.id, + updates: { + applied: isReferrer ? true : undefined, + redeemer_applied: isReferrer ? undefined : true, + }, + }); - logger.info( - `discount.deleted: applied reward ${reward.name} on customer ${customer.name} (${customer.id})` - ); - logger.info(`Redemption ID: ${redemption.id}`); + return; + } + + const redemption = redemptions[0]; + + // Apply redemption to customer + const stripeCli = createStripeCli({ + org, + env, + }); + + const stripeCus = (await stripeCli.customers.retrieve( + discount.customer, + )) as Stripe.Customer; + + if (stripeCus && notNullish(stripeCus.discount)) { + logger.info( + `discount.deleted: stripe customer ${discount.customer} already has a discount`, + ); + return; + } + + const reward = await RewardService.get({ + db, + orgId: org.id, + env, + idOrInternalId: redemption.reward_program.internal_reward_id!, + }); + + if (!reward) { + logger.warn( + `discount.deleted: reward ${redemption.reward_program.internal_id} not found`, + ); + return; + } + + const legacyStripe = createStripeCli({ + org, + env, + legacyVersion: true, + }); + + await legacyStripe.customers.update(discount.customer, { + // @ts-expect-error + coupon: reward.id, + }); + + await RewardRedemptionService.update({ + db, + id: redemption.id, + updates: { + applied: true, + }, + }); + + logger.info( + `discount.deleted: applied reward ${reward.name} on customer ${customer.name} (${customer.id})`, + ); + logger.info(`Redemption ID: ${redemption.id}`); } diff --git a/server/src/external/stripe/webhookHandlers/handleInvoiceCreated/handleInvoiceCreated.ts b/server/src/external/stripe/webhookHandlers/handleInvoiceCreated/handleInvoiceCreated.ts index 3aa29ef52..480bb584c 100644 --- a/server/src/external/stripe/webhookHandlers/handleInvoiceCreated/handleInvoiceCreated.ts +++ b/server/src/external/stripe/webhookHandlers/handleInvoiceCreated/handleInvoiceCreated.ts @@ -130,9 +130,6 @@ const handleInArrearProrated = async ({ } } - console.log("New entities: ", newEntities); - console.log("Cus ent ID: ", linkedCusEnt.id); - let updated = await CusEntService.update({ db, id: linkedCusEnt.id, diff --git a/server/src/external/stripe/webhookHandlers/handleInvoicePaid.ts b/server/src/external/stripe/webhookHandlers/handleInvoicePaid.ts index 9abaaab75..6c727f283 100644 --- a/server/src/external/stripe/webhookHandlers/handleInvoicePaid.ts +++ b/server/src/external/stripe/webhookHandlers/handleInvoicePaid.ts @@ -1,14 +1,18 @@ -import Stripe from "stripe"; -import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js"; -import { +import type { AppEnv, FullCusProduct, FullCustomerPrice, InvoiceStatus, Organization, } from "@autumn/shared"; -import { createStripeCli } from "../utils.js"; - +import type Stripe from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import { handleInvoiceCheckoutPaid } from "@/internal/customers/attach/attachFunctions/invoiceCheckoutPaid/handleInvoiceCheckoutPaid.js"; +import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js"; +import { InvoiceService } from "@/internal/invoices/InvoiceService.js"; +import { getInvoiceItems } from "@/internal/invoices/invoiceUtils.js"; +import { JobName } from "@/queue/JobName.js"; +import { addTaskToQueue } from "@/queue/queueUtils.js"; import { nullish } from "@/utils/genUtils.js"; import { getFullStripeInvoice, @@ -16,282 +20,277 @@ import { invoiceToSubId, updateInvoiceIfExists, } from "../stripeInvoiceUtils.js"; -import { getStripeSubs } from "../stripeSubUtils.js"; -import { addTaskToQueue } from "@/queue/queueUtils.js"; -import { JobName } from "@/queue/JobName.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { InvoiceService } from "@/internal/invoices/InvoiceService.js"; -import { getInvoiceItems } from "@/internal/invoices/invoiceUtils.js"; -import { handleInvoicePaidDiscount } from "./handleInvoicePaidDiscount.js"; -import { handleInvoiceCheckoutPaid } from "@/internal/customers/attach/attachFunctions/invoiceCheckoutPaid/handleInvoiceCheckoutPaid.js"; import { lineItemInCusProduct } from "../stripeSubUtils/stripeSubItemUtils.js"; +import { getStripeSubs } from "../stripeSubUtils.js"; +import { createStripeCli } from "../utils.js"; +import { handleInvoicePaidDiscount } from "./handleInvoicePaidDiscount.js"; const handleOneOffInvoicePaid = async ({ - db, - stripeInvoice, - logger, + db, + stripeInvoice, + logger, }: { - db: DrizzleCli; - stripeInvoice: Stripe.Invoice; - event: Stripe.Event; - logger: any; + db: DrizzleCli; + stripeInvoice: Stripe.Invoice; + event: Stripe.Event; + logger: any; }) => { - // Search for invoice - const invoice = await InvoiceService.getByStripeId({ - db, - stripeId: stripeInvoice.id!, - }); + // Search for invoice + const invoice = await InvoiceService.getByStripeId({ + db, + stripeId: stripeInvoice.id!, + }); - if (!invoice) { - console.log(`Invoice not found`); - return; - } + if (!invoice) { + console.log(`Invoice not found`); + return; + } - // Update invoice status - await InvoiceService.updateByStripeId({ - db, - stripeId: stripeInvoice.id!, - updates: { - status: stripeInvoice.status as InvoiceStatus, - hosted_invoice_url: stripeInvoice.hosted_invoice_url, - discounts: getInvoiceDiscounts({ - expandedInvoice: stripeInvoice, - }), - }, - }); + // Update invoice status + await InvoiceService.updateByStripeId({ + db, + stripeId: stripeInvoice.id!, + updates: { + status: stripeInvoice.status as InvoiceStatus, + hosted_invoice_url: stripeInvoice.hosted_invoice_url, + discounts: getInvoiceDiscounts({ + expandedInvoice: stripeInvoice, + }), + }, + }); - console.log(`Updated one off invoice status to ${stripeInvoice.status}`); + console.log(`Updated one off invoice status to ${stripeInvoice.status}`); }; const convertToChargeAutomatically = async ({ - org, - env, - invoice, - activeCusProducts, - logger, + org, + env, + invoice, + activeCusProducts, + logger, }: { - org: Organization; - env: AppEnv; - invoice: Stripe.Invoice; - activeCusProducts: FullCusProduct[]; - logger: any; + org: Organization; + env: AppEnv; + invoice: Stripe.Invoice; + activeCusProducts: FullCusProduct[]; + logger: any; }) => { - const stripeCli = createStripeCli({ org, env }); + const stripeCli = createStripeCli({ org, env }); - const subs = await getStripeSubs({ - stripeCli, - subIds: activeCusProducts.flatMap((p) => p.subscription_ids || []), - }); + const subs = await getStripeSubs({ + stripeCli, + subIds: activeCusProducts.flatMap((p) => p.subscription_ids || []), + }); - const payments = invoice.payments; - const firstPayment = payments?.data?.[0]; - const paymentIntentId = firstPayment?.payment?.payment_intent as string; + const payments = invoice.payments; + const firstPayment = payments?.data?.[0]; + const paymentIntentId = firstPayment?.payment?.payment_intent as string; - if ( - subs.every((s) => s.collection_method === "charge_automatically") || - nullish(paymentIntentId) - ) { - return; - } + if ( + subs.every((s) => s.collection_method === "charge_automatically") || + nullish(paymentIntentId) + ) { + return; + } - // Get payment intent... + // Get payment intent... - // Try to attach payment method to subscription - try { - logger.info(`Converting to charge automatically`); - // 1. Get payment intent - const paymentIntent = - await stripeCli.paymentIntents.retrieve(paymentIntentId); + // Try to attach payment method to subscription + try { + logger.info(`Converting to charge automatically`); + // 1. Get payment intent + const paymentIntent = + await stripeCli.paymentIntents.retrieve(paymentIntentId); - // 2. Get payment method - const paymentMethod = await stripeCli.paymentMethods.retrieve( - paymentIntent.payment_method as string - ); + // 2. Get payment method + const paymentMethod = await stripeCli.paymentMethods.retrieve( + paymentIntent.payment_method as string, + ); - await stripeCli.paymentMethods.attach(paymentMethod.id, { - customer: invoice.customer as string, - }); + await stripeCli.paymentMethods.attach(paymentMethod.id, { + customer: invoice.customer as string, + }); - const batchUpdateSubs = []; - const updateSub = async (sub: Stripe.Subscription) => { - try { - await stripeCli.subscriptions.update(sub.id, { - collection_method: "charge_automatically", - default_payment_method: paymentMethod.id, - }); - } catch (error) { - logger.warn( - `Convert to charge automatically: error updating subscription ${sub.id}` - ); - logger.warn(error); - } - }; + const batchUpdateSubs = []; + const updateSub = async (sub: Stripe.Subscription) => { + try { + await stripeCli.subscriptions.update(sub.id, { + collection_method: "charge_automatically", + default_payment_method: paymentMethod.id, + }); + } catch (error) { + logger.warn( + `Convert to charge automatically: error updating subscription ${sub.id}`, + ); + logger.warn(error); + } + }; - for (const sub of subs) { - batchUpdateSubs.push(updateSub(sub)); - } + for (const sub of subs) { + batchUpdateSubs.push(updateSub(sub)); + } - await Promise.all(batchUpdateSubs); + await Promise.all(batchUpdateSubs); - logger.info("Convert to charge automatically successful!"); - } catch (error) { - logger.warn(`Convert to charge automatically failed: ${error}`); - } + logger.info("Convert to charge automatically successful!"); + } catch (error) { + logger.warn(`Convert to charge automatically failed: ${error}`); + } }; export const handleInvoicePaid = async ({ - db, - req, - org, - invoiceData, - env, - event, + db, + req, + org, + invoiceData, + env, + event, }: { - db: DrizzleCli; - req: any; - org: Organization; - invoiceData: Stripe.Invoice; - env: AppEnv; - event: Stripe.Event; + db: DrizzleCli; + req: any; + org: Organization; + invoiceData: Stripe.Invoice; + env: AppEnv; + event: Stripe.Event; }) => { - const logger = req.logtail; - const stripeCli = createStripeCli({ org, env }); - const invoice = await getFullStripeInvoice({ - stripeCli, - stripeId: invoiceData.id!, - expand: ["payments"], - }); + const logger = req.logtail; + const stripeCli = createStripeCli({ org, env }); + const invoice = await getFullStripeInvoice({ + stripeCli, + stripeId: invoiceData.id!, + expand: ["payments"], + }); - if (invoice.metadata?.autumn_metadata_id) { - await handleInvoiceCheckoutPaid({ - req, - org, - env, - db, - stripeCli, - invoice, - }); - } + if (invoice.metadata?.autumn_metadata_id) { + await handleInvoiceCheckoutPaid({ + req, + org, + env, + db, + stripeCli, + invoice, + }); + } - await handleInvoicePaidDiscount({ - db, - expandedInvoice: invoice, - org, - env, - logger, - }); + await handleInvoicePaidDiscount({ + db, + expandedInvoice: invoice, + org, + env, + logger, + }); - const subId = invoiceToSubId({ invoice }); - if (subId) { - // Get customer product - const activeCusProducts = await CusProductService.getByStripeSubId({ - db, - stripeSubId: subId, - orgId: org.id, - env, - }); + const subId = invoiceToSubId({ invoice }); + if (subId) { + // Get customer product + const activeCusProducts = await CusProductService.getByStripeSubId({ + db, + stripeSubId: subId, + orgId: org.id, + env, + }); - if (!activeCusProducts || activeCusProducts.length === 0) { - // TODO: Send alert - if (invoice.livemode) { - logger.warn( - `invoice.paid: customer product not found for invoice ${invoice.id}` - ); - } - return; - } + if (!activeCusProducts || activeCusProducts.length === 0) { + // TODO: Send alert + if (invoice.livemode) { + logger.warn( + `invoice.paid: customer product not found for invoice ${invoice.id}`, + ); + } + return; + } - if (org.config.convert_to_charge_automatically) { - await convertToChargeAutomatically({ - org, - env, - invoice, - activeCusProducts, - logger, - }); - } + if (org.config.convert_to_charge_automatically) { + await convertToChargeAutomatically({ + org, + env, + invoice, + activeCusProducts, + logger, + }); + } - const updated = await updateInvoiceIfExists({ - db, - invoice, - }); + const updated = await updateInvoiceIfExists({ + db, + invoice, + }); - if (!updated) { - let invoiceItems = await getInvoiceItems({ - stripeInvoice: invoice, - prices: activeCusProducts.flatMap((p) => - p.customer_prices.map((cpr: FullCustomerPrice) => cpr.price) - ), - logger, - }); + if (!updated) { + const invoiceItems = await getInvoiceItems({ + stripeInvoice: invoice, + prices: activeCusProducts.flatMap((p) => + p.customer_prices.map((cpr: FullCustomerPrice) => cpr.price), + ), + logger, + }); - const invoiceLines = invoice.lines.data; - let cusProducts: FullCusProduct[] = activeCusProducts; - try { - cusProducts = activeCusProducts.filter((cp) => - invoiceLines.some((l) => - lineItemInCusProduct({ cusProduct: cp, lineItem: l }) - ) - ); + const invoiceLines = invoice.lines.data; + let cusProducts: FullCusProduct[] = activeCusProducts; + try { + cusProducts = activeCusProducts.filter((cp) => + invoiceLines.some((l) => + lineItemInCusProduct({ cusProduct: cp, lineItem: l }), + ), + ); - console.log( - "Invoice paid, filtered cus products:", - cusProducts.map((cp) => `${cp.product.name} - ${cp.product.id}`) - ); + console.log( + "Invoice paid, filtered cus products:", + cusProducts.map((cp) => `${cp.product.name} - ${cp.product.id}`), + ); - if (cusProducts.length == 0) { - cusProducts = activeCusProducts; - } - } catch (error) { - logger.error("Failed to filter cus products for invoice"); - logger.error({ error }); - } + if (cusProducts.length == 0) { + cusProducts = activeCusProducts; + } + } catch (error) { + logger.error("Failed to filter cus products for invoice"); + logger.error({ error }); + } - const internalEntityId = new Set( - cusProducts.map((cp) => cp.internal_entity_id) - ); + const internalEntityId = new Set( + cusProducts.map((cp) => cp.internal_entity_id), + ); - await InvoiceService.createInvoiceFromStripe({ - db, - stripeInvoice: invoice, - internalCustomerId: activeCusProducts[0].internal_customer_id, - internalEntityId: - internalEntityId.size > 1 - ? undefined - : internalEntityId.values().next().value, + await InvoiceService.createInvoiceFromStripe({ + db, + stripeInvoice: invoice, + internalCustomerId: activeCusProducts[0].internal_customer_id, + internalEntityId: + internalEntityId.size > 1 + ? undefined + : internalEntityId.values().next().value, - productIds: [...new Set(cusProducts.map((p) => p.product_id))], - internalProductIds: [ - ...new Set(cusProducts.map((p) => p.internal_product_id)), - ], - org: org, - items: invoiceItems, - }); - } + productIds: [...new Set(cusProducts.map((p) => p.product_id))], + internalProductIds: [ + ...new Set(cusProducts.map((p) => p.internal_product_id)), + ], + org: org, + items: invoiceItems, + }); + } - for (const cusProd of activeCusProducts) { - try { - await addTaskToQueue({ - jobName: JobName.TriggerCheckoutReward, - payload: { - customer: cusProd.customer, - product: cusProd.product, - org, - env: cusProd.customer!.env, - subId: cusProd.subscription_ids?.[0], - }, - }); - } catch (error) { - logger.error(`invoice.paid: failed to trigger checkout reward check`); - logger.error(error); - } - } - } else { - await handleOneOffInvoicePaid({ - db, - stripeInvoice: invoice, - event, - logger, - }); - } + for (const cusProd of activeCusProducts) { + try { + await addTaskToQueue({ + jobName: JobName.TriggerCheckoutReward, + payload: { + customer: cusProd.customer, + product: cusProd.product, + org, + env: cusProd.customer!.env, + subId: cusProd.subscription_ids?.[0], + }, + }); + } catch (error) { + logger.error(`invoice.paid: failed to trigger checkout reward check`); + logger.error(error); + } + } + } else { + await handleOneOffInvoicePaid({ + db, + stripeInvoice: invoice, + event, + logger, + }); + } }; diff --git a/server/src/external/stripe/webhookHandlers/handleInvoicePaidDiscount.ts b/server/src/external/stripe/webhookHandlers/handleInvoicePaidDiscount.ts index 9a37eb8e7..bf781198a 100644 --- a/server/src/external/stripe/webhookHandlers/handleInvoicePaidDiscount.ts +++ b/server/src/external/stripe/webhookHandlers/handleInvoicePaidDiscount.ts @@ -1,181 +1,183 @@ -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { RewardService } from "@/internal/rewards/RewardService.js"; import { + type AppEnv, CouponDurationType, - Organization, - Reward, + type Organization, + type Reward, RewardType, } from "@autumn/shared"; -import { AppEnv } from "@autumn/shared"; +import { addMonths } from "date-fns"; +import { Decimal } from "decimal.js"; -import Stripe from "stripe"; +import type Stripe from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; +import { generateId } from "@/utils/genUtils.js"; +import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js"; import { deleteCouponFromCus, deleteCouponFromSub, } from "../stripeCouponUtils/deleteCouponFromCus.js"; -import { createStripeCli } from "../utils.js"; -import { Decimal } from "decimal.js"; -import { generateId } from "@/utils/genUtils.js"; -import { addMonths } from "date-fns"; -import { getStripeNow } from "@/utils/scriptUtils/testClockUtils.js"; import { invoiceToSubId } from "../stripeInvoiceUtils.js"; +import { createStripeCli } from "../utils.js"; export const handleInvoicePaidDiscount = async ({ - db, - expandedInvoice, - org, - env, - logger, + db, + expandedInvoice, + org, + env, + logger, }: { - db: DrizzleCli; - expandedInvoice: Stripe.Invoice; - org: Organization; - env: AppEnv; - logger: any; + db: DrizzleCli; + expandedInvoice: Stripe.Invoice; + org: Organization; + env: AppEnv; + logger: any; }) => { - // Handle coupon - const stripeCli = createStripeCli({ org, env }); - if (expandedInvoice.discounts.length === 0) { - return; - } + // Handle coupon + const stripeCli = createStripeCli({ org, env }); + if (expandedInvoice.discounts.length === 0) { + return; + } - let stripeCus = await stripeCli.customers.retrieve( - expandedInvoice.customer as string - ); + const stripeCus = await stripeCli.customers.retrieve( + expandedInvoice.customer as string, + ); - try { - const totalDiscountAmounts = expandedInvoice.total_discount_amounts; + try { + const totalDiscountAmounts = expandedInvoice.total_discount_amounts; - // Log coupon information for debugging - for (const discount of expandedInvoice.discounts) { - if (typeof discount === "string") { - continue; - } + // Log coupon information for debugging + for (const discount of expandedInvoice.discounts) { + if (typeof discount === "string") { + continue; + } - const curCoupon = discount.coupon; + const curCoupon = discount.coupon; - if (!curCoupon) { - continue; - } + if (!curCoupon) { + continue; + } - const rollSuffixIndex = curCoupon.id.indexOf("_roll_"); - const couponId = - rollSuffixIndex !== -1 - ? curCoupon.id.substring(0, rollSuffixIndex) - : curCoupon.id; + const rollSuffixIndex = curCoupon.id.indexOf("_roll_"); + const couponId = + rollSuffixIndex !== -1 + ? curCoupon.id.substring(0, rollSuffixIndex) + : curCoupon.id; - const autumnReward: Reward | null = await RewardService.get({ - db, - idOrInternalId: couponId, - orgId: org.id, - env, - }); + const autumnReward: Reward | null = await RewardService.get({ + db, + idOrInternalId: couponId, + orgId: org.id, + env, + }); - let shouldRollover = - autumnReward && autumnReward.type == RewardType.InvoiceCredits; + const shouldRollover = + autumnReward && + (autumnReward.type === RewardType.InvoiceCredits || + autumnReward.type === RewardType.FreeProduct); - if (!shouldRollover) { - continue; - } + if (!shouldRollover) { + continue; + } - // Get ID of coupon - const originalCoupon = await stripeCli.coupons.retrieve(couponId, { - expand: ["applies_to"], - }); + // Get ID of coupon + const originalCoupon = await stripeCli.coupons.retrieve(couponId, { + expand: ["applies_to"], + }); - // 1. New amount: - const curAmount = discount.coupon.amount_off; + // 1. New amount: + const curAmount = discount.coupon.amount_off; - const amountUsed = totalDiscountAmounts?.find( - (item) => item.discount === discount.id - )?.amount; + const amountUsed = totalDiscountAmounts?.find( + (item) => item.discount === discount.id, + )?.amount; - const newAmount = new Decimal(curAmount!).sub(amountUsed!).toNumber(); + const newAmount = new Decimal(curAmount!).sub(amountUsed!).toNumber(); - let curExpiresAt = curCoupon.metadata?.expires_at - ? Number(curCoupon.metadata.expires_at) - : null; + const curExpiresAt = curCoupon.metadata?.expires_at + ? Number(curCoupon.metadata.expires_at) + : null; - let discountFinished = newAmount <= 0; + const discountFinished = newAmount <= 0; - let now = await getStripeNow({ - stripeCli, - stripeCus: stripeCus as Stripe.Customer, - }); + const now = await getStripeNow({ + stripeCli, + stripeCus: stripeCus as Stripe.Customer, + }); - let expired = curExpiresAt && curExpiresAt < now; - const subId = invoiceToSubId({ invoice: expandedInvoice }); + const expired = curExpiresAt && curExpiresAt < now; + const subId = invoiceToSubId({ invoice: expandedInvoice }); - if (discountFinished || expired) { - logger.info( - `Coupon ${couponId}, stripeCus: ${stripeCus.id}: credits used up or expired. discountFinished: ${discountFinished}, expired: ${expired}` - ); + if (discountFinished || expired) { + logger.info( + `Coupon ${couponId}, stripeCus: ${stripeCus.id}: credits used up or expired. discountFinished: ${discountFinished}, expired: ${expired}`, + ); - if (subId) { - await deleteCouponFromCus({ - stripeCli, - stripeSubId: subId, - stripeCusId: expandedInvoice.customer as string, - discountId: discount.id, - logger, - }); - } + if (subId) { + await deleteCouponFromCus({ + stripeCli, + stripeSubId: subId, + stripeCusId: expandedInvoice.customer as string, + discountId: discount.id, + logger, + }); + } - continue; - } + continue; + } - logger.info( - `Coupon ${couponId}, stripeCus: ${stripeCus.id}, updating amount from ${curAmount} to ${newAmount}` - ); + logger.info( + `Coupon ${couponId}, stripeCus: ${stripeCus.id}, updating amount from ${curAmount} to ${newAmount}`, + ); - // Set expiry date - let expiresAt = curCoupon.metadata?.expires_at || null; - let discountConfig = autumnReward?.discount_config; - if (discountConfig?.duration_type == CouponDurationType.Months) { - expiresAt = addMonths(new Date(), discountConfig.duration_value) - .getTime() - .toString(); - } + // Set expiry date + let expiresAt = curCoupon.metadata?.expires_at || null; + const discountConfig = autumnReward?.discount_config; + if (discountConfig?.duration_type === CouponDurationType.Months) { + expiresAt = addMonths(new Date(), discountConfig.duration_value) + .getTime() + .toString(); + } - const newCoupon = await stripeCli.coupons.create({ - id: `${couponId}_${generateId("roll")}`, - name: discount.coupon.name as string, - amount_off: newAmount, - currency: expandedInvoice.currency, - duration: "once", - applies_to: originalCoupon.applies_to, - metadata: { - expires_at: expiresAt, - }, - }); + const newCoupon = await stripeCli.coupons.create({ + id: `${couponId}_${generateId("roll")}`, + name: discount.coupon.name as string, + amount_off: newAmount, + currency: expandedInvoice.currency, + duration: "once", + applies_to: originalCoupon.applies_to, + metadata: { + expires_at: expiresAt, + }, + }); - const legacyStripeCli = createStripeCli({ - org, - env, - legacyVersion: true, - }); + const legacyStripeCli = createStripeCli({ + org, + env, + legacyVersion: true, + }); - await legacyStripeCli.rawRequest( - "POST", - `/v1/customers/${expandedInvoice.customer}`, - { - coupon: newCoupon.id, - } - ); + await legacyStripeCli.rawRequest( + "POST", + `/v1/customers/${expandedInvoice.customer}`, + { + coupon: newCoupon.id, + }, + ); - await stripeCli.coupons.del(newCoupon.id); + await stripeCli.coupons.del(newCoupon.id); - if (subId) { - await deleteCouponFromSub({ - stripeCli, - stripeSubId: subId, - discountId: discount.id, - logger, - }); - } - } - } catch (error) { - logger.error("invoice.paid: error updating coupon"); - logger.error(error); - } + if (subId) { + await deleteCouponFromSub({ + stripeCli, + stripeSubId: subId, + discountId: discount.id, + logger, + }); + } + } + } catch (error) { + logger.error("invoice.paid: error updating coupon"); + logger.error(error); + } }; diff --git a/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSchedulePhaseCompleted.ts b/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSchedulePhaseCompleted.ts index 5f02029a8..b16e73320 100644 --- a/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSchedulePhaseCompleted.ts +++ b/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSchedulePhaseCompleted.ts @@ -121,11 +121,10 @@ export const handleSchedulePhaseCompleted = async ({ scheduled_ids: [], }, }); - } catch (error) { + } catch (error: any) { logger.error( - `schedule.phase.completed: failed to cancel schedule ${schedule.id}` + `schedule.phase.completed: failed to cancel schedule ${schedule.id}, error: ${error.message}` ); - logger.error({ error }); } } // const currentPhase = schedule.phases.find( diff --git a/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubCanceled.ts b/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubCanceled.ts index 94900bc0e..82c66fe5d 100644 --- a/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubCanceled.ts +++ b/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubCanceled.ts @@ -102,9 +102,7 @@ export const handleSubCanceled = async ({ const { db, env, logtail: logger } = req; - if (!canceledFromPortal || updatedCusProducts.length == 0) { - return; - } + if (!canceledFromPortal || updatedCusProducts.length == 0) return; await updateCusProductCanceled({ db, @@ -194,6 +192,8 @@ export const handleSubCanceled = async ({ } for (let cusProd of updatedCusProducts) { + console.log("Sending webhook for canceled product: ", cusProd.product.id); + try { await addProductsUpdatedWebhookTask({ req, diff --git a/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubRenewed.ts b/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubRenewed.ts index df91bd565..0def51dbe 100644 --- a/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubRenewed.ts +++ b/server/src/external/stripe/webhookHandlers/handleSubUpdated/handleSubRenewed.ts @@ -7,6 +7,7 @@ import { AttachScenario, FullCusProduct } from "@autumn/shared"; import Stripe from "stripe"; import { isMultiProductSub } from "@/internal/customers/attach/mergeUtils/mergeUtils.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; +import { getSubScenarioFromCache } from "@/internal/customers/cusCache/subCacheUtils.js"; const isSubRenewed = ({ previousAttributes, sub, @@ -70,12 +71,18 @@ export const handleSubRenewed = async ({ if (!renewed || updatedCusProducts.length == 0) return; + const subScenario = await getSubScenarioFromCache({ subId: sub.id }); + console.log(`Renewed: ${renewed}, subScenario: ${subScenario}`); + if (subScenario === AttachScenario.Renew) return; + const customer = updatedCusProducts[0].customer; let cusProducts = await CusProductService.list({ db, internalCustomerId: customer!.internal_id, }); + console.log(`handling sub.renewed!`); + if (isMultiProductSub({ sub, cusProducts }) || sub.schedule) return; await CusProductService.updateByStripeSubId({ diff --git a/server/src/internal/analytics/handlers/handleProductsUpdated.ts b/server/src/internal/analytics/handlers/handleProductsUpdated.ts index ba10f4967..6098162b5 100644 --- a/server/src/internal/analytics/handlers/handleProductsUpdated.ts +++ b/server/src/internal/analytics/handlers/handleProductsUpdated.ts @@ -2,9 +2,9 @@ import { ActionType, AppEnv, AuthType, - CusProductStatus, FullCusProduct, FullProduct, + notNullish, Organization, } from "@autumn/shared"; @@ -23,7 +23,8 @@ import { ActionService } from "@/internal/analytics/ActionService.js"; import { constructAction } from "@/internal/analytics/actionUtils.js"; import { parseReqForAction } from "@/internal/analytics/actionUtils.js"; import { RELEVANT_STATUSES } from "@/internal/customers/cusProducts/CusProductService.js"; -import { cusProductToPrices, cusProductToProduct } from "@autumn/shared"; +import { cusProductToProduct } from "@autumn/shared"; +import { getSingleEntityResponse } from "@/internal/api/entities/getEntityUtils.js"; interface ActionDetails { request_id: string; @@ -121,19 +122,8 @@ export const handleProductsUpdated = async ({ // Product: let product = cusProduct.product; - // const prices = cusProductToPrices({ cusProduct }); - // const ents = cusProductToEnts({ cusProduct }); - // let freeTrial = cusProduct.free_trial; - let fullProduct: FullProduct = cusProductToProduct({ cusProduct }); - // { - // ...product, - // prices, - // entitlements: ents, - // free_trial: freeTrial || null, - // }; - let customer = await CusService.getFull({ db, idOrInternalId: data.customerId || data.internalCustomerId, @@ -165,9 +155,6 @@ export const handleProductsUpdated = async ({ features, }); - // 1. Log action to DB - - // console.log(`handling products.updated for customer ${customer.id}`); try { if (req) { let action = constructAction({ @@ -205,6 +192,28 @@ export const handleProductsUpdated = async ({ } } + let entityRes = null; + if (notNullish(customer?.entity)) { + entityRes = await getSingleEntityResponse({ + entityId: customer.entity!.id, + org, + env, + fullCus: customer, + entity: customer.entity!, + features, + }); + } + + // console.log(`Sending svix event for customer ${customer.id}`); + // console.log( + // "Products:", + // cusDetails.products.map((p) => ({ + // id: p.id, + // status: p.status, + // quantity: p.quantity, + // })) + // ); + // 2. Send Svix event await sendSvixEvent({ org, @@ -213,6 +222,7 @@ export const handleProductsUpdated = async ({ data: { scenario, customer: cusDetails, + entity: entityRes, updated_product: productRes, }, }); diff --git a/server/src/internal/analytics/runActionHandlerTask.ts b/server/src/internal/analytics/runActionHandlerTask.ts index eb2528b45..619b3010c 100644 --- a/server/src/internal/analytics/runActionHandlerTask.ts +++ b/server/src/internal/analytics/runActionHandlerTask.ts @@ -43,11 +43,11 @@ export const runActionHandlerTask = async ({ }); break; } - } catch (error) { + } catch (error: any) { logger.error("Error processing action handler job:", { - jobName: job.name, - error, - payload, + // jobName: job.name, + // payload, + message: error.message, }); } finally { await releaseLock({ lockKey, useBackup }); diff --git a/server/src/internal/api/entities/getEntityUtils.ts b/server/src/internal/api/entities/getEntityUtils.ts index be129258f..459363200 100644 --- a/server/src/internal/api/entities/getEntityUtils.ts +++ b/server/src/internal/api/entities/getEntityUtils.ts @@ -1,10 +1,5 @@ import type { DrizzleCli } from "@/db/initDrizzle.js"; import { getCusWithCache } from "@/internal/customers/cusCache/getCusWithCache.js"; -import { - ACTIVE_STATUSES, - RELEVANT_STATUSES, -} from "@/internal/customers/cusProducts/CusProductService.js"; -import { CusService } from "@/internal/customers/CusService.js"; import { getCusFeaturesResponse } from "@/internal/customers/cusUtils/cusFeatureResponseUtils/getCusFeaturesResponse.js"; import { processFullCusProducts } from "@/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.js"; @@ -13,7 +8,6 @@ import { nullish } from "@/utils/genUtils.js"; import { type AppEnv, Feature, - CusProductStatus, type Entity, EntityExpand, type EntityResponse, @@ -32,6 +26,7 @@ export const getSingleEntityResponse = async ({ org, env, fullCus, + entity, features, withAutumnId = false, }: { @@ -39,13 +34,10 @@ export const getSingleEntityResponse = async ({ org: Organization; env: AppEnv; fullCus: FullCustomer; + entity: Entity; features: Feature[]; withAutumnId?: boolean; }) => { - let entity = fullCus.entities.find( - (e: Entity) => e.id == entityId || e.internal_id == entityId - ); - const apiVersion = APIVersion.v1_2; if (!entity) { @@ -80,7 +72,7 @@ export const getSingleEntityResponse = async ({ let { main, addOns } = await processFullCusProducts({ fullCusProducts: entityCusProducts, - entities: fullCus.entities, + entity, subs: entitySubs, org, apiVersion: APIVersion.v1_2, @@ -136,7 +128,7 @@ export const getEntityResponse = async ({ logger: any; skipCache?: boolean; }) => { - let customer = await getCusWithCache({ + let fullCus = await getCusWithCache({ db, idOrInternalId: customerId, org, @@ -147,7 +139,7 @@ export const getEntityResponse = async ({ skipCache, }); - if (!customer) { + if (!fullCus) { throw new RecaseError({ message: `Customer ${customerId} not found`, code: ErrCode.CustomerNotFound, @@ -156,12 +148,26 @@ export const getEntityResponse = async ({ } const entityResponses: EntityResponse[] = []; + for (const entityId of entityIds) { + const entity = fullCus.entities.find( + (e: Entity) => e.id == entityId || e.internal_id == entityId + ); + + if (!entity) { + throw new RecaseError({ + message: `Entity ${entityId} not found for customer ${fullCus.id}`, + code: ErrCode.EntityNotFound, + statusCode: 400, + }); + } + let entityResponse = await getSingleEntityResponse({ entityId, org, env, - fullCus: customer, + fullCus, + entity, features, withAutumnId, }); @@ -171,8 +177,8 @@ export const getEntityResponse = async ({ return { entities: entityResponses, - customer, - fullEntities: customer.entities, - invoices: customer.invoices, + customer: fullCus, + fullEntities: fullCus.entities, + invoices: fullCus.invoices, }; }; diff --git a/server/src/internal/api/entities/handlers/handleDeleteEntity.ts b/server/src/internal/api/entities/handlers/handleDeleteEntity.ts index fb78600c4..b7e8cc102 100644 --- a/server/src/internal/api/entities/handlers/handleDeleteEntity.ts +++ b/server/src/internal/api/entities/handlers/handleDeleteEntity.ts @@ -172,99 +172,6 @@ export const handleDeleteEntity = async (req: any, res: any) => { return res.status(200).json({ success: true, }); - - // const linkedCusEnts = findLinkedCusEnts({ - // cusEnts: cusEnt.customer_product.customer_entitlements, - // feature: cusEnt.entitlement.feature, - // }); - - // if (!cusPriceExists || org.config.prorate_unused) { - // let cusEnts = cusProductsToCusEnts({ cusProducts }); - // for (const cusEnt of cusEnts) { - // let relatedCusPrice = getRelatedCusPrice( - // cusEnt, - // cusProducts.flatMap((p: any) => p.customer_prices), - // ); - // await removeEntityFromCusEnt({ - // db, - // cusEnt, - // entity, - // logger, - // cusPrice: relatedCusPrice, - // customer, - // org, - // env, - // }); - // } - - // try { - // let stripeCli = createStripeCli({ org, env }); - // let curSubs = await getStripeSubs({ - // stripeCli, - // subIds: cusProducts.flatMap((p: any) => p.subscription_ids), - // }); - - // for (const cusProduct of cusProducts) { - // if (cusProduct.internal_entity_id !== entity.internal_id) { - // continue; - // } - - // if (cusProduct.status == CusProductStatus.Scheduled) { - // await removeScheduledProduct({ - // req, - // db, - // cusProduct, - // cusProducts, - // org, - // env, - // logger, - // renewCurProduct: false, - // }); - // } else { - // await cancelCurSubs({ - // curCusProduct: cusProduct, - // curSubs, - // stripeCli, - // }); - // } - // } - // } catch (error) { - // logger.error("FAILED TO CANCEL SUBS FOR DELETED ENTITY", error); - // } - - // // Perform deduction on cus ent - // let updateCusEnt = cusEnts.find( - // (e: any) => e.entitlement.feature.id === entity.feature_id, - // ); - // if (updateCusEnt) { - // await CusEntService.increment({ - // db, - // id: updateCusEnt.id, - // amount: 1, - // }); - // } - - // await EntityService.deleteInInternalIds({ - // db, - // internalIds: [entity.internal_id], - // orgId: req.orgId, - // env: req.env, - // }); - // } else { - // await EntityService.update({ - // db, - // internalId: entity.internal_id, - // update: { - // deleted: true, - // }, - // }); - // } - - // logger.info(` ✅ Finished deleting entity ${entity_id}`); - - // res.status(200).json({ - // success: true, - // }); } catch (error) { handleRequestError({ error, req, res, action: "delete entity" }); } diff --git a/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts b/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts index 3946aad8c..f9cb91382 100644 --- a/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts +++ b/server/src/internal/api/entitled/checkUtils/getV1CheckResponse.ts @@ -134,7 +134,9 @@ export const getV1CheckResponse = ({ balances.push(newBalance); // allowed = allowed && actual! >= required; - allowed = actual! + (totalPaidAllowance || 0) >= required; + allowed = + (required && required < 0) || + actual! + (totalPaidAllowance || 0) >= required; if (allowed) { break; diff --git a/server/src/internal/api/entitled/checkUtils/getV2CheckResponse.ts b/server/src/internal/api/entitled/checkUtils/getV2CheckResponse.ts index 5b9d713fe..b5009df8b 100644 --- a/server/src/internal/api/entitled/checkUtils/getV2CheckResponse.ts +++ b/server/src/internal/api/entitled/checkUtils/getV2CheckResponse.ts @@ -115,7 +115,7 @@ export const getV2CheckResponse = async ({ if ( (cusFeature && unlimited) || usageAllowed || - // cusFeature.balance >= (requiredBalance || 1) + (requiredBalance && requiredBalance < 0) || cusFeature.balance + totalPaidUsageAllowance >= (requiredBalance || 1) ) { allowed = true; diff --git a/server/src/internal/api/events/eventRouter.ts b/server/src/internal/api/events/eventRouter.ts index 846f690f3..ebff76ef8 100644 --- a/server/src/internal/api/events/eventRouter.ts +++ b/server/src/internal/api/events/eventRouter.ts @@ -83,9 +83,7 @@ const getEventAndCustomer = async ({ let internalEntityId = null; if (event_data.entity_id) { - internalEntityId = customer.entities.find( - (e) => e.id === event_data.entity_id - )?.internal_id; + internalEntityId = customer.entity?.internal_id; } const newEvent: EventInsert = { diff --git a/server/src/internal/api/events/usageRouter.ts b/server/src/internal/api/events/usageRouter.ts index c029efb8a..b1f3f91f4 100644 --- a/server/src/internal/api/events/usageRouter.ts +++ b/server/src/internal/api/events/usageRouter.ts @@ -100,9 +100,7 @@ const createAndInsertEvent = async ({ const entityId = req.body.entity_id; let internalEntityId = null; if (entityId) { - internalEntityId = customer.entities.find( - (e) => e.id === entityId - )?.internal_id; + internalEntityId = customer.entity?.internal_id; } const newEvent: EventInsert = { diff --git a/server/src/internal/api/rewards/handlers/referrals/handleGetRedemption.ts b/server/src/internal/api/rewards/handlers/referrals/handleGetRedemption.ts new file mode 100644 index 000000000..0f3a29e6d --- /dev/null +++ b/server/src/internal/api/rewards/handlers/referrals/handleGetRedemption.ts @@ -0,0 +1,20 @@ +import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "get redemption by id", + handler: async (req, res) => { + const { db } = req; + const { redemptionId } = req.params; + + const redemption = await RewardRedemptionService.getById({ + db, + id: redemptionId, + }); + + res.status(200).json(redemption); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/referrals/handleGetReferralCode.ts b/server/src/internal/api/rewards/handlers/referrals/handleGetReferralCode.ts new file mode 100644 index 000000000..31c8b2e09 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/referrals/handleGetReferralCode.ts @@ -0,0 +1,85 @@ +import { ErrCode } from "@autumn/shared"; +import { CusService } from "@/internal/customers/CusService.js"; +import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; +import { generateReferralCode } from "@/internal/rewards/referralUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { generateId } from "@/utils/genUtils.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "get referral code", + handler: async (req, res) => { + const { orgId, env, db } = req; + const { program_id: rewardProgramId, customer_id: customerId } = req.body; + + const [rewardProgram, customer] = await Promise.all([ + RewardProgramService.get({ + db, + id: rewardProgramId, + orgId, + env, + errorIfNotFound: true, + }), + CusService.get({ + db: req.db, + orgId, + env, + idOrInternalId: customerId, + }), + ]); + + if (!customer) { + throw new RecaseError({ + message: "Customer not found", + statusCode: 404, + code: ErrCode.CustomerNotFound, + }); + } + + if (!rewardProgram) { + throw new RecaseError({ + message: "Reward program not found", + statusCode: 404, + code: ErrCode.RewardProgramNotFound, + }); + } + + // Get referral code by customer and reward trigger + let referralCode = + await RewardProgramService.getCodeByCustomerAndRewardProgram({ + db, + orgId, + env, + internalCustomerId: customer.internal_id, + internalRewardProgramId: rewardProgram.internal_id, + }); + + if (!referralCode) { + const code = generateReferralCode(); + + referralCode = { + code, + org_id: orgId, + env, + internal_customer_id: customer.internal_id, + internal_reward_program_id: rewardProgram.internal_id, + id: generateId("rc"), + created_at: Date.now(), + }; + + referralCode = await RewardProgramService.createReferralCode({ + db, + data: referralCode, + }); + } + + res.status(200).json({ + code: referralCode.code, + customer_id: customer.id, + created_at: referralCode.created_at, + }); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/referrals/handleRedeemReferral.ts b/server/src/internal/api/rewards/handlers/referrals/handleRedeemReferral.ts new file mode 100644 index 000000000..51a8b44f4 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/referrals/handleRedeemReferral.ts @@ -0,0 +1,218 @@ +import { + ErrCode, + RewardCategory, + type RewardRedemption, + RewardTriggerEvent, +} from "@autumn/shared"; +import { parseReqForAction } from "@/internal/analytics/actionUtils.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; +import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; +import { triggerRedemption } from "@/internal/rewards/referralUtils.js"; +import { triggerFreeProduct } from "@/internal/rewards/referralUtils/triggerFreeProduct.js"; +import { getRewardCat } from "@/internal/rewards/rewardUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { generateId, notNullish } from "@/utils/genUtils.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "redeem referral code", + handler: async (req, res) => { + const { orgId, env, logtail: logger, db } = req; + const { code, customer_id: customerId } = req.body; + + // 1. Get redeemed by customer, and referral code + const [customer, referralCode, org] = await Promise.all([ + CusService.get({ + db, + orgId, + env, + idOrInternalId: customerId, + }), + RewardProgramService.getReferralCode({ + db, + orgId, + env, + code, + withRewardProgram: true, + }), + OrgService.getFromReq(req), + ]); + + if (!customer) { + throw new RecaseError({ + message: "Customer not found", + statusCode: 404, + code: ErrCode.CustomerNotFound, + }); + } + + // 2. Check that code has not reached max redemptions + const redemptionCount = await RewardProgramService.getCodeRedemptionCount( + { + db, + referralCodeId: referralCode.id, + } + ); + + if ( + referralCode.reward_program.max_redemptions && + redemptionCount >= referralCode.reward_program.max_redemptions + ) { + throw new RecaseError({ + message: "Referral code has reached max redemptions", + statusCode: 400, + code: ErrCode.ReferralCodeMaxRedemptionsReached, + }); + } + + // 3. Check that customer has not already redeemed a code in this referral program + const existingRedemptions = await RewardRedemptionService.getByCustomer({ + db, + internalCustomerId: customer.internal_id, + internalRewardProgramId: referralCode.internal_reward_program_id, + }); + + if (existingRedemptions.length > 0) { + throw new RecaseError({ + message: `Customer ${customer.id} has already redeemed a code in this referral program`, + statusCode: 400, + code: ErrCode.CustomerAlreadyRedeemedReferralCode, + }); + } + + // Don't let customer redeem their own code + const codeCustomer = await CusService.getByInternalId({ + db: req.db, + internalId: referralCode.internal_customer_id, + }); + + if (!codeCustomer) { + throw new RecaseError({ + message: "Referral code customer not found", + statusCode: 404, + code: ErrCode.CustomerNotFound, + }); + } + + if ( + codeCustomer.id === customer.id || + (notNullish(codeCustomer.fingerprint) && + codeCustomer.fingerprint === customer.fingerprint) + ) { + throw new RecaseError({ + message: "Customer cannot redeem their own code", + statusCode: 400, + code: ErrCode.CustomerCannotRedeemOwnCode, + }); + } + + // 4. Insert redemption into db + let redemption: RewardRedemption = { + id: generateId("rr"), + referral_code_id: referralCode.id, + internal_customer_id: customer.internal_id, // redeemed by customer + internal_reward_program_id: referralCode.internal_reward_program_id, + created_at: Date.now(), + triggered: + referralCode.reward_program.when === + RewardTriggerEvent.CustomerCreation, + applied: false, + updated_at: Date.now(), + }; + + redemption = await RewardRedemptionService.insert({ + db, + rewardRedemption: redemption, + }); + + // 5. If reward trigger when is immediate: + const { reward_program } = referralCode; + const redeemRewardNow = + referralCode.reward_program.when === + RewardTriggerEvent.CustomerCreation; + + if (redeemRewardNow) { + const reward = await RewardService.get({ + db, + orgId, + env, + idOrInternalId: reward_program.internal_reward_id, + }); + + if (!reward) { + throw new RecaseError({ + message: `Reward ${reward_program.internal_reward_id} not found`, + statusCode: 404, + code: ErrCode.RewardNotFound, + }); + } + + const rewardCat = getRewardCat(reward); + if (rewardCat === RewardCategory.FreeProduct) { + await triggerFreeProduct({ + req: parseReqForAction(req) as ExtendedRequest, + db, + referralCode, + redeemer: customer, + rewardProgram: reward_program, + org, + env, + logger, + redemption, + }); + } else { + await triggerRedemption({ + db, + referralCode, + org, + env, + logger, + reward, + redemption, + }); + } + } + + return res.status(200).json({ + id: redemption.id, + customer_id: customer.id, + reward_id: reward_program.reward.id, + referrer: { + id: codeCustomer.id, + name: codeCustomer.name, + email: codeCustomer.email, + created_at: codeCustomer.created_at, + }, + redeemer: { + id: customer.id, + name: customer.name, + email: customer.email, + created_at: customer.created_at, + }, + }); + }, + }); + +// res.status(200).json({ +// id: redemption.id, +// customer_id: customer.id, +// reward_id: reward_program.reward.id, +// referrer: { +// id: codeCustomer.id, +// name: codeCustomer.name, +// email: codeCustomer.email, +// created_at: codeCustomer.created_at, +// code: applications.referrer.cause, +// }, +// redeemer: { +// code: applications.redeemer.cause, +// ...applications.redeemer.meta, +// }, +// }); diff --git a/server/src/internal/api/rewards/handlers/referrals/index.ts b/server/src/internal/api/rewards/handlers/referrals/index.ts new file mode 100644 index 000000000..4fd0e2a2c --- /dev/null +++ b/server/src/internal/api/rewards/handlers/referrals/index.ts @@ -0,0 +1,3 @@ +export { default as handleGetRedemption } from "./handleGetRedemption.js"; +export { default as handleGetReferralCode } from "./handleGetReferralCode.js"; +export { default as handleRedeemReferral } from "./handleRedeemReferral.js"; diff --git a/server/src/internal/api/rewards/handlers/rewardPrograms/handleCreateRewardProgram.ts b/server/src/internal/api/rewards/handlers/rewardPrograms/handleCreateRewardProgram.ts new file mode 100644 index 000000000..150d5f04d --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewardPrograms/handleCreateRewardProgram.ts @@ -0,0 +1,85 @@ +import { + CreateRewardProgram, + ErrCode, + RewardTriggerEvent, +} from "@autumn/shared"; +import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; +import { constructRewardProgram } from "@/internal/rewards/rewardTriggerUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { nullish } from "@/utils/genUtils.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "create reward trigger", + handler: async (req, res) => { + const { orgId, env, db } = req; + const body = req.body; + + if (!body.internal_reward_id) { + throw new RecaseError({ + message: "Please select a reward to link this program to", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + if (!body.id) { + throw new RecaseError({ + message: "Please give this program an ID", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + const existingProgram = await RewardProgramService.get({ + db, + id: body.id, + orgId, + env, + }); + + if (existingProgram) { + throw new RecaseError({ + message: `Program with ID ${body.id} already exists`, + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + const rewardProgram = constructRewardProgram({ + rewardProgramData: CreateRewardProgram.parse(req.body), + orgId, + env, + }); + + // Fetch reward ID + // let reward = await RewardService.get({ + // db, + // id: rewardProgram.internal_reward_id, + // orgId, + // env, + // }); + + if ( + rewardProgram.when === RewardTriggerEvent.Checkout && + (nullish(rewardProgram.product_ids) || + rewardProgram.product_ids!.length === 0) + ) { + throw new RecaseError({ + message: "If redeem on checkout, must specify at least one product", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + const createdRewardProgram = await RewardProgramService.create({ + db, + data: rewardProgram, + }); + + return res.status(200).json(createdRewardProgram); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/rewardPrograms/handleCreateTrigger.ts b/server/src/internal/api/rewards/handlers/rewardPrograms/handleCreateTrigger.ts new file mode 100644 index 000000000..722e21d64 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewardPrograms/handleCreateTrigger.ts @@ -0,0 +1,85 @@ +import { + CreateRewardProgram, + ErrCode, + RewardTriggerEvent, +} from "@autumn/shared"; +import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; +import { constructRewardProgram } from "@/internal/rewards/rewardTriggerUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { nullish } from "@/utils/genUtils.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "create trigger", + handler: async (req, res) => { + const { orgId, env, db } = req; + const body = req.body; + + if (!body.internal_reward_id) { + throw new RecaseError({ + message: "Please select a reward to link this program to", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + if (!body.id) { + throw new RecaseError({ + message: "Please give this program an ID", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + const existingProgram = await RewardProgramService.get({ + db, + id: body.id, + orgId, + env, + }); + + if (existingProgram) { + throw new RecaseError({ + message: `Program with ID ${body.id} already exists`, + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + const rewardProgram = constructRewardProgram({ + rewardProgramData: CreateRewardProgram.parse(req.body), + orgId, + env, + }); + + // Fetch reward ID + // let reward = await RewardService.get({ + // db, + // id: rewardProgram.internal_reward_id, + // orgId, + // env, + // }); + + if ( + rewardProgram.when === RewardTriggerEvent.Checkout && + (nullish(rewardProgram.product_ids) || + rewardProgram.product_ids!.length === 0) + ) { + throw new RecaseError({ + message: "If redeem on checkout, must specify at least one product", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } + + const createdRewardProgram = await RewardProgramService.create({ + db, + data: rewardProgram, + }); + + return res.status(200).json(createdRewardProgram); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/rewardPrograms/handleDeleteRewardProgram.ts b/server/src/internal/api/rewards/handlers/rewardPrograms/handleDeleteRewardProgram.ts new file mode 100644 index 000000000..343d9eb1b --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewardPrograms/handleDeleteRewardProgram.ts @@ -0,0 +1,22 @@ +import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "delete reward scheme", + handler: async (req, res) => { + const { orgId, env, db } = req; + const { id } = req.params; + + const rewardProgram = await RewardProgramService.delete({ + db, + id, + orgId, + env, + }); + + return res.status(200).json(rewardProgram); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/rewardPrograms/index.ts b/server/src/internal/api/rewards/handlers/rewardPrograms/index.ts new file mode 100644 index 000000000..2f35e4645 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewardPrograms/index.ts @@ -0,0 +1,3 @@ +export { default as handleCreateRewardProgram } from "./handleCreateRewardProgram.js"; +export { default as handleCreateTrigger } from "./handleCreateTrigger.js"; +export { default as handleDeleteRewardProgram } from "./handleDeleteRewardProgram.js"; \ No newline at end of file diff --git a/server/src/internal/api/rewards/handlers/rewards/handleCreateCoupon.ts b/server/src/internal/api/rewards/handlers/rewards/handleCreateCoupon.ts new file mode 100644 index 000000000..c51b4948b --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewards/handleCreateCoupon.ts @@ -0,0 +1,103 @@ +import { + CreateRewardSchema, + isFixedPrice, + RewardCategory, +} from "@autumn/shared"; +import { createStripeCoupon } from "@/external/stripe/stripeCouponUtils/stripeCouponUtils.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { ProductService } from "@/internal/products/ProductService.js"; +import { PriceService } from "@/internal/products/prices/PriceService.js"; +import { pricesOnlyOneOff } from "@/internal/products/prices/priceUtils.js"; +import { isFreeProduct } from "@/internal/products/productUtils.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; +import { + constructReward, + getRewardCat, + initRewardStripePrices, +} from "@/internal/rewards/rewardUtils.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "create coupon", + handler: async (req, res) => { + const { db, orgId, env, logtail: logger } = req; + const rewardBody = req.body; + const rewardData = CreateRewardSchema.parse(rewardBody); + + const org = await OrgService.getFromReq(req); + + const newReward = constructReward({ + reward: rewardData, + orgId, + env, + }); + + if (getRewardCat(newReward) === RewardCategory.Discount) { + const discountConfig = newReward.discount_config; + + // Get prices for coupon + const [prices] = await Promise.all([ + PriceService.getInIds({ + db, + ids: discountConfig!.price_ids || [], + }), + ]); + + await initRewardStripePrices({ + db, + prices, + org, + env, + logger, + }); + + await createStripeCoupon({ + reward: newReward, + org, + env, + prices, + logger, + legacyVersion: req.query.legacyStripe === "true", + }); + } + + if (getRewardCat(newReward) === RewardCategory.FreeProduct) { + // 1. Check if product is paid + const fullProduct = await ProductService.getFull({ + db, + idOrInternalId: newReward.free_product_id!, + orgId: org.id, + env, + }); + + if (!isFreeProduct(fullProduct.prices)) { + // For one-off products, include all prices; for recurring products, only fixed prices + const isProductOneOff = pricesOnlyOneOff(fullProduct.prices); + const relevantPrices = isProductOneOff + ? fullProduct.prices // Include all prices for one-off products + : fullProduct.prices.filter((price) => isFixedPrice({ price })); // Only fixed prices for recurring products + + await createStripeCoupon({ + reward: newReward, + org, + env, + prices: relevantPrices.map((price) => ({ + ...price, + product: fullProduct, + })), + logger, + }); + } + } + + const insertedCoupon = await RewardService.insert({ + db, + data: newReward, + }); + + res.status(200).json(insertedCoupon); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/rewards/handleDeleteCoupon.ts b/server/src/internal/api/rewards/handlers/rewards/handleDeleteCoupon.ts new file mode 100644 index 000000000..da02f9751 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewards/handleDeleteCoupon.ts @@ -0,0 +1,55 @@ +import { ErrCode } from "@autumn/shared"; +import { createStripeCli } from "@/external/stripe/utils.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "delete reward", + handler: async (req, res) => { + const { id } = req.params; + const { orgId, env, db } = req; + + const org = await OrgService.getFromReq(req); + const stripeCli = createStripeCli({ + org, + env, + }); + + const reward = await RewardService.get({ + db, + idOrInternalId: id, + orgId, + env, + }); + + if (!reward) { + throw new RecaseError({ + message: `Reward ${id} not found`, + code: ErrCode.InvalidRequest, + }); + } + + try { + await stripeCli.coupons.del(reward.id); + } catch (error) { + console.log(`Failed to delete coupon from stripe: ${(error as { message: string }).message}`); + } + + await RewardService.delete({ + db, + internalId: reward.internal_id, + env, + orgId, + }); + + res.status(200).json({ + success: true, + message: "Reward deleted successfully", + }); + }, + }); diff --git a/server/src/internal/api/rewards/handlers/rewards/handleGetCoupon.ts b/server/src/internal/api/rewards/handlers/rewards/handleGetCoupon.ts new file mode 100644 index 000000000..f4a694555 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewards/handleGetCoupon.ts @@ -0,0 +1,21 @@ +import { RewardService } from "@/internal/rewards/RewardService.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => routeHandler({ + req, + res, + action: "get reward", + handler: async (req, res) => { + const { id } = req.params; + const { orgId, env, db } = req; + + const reward = await RewardService.get({ + db, + idOrInternalId: id, + orgId, + env, + }); + + res.status(200).json(reward); + } +}); \ No newline at end of file diff --git a/server/src/internal/api/rewards/handlers/rewards/handleUpdateCoupon.ts b/server/src/internal/api/rewards/handlers/rewards/handleUpdateCoupon.ts new file mode 100644 index 000000000..a664f8497 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewards/handleUpdateCoupon.ts @@ -0,0 +1,110 @@ +import { ErrCode, PriceType, RewardCategory } from "@autumn/shared"; +import { createStripeCoupon } from "@/external/stripe/stripeCouponUtils/stripeCouponUtils.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { ProductService } from "@/internal/products/ProductService.js"; +import { PriceService } from "@/internal/products/prices/PriceService.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; +import { getRewardCat } from "@/internal/rewards/rewardUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { routeHandler } from "@/utils/routerUtils.js"; + +export default async (req: any, res: any) => + routeHandler({ + req, + res, + action: "update coupon", + handler: async (req, res) => { + const { internalId } = req.params; + const { orgId, env, db, logtail: logger } = req; + const rewardBody = req.body; + + const org = await OrgService.getFromReq(req); + const stripeCli = createStripeCli({ + org, + env, + }); + + const reward = await RewardService.get({ + db, + idOrInternalId: internalId, + orgId, + env, + }); + + if (!reward) { + throw new RecaseError({ + message: `Reward ${internalId} not found`, + code: ErrCode.InvalidRequest, + }); + } + + // Determine prices depending on reward category + const rewardCat = getRewardCat(rewardBody); + + let prices: any[] = []; + if (rewardCat === RewardCategory.Discount) { + const stripePriceIds = + rewardBody.discount_config?.price_ids ?? + reward.discount_config?.price_ids ?? + []; + const byStripeId = await PriceService.getByStripeIds({ + db, + stripePriceIds, + }); + prices = stripePriceIds + .map((id: string) => byStripeId[id]) + .filter(Boolean); + } else if (rewardCat === RewardCategory.FreeProduct) { + const freeProductId = + rewardBody.free_product_id ?? reward.free_product_id; + if (freeProductId) { + const fullProduct = await ProductService.getFull({ + db, + idOrInternalId: freeProductId, + orgId: org.id, + env, + }); + prices = fullProduct.prices + .map((price) => ({ + ...price, + product: fullProduct, + })) + .filter((x) => x.config?.type === PriceType.Fixed); + } + } + + // 1. Delete old prices from stripe + try { + await stripeCli.coupons.del(reward.id); + await stripeCli.coupons.del(reward.internal_id); + } catch (_) { + // console.log(`Failed to delete coupon from stripe: ${error.message}`); + } + + if ( + rewardCat === RewardCategory.Discount || + (rewardCat === RewardCategory.FreeProduct && prices.length > 0) + ) { + await createStripeCoupon({ + reward: rewardBody, + org, + env, + prices, + logger, + legacyVersion: req.query.legacyStripe === "true", + }); + } + + // 3. Update coupon in db + const updatedCoupon = await RewardService.update({ + db, + internalId: reward.internal_id, + env, + orgId, + update: rewardBody, + }); + + res.status(200).json(updatedCoupon); + }, + }); \ No newline at end of file diff --git a/server/src/internal/api/rewards/handlers/rewards/index.ts b/server/src/internal/api/rewards/handlers/rewards/index.ts new file mode 100644 index 000000000..29b1d2695 --- /dev/null +++ b/server/src/internal/api/rewards/handlers/rewards/index.ts @@ -0,0 +1,6 @@ +import handleCreateCoupon from "./handleCreateCoupon.js"; +import handleDeleteCoupon from "./handleDeleteCoupon.js"; +import handleGetCoupon from "./handleGetCoupon.js"; +import handleUpdateCoupon from "./handleUpdateCoupon.js"; + +export { handleCreateCoupon, handleDeleteCoupon, handleGetCoupon, handleUpdateCoupon }; \ No newline at end of file diff --git a/server/src/internal/api/rewards/referralRouter.ts b/server/src/internal/api/rewards/referralRouter.ts index bcd0d3290..98021711b 100644 --- a/server/src/internal/api/rewards/referralRouter.ts +++ b/server/src/internal/api/rewards/referralRouter.ts @@ -1,301 +1,17 @@ -import { CusService } from "@/internal/customers/CusService.js"; +import express, { type Router } from "express"; import { - generateReferralCode, - triggerFreeProduct, - triggerRedemption, -} from "@/internal/rewards/referralUtils.js"; -import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; - -import RecaseError from "@/utils/errorUtils.js"; -import { generateId, notNullish } from "@/utils/genUtils.js"; -import { routeHandler } from "@/utils/routerUtils.js"; -import { ErrCode, RewardCategory, RewardTriggerEvent } from "@autumn/shared"; -import express, { Router } from "express"; -import { RewardRedemption } from "@autumn/shared"; -import { OrgService } from "@/internal/orgs/OrgService.js"; -import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; -import { getRewardCat } from "@/internal/rewards/rewardUtils.js"; -import { RewardService } from "@/internal/rewards/RewardService.js"; -import { parseReqForAction } from "@/internal/analytics/actionUtils.js"; -import { ExtendedRequest } from "@/utils/models/Request.js"; + handleGetRedemption, + handleGetReferralCode, + handleRedeemReferral, +} from "./handlers/referrals/index.js"; export const referralRouter: Router = express.Router(); // 1. Get referral code -referralRouter.post("/code", (req, res) => - routeHandler({ - req, - res, - action: "get referral code", - handler: async (req: any, res: any) => { - const { orgId, env, logtail: logger, db } = req; - const { program_id: rewardProgramId, customer_id: customerId } = req.body; +referralRouter.post("/code", handleGetReferralCode); - let [rewardProgram, customer] = await Promise.all([ - RewardProgramService.get({ - db, - id: rewardProgramId, - orgId, - env, - errorIfNotFound: true, - }), - CusService.get({ - db: req.db, - orgId, - env, - idOrInternalId: customerId, - }), - ]); - - if (!customer) { - throw new RecaseError({ - message: "Customer not found", - statusCode: 404, - code: ErrCode.CustomerNotFound, - }); - } - - if (!rewardProgram) { - throw new RecaseError({ - message: "Reward program not found", - statusCode: 404, - code: ErrCode.RewardProgramNotFound, - }); - } - - // Get referral code by customer and reward trigger - let referralCode = - await RewardProgramService.getCodeByCustomerAndRewardProgram({ - db, - orgId, - env, - internalCustomerId: customer.internal_id, - internalRewardProgramId: rewardProgram.internal_id, - }); - - if (!referralCode) { - const code = generateReferralCode(); - - referralCode = { - code, - org_id: orgId, - env, - internal_customer_id: customer.internal_id, - internal_reward_program_id: rewardProgram.internal_id, - id: generateId("rc"), - created_at: Date.now(), - }; - - referralCode = await RewardProgramService.createReferralCode({ - db, - data: referralCode, - }); - } - - res.status(200).json({ - code: referralCode.code, - customer_id: customer.id, - created_at: referralCode.created_at, - }); - }, - }) -); - -referralRouter.post("/redeem", (req, res) => - routeHandler({ - req, - res, - action: "redeem referral code", - handler: async (req: any, res: any) => { - const { orgId, env, logtail: logger, db } = req; - // const { referral_id: rewardTriggerId } = req.params; - const { code, customer_id: customerId } = req.body; - - // 1. Get redeemed by customer, and referral code - let [customer, referralCode, org] = await Promise.all([ - CusService.get({ - db, - orgId, - env, - idOrInternalId: customerId, - }), - RewardProgramService.getReferralCode({ - db, - orgId, - env, - code, - withRewardProgram: true, - }), - OrgService.getFromReq(req), - ]); - - if (!customer) { - throw new RecaseError({ - message: "Customer not found", - statusCode: 404, - code: ErrCode.CustomerNotFound, - }); - } - - // 2. Check that code has not reached max redemptions - let redemptionCount = await RewardProgramService.getCodeRedemptionCount({ - db, - referralCodeId: referralCode.id, - }); - - if ( - referralCode.reward_program.max_redemptions && - redemptionCount >= referralCode.reward_program.max_redemptions - ) { - throw new RecaseError({ - message: "Referral code has reached max redemptions", - statusCode: 400, - code: ErrCode.ReferralCodeMaxRedemptionsReached, - }); - } - - // 3. Check that customer has not already redeemed a code in this referral program - let existingRedemptions = await RewardRedemptionService.getByCustomer({ - db, - internalCustomerId: customer.internal_id, - internalRewardProgramId: referralCode.internal_reward_program_id, - }); - - if (existingRedemptions.length > 0) { - throw new RecaseError({ - message: `Customer ${customer.id} has already redeemed a code in this referral program`, - statusCode: 400, - code: ErrCode.CustomerAlreadyRedeemedReferralCode, - }); - } - - // Don't let customer redeem their own code - let codeCustomer = await CusService.getByInternalId({ - db: req.db, - internalId: referralCode.internal_customer_id, - }); - - if (!codeCustomer) { - throw new RecaseError({ - message: "Referral code customer not found", - statusCode: 404, - code: ErrCode.CustomerNotFound, - }); - } - - if ( - codeCustomer.id === customer.id || - (notNullish(codeCustomer.fingerprint) && - codeCustomer.fingerprint === customer.fingerprint) - ) { - throw new RecaseError({ - message: "Customer cannot redeem their own code", - statusCode: 400, - code: ErrCode.CustomerCannotRedeemOwnCode, - }); - } - - // 4. Insert redemption into db - let redemption: RewardRedemption = { - id: generateId("rr"), - referral_code_id: referralCode.id, - internal_customer_id: customer.internal_id, // redeemed by customer - internal_reward_program_id: referralCode.internal_reward_program_id, - created_at: Date.now(), - triggered: - referralCode.reward_program.when === - RewardTriggerEvent.CustomerCreation, - applied: false, - updated_at: Date.now(), - }; - - redemption = await RewardRedemptionService.insert({ - db, - rewardRedemption: redemption, - }); - - // 5. If reward trigger when is immediate: - let { reward_program } = referralCode; - - if ( - referralCode.reward_program.when === RewardTriggerEvent.CustomerCreation - ) { - const reward = await RewardService.get({ - db, - orgId, - env, - idOrInternalId: reward_program.internal_reward_id, - }); - - if (!reward) { - throw new RecaseError({ - message: `Reward ${reward_program.internal_reward_id} not found`, - statusCode: 404, - code: ErrCode.RewardNotFound, - }); - } - - let rewardCat = getRewardCat(reward); - if (rewardCat === RewardCategory.FreeProduct) { - await triggerFreeProduct({ - req: parseReqForAction(req) as ExtendedRequest, - db, - referralCode, - redeemer: customer, - rewardProgram: reward_program, - org, - env, - logger, - redemption, - }); - } else { - await triggerRedemption({ - db, - referralCode, - org, - env, - logger, - reward, - redemption, - }); - } - } - - // Add coupon to customer? - res.status(200).json({ - id: redemption.id, - customer_id: customer.id, - // triggered: redemption?.applied, - // applied: redemption?.applied, - reward_id: reward_program.reward.id, - referrer: { - id: codeCustomer.id, - name: codeCustomer.name, - email: codeCustomer.email, - created_at: codeCustomer.created_at, - }, - }); - }, - }) -); +referralRouter.post("/redeem", handleRedeemReferral); export const redemptionRouter: Router = express.Router(); -redemptionRouter.get("/:redemptionId", (req, res) => - routeHandler({ - req, - res, - action: "get redemption by id", - handler: async (req: any, res: any) => { - const { db } = req; - const { redemptionId } = req.params; - - let redemption = await RewardRedemptionService.getById({ - db, - id: redemptionId, - }); - - res.status(200).json(redemption); - }, - }) -); +redemptionRouter.get("/:redemptionId", handleGetRedemption); diff --git a/server/src/internal/api/rewards/rewardProgramRouter.ts b/server/src/internal/api/rewards/rewardProgramRouter.ts index 70db20ddc..725b9ac99 100644 --- a/server/src/internal/api/rewards/rewardProgramRouter.ts +++ b/server/src/internal/api/rewards/rewardProgramRouter.ts @@ -1,111 +1,8 @@ -import { RewardProgramService } from "@/internal/rewards/RewardProgramService.js"; -import { RewardService } from "@/internal/rewards/RewardService.js"; -import { constructRewardProgram } from "@/internal/rewards/rewardTriggerUtils.js"; -import RecaseError from "@/utils/errorUtils.js"; -import { nullish } from "@/utils/genUtils.js"; -import { routeHandler } from "@/utils/routerUtils.js"; -import { - CreateRewardProgram, - ErrCode, - RewardTriggerEvent, -} from "@autumn/shared"; -import express, { Router } from "express"; +import express, { type Router } from "express"; +import { handleCreateRewardProgram, handleDeleteRewardProgram } from "./handlers/rewardPrograms/index.js"; export const rewardProgramRouter: Router = express.Router(); -rewardProgramRouter.post("", (req, res) => - routeHandler({ - req, - res, - action: "create reward trigger", - handler: async (req: any, res: any) => { - const { orgId, env, db } = req; - const body = req.body; +rewardProgramRouter.post("", handleCreateRewardProgram); - if (!body.internal_reward_id) { - throw new RecaseError({ - message: "Please select a reward to link this program to", - code: ErrCode.InvalidRequest, - statusCode: 400, - }); - } - - if (!body.id) { - throw new RecaseError({ - message: "Please give this program an ID", - code: ErrCode.InvalidRequest, - statusCode: 400, - }); - } - - let existingProgram = await RewardProgramService.get({ - db, - id: body.id, - orgId, - env, - }); - - if (existingProgram) { - throw new RecaseError({ - message: `Program with ID ${body.id} already exists`, - code: ErrCode.InvalidRequest, - statusCode: 400, - }); - } - - const rewardProgram = constructRewardProgram({ - rewardProgramData: CreateRewardProgram.parse(req.body), - orgId, - env, - }); - - // Fetch reward ID - // let reward = await RewardService.get({ - // db, - // id: rewardProgram.internal_reward_id, - // orgId, - // env, - // }); - - if ( - rewardProgram.when == RewardTriggerEvent.Checkout && - (nullish(rewardProgram.product_ids) || - rewardProgram.product_ids!.length == 0) - ) { - throw new RecaseError({ - message: "If redeem on checkout, must specify at least one product", - code: ErrCode.InvalidRequest, - statusCode: 400, - }); - } - - let createdRewardProgram = await RewardProgramService.create({ - db, - data: rewardProgram, - }); - - return res.status(200).json(createdRewardProgram); - }, - }), -); - -rewardProgramRouter.delete("/:id", (req, res) => - routeHandler({ - req, - res, - action: "delete reward scheme", - handler: async (req: any, res: any) => { - const { orgId, env, db } = req; - const { id } = req.params; - - let rewardProgram = await RewardProgramService.delete({ - db, - id, - orgId, - env, - }); - - return res.status(200).json(rewardProgram); - }, - }), -); +rewardProgramRouter.delete("/:id", handleDeleteRewardProgram); \ No newline at end of file diff --git a/server/src/internal/api/rewards/rewardRouter.ts b/server/src/internal/api/rewards/rewardRouter.ts index 6bb45de32..86aff9652 100644 --- a/server/src/internal/api/rewards/rewardRouter.ts +++ b/server/src/internal/api/rewards/rewardRouter.ts @@ -1,239 +1,14 @@ -import express from "express"; -import { CreateRewardSchema, ErrCode, RewardCategory } from "@autumn/shared"; -import { Router } from "express"; -import RecaseError, { handleRequestError } from "@/utils/errorUtils.js"; -import { createStripeCli } from "@/external/stripe/utils.js"; -import { OrgService } from "@/internal/orgs/OrgService.js"; - -import { createStripeCoupon } from "@/external/stripe/stripeCouponUtils/stripeCouponUtils.js"; -import { RewardService } from "@/internal/rewards/RewardService.js"; -import { PriceService } from "@/internal/products/prices/PriceService.js"; -import { EntitlementService } from "@/internal/products/entitlements/EntitlementService.js"; -import { - constructReward, - getRewardCat, - initRewardStripePrices, -} from "@/internal/rewards/rewardUtils.js"; -import { notNullish } from "@/utils/genUtils.js"; +import express, { type Router } from "express"; +import { handleCreateCoupon, handleDeleteCoupon, handleGetCoupon, handleUpdateCoupon } from "./handlers/rewards/index.js"; const rewardRouter: Router = express.Router(); -rewardRouter.post("", async (req: any, res: any) => { - try { - const { db, orgId, env, logtail: logger } = req; - const rewardBody = req.body; - const rewardData = CreateRewardSchema.parse(rewardBody); +rewardRouter.post("", handleCreateCoupon); - const org = await OrgService.getFromReq(req); +rewardRouter.delete("/:id", handleDeleteCoupon); - const newReward = constructReward({ - reward: rewardData, - orgId, - env, - // internalId: rewardBody.internal_id, - }); +rewardRouter.post("/:internalId", handleUpdateCoupon); - if (getRewardCat(newReward) === RewardCategory.Discount) { - const stripeCli = createStripeCli({ - org, - env, - }); +rewardRouter.get("/:id", handleGetCoupon); - let discountConfig = newReward.discount_config; - - // Get prices for coupon - const [prices, entitlements] = await Promise.all([ - PriceService.getInIds({ - db, - ids: discountConfig!.price_ids || [], - }), - EntitlementService.getByOrg({ - db, - orgId, - env, - }), - ]); - - await initRewardStripePrices({ - db, - prices, - org, - env, - logger, - }); - - await createStripeCoupon({ - reward: newReward, - org, - env, - prices, - logger, - legacyVersion: req.query.legacyStripe === "true", - }); - } - - const insertedCoupon = await RewardService.insert({ - db, - data: newReward, - }); - - res.status(200).json(insertedCoupon); - } catch (error) { - handleRequestError({ - error, - res, - req, - action: "create coupon", - }); - } -}); - -rewardRouter.delete("/:id", async (req: any, res: any) => { - try { - const { id } = req.params; - const { orgId, env, db } = req; - - const org = await OrgService.getFromReq(req); - const stripeCli = createStripeCli({ - org, - env, - }); - - let reward = await RewardService.get({ - db, - idOrInternalId: id, - orgId, - env, - }); - - if (!reward) { - throw new RecaseError({ - message: `Reward ${id} not found`, - code: ErrCode.InvalidRequest, - }); - } - - try { - await stripeCli.coupons.del(reward.id); - } catch (error: any) { - console.log(`Failed to delete coupon from stripe: ${error.message}`); - } - - await RewardService.delete({ - db, - internalId: reward.internal_id, - env, - orgId, - }); - - res.status(200).json({ - success: true, - message: "Reward deleted successfully", - }); - } catch (error) { - handleRequestError({ - error, - res, - req, - action: "delete coupon", - }); - } -}); - -rewardRouter.post("/:internalId", async (req: any, res: any) => { - try { - const { internalId } = req.params; - const { orgId, env, db, logtail: logger } = req; - const rewardBody = req.body; - - const org = await OrgService.getFromReq(req); - const stripeCli = createStripeCli({ - org, - env, - }); - - const reward = await RewardService.get({ - db, - idOrInternalId: internalId, - orgId, - env, - }); - - if (!reward) { - throw new RecaseError({ - message: `Reward ${internalId} not found`, - code: ErrCode.InvalidRequest, - }); - } - - const prices = await PriceService.getInIds({ - db, - ids: notNullish(rewardBody.price_ids) - ? rewardBody.price_ids - : reward.discount_config?.price_ids, - }); - - // 1. Delete old prices from stripe - try { - await stripeCli.coupons.del(reward.id); - await stripeCli.coupons.del(reward.internal_id); - } catch (error) { - // console.log(`Failed to delete coupon from stripe: ${error.message}`); - } - - let rewardCat = getRewardCat(rewardBody); - if (rewardCat == RewardCategory.Discount) { - await createStripeCoupon({ - reward: rewardBody, - org, - env, - prices, - logger, - legacyVersion: req.query.legacyStripe === "true", - }); - } - - // 3. Update coupon in db - const updatedCoupon = await RewardService.update({ - db, - internalId: reward.internal_id, - env, - orgId, - update: rewardBody, - }); - - res.status(200).json(updatedCoupon); - } catch (error) { - handleRequestError({ - error, - res, - req, - action: "update coupon", - }); - } -}); - -rewardRouter.get("/:id", async (req: any, res: any) => { - try { - const { id } = req.params; - const { orgId, env, db } = req; - - const reward = await RewardService.get({ - db, - idOrInternalId: id, - orgId, - env, - }); - - res.status(200).json(reward); - } catch (error) { - handleRequestError({ - error, - res, - req, - action: "get reward", - }); - } -}); - -export default rewardRouter; +export default rewardRouter; \ No newline at end of file diff --git a/server/src/internal/customers/CusService.ts b/server/src/internal/customers/CusService.ts index d1d91d9f5..87a5a11cf 100644 --- a/server/src/internal/customers/CusService.ts +++ b/server/src/internal/customers/CusService.ts @@ -13,7 +13,7 @@ import { import RecaseError from "@/utils/errorUtils.js"; import { ErrCode } from "@/errors/errCodes.js"; import { StatusCodes } from "http-status-codes"; -import { and, eq, or, sql } from "drizzle-orm"; +import { and, eq, ilike, or, sql } from "drizzle-orm"; import { DrizzleCli } from "@/db/initDrizzle.js"; import { getFullCusQuery } from "./getFullCusQuery.js"; import { trace } from "@opentelemetry/api"; @@ -151,7 +151,7 @@ export class CusService { }) { const customer = await db.query.customers.findMany({ where: and( - eq(customers.email, email), + ilike(customers.email, email), eq(customers.org_id, orgId), eq(customers.env, env) ), diff --git a/server/src/internal/customers/add-product/createFullCusProduct.ts b/server/src/internal/customers/add-product/createFullCusProduct.ts index a7b4bca95..09f8468e1 100644 --- a/server/src/internal/customers/add-product/createFullCusProduct.ts +++ b/server/src/internal/customers/add-product/createFullCusProduct.ts @@ -1,41 +1,39 @@ import { - CusProductStatus, - Price, - ProcessorType, - CustomerEntitlement, - CusProduct, - FeatureOptions, - FreeTrial, + type APIVersion, CollectionMethod, - FullCusProduct, - APIVersion, - InsertReplaceable, - ProductOptions, + type CusProduct, + CusProductStatus, + type Customer, + type CustomerEntitlement, + type CustomerPrice, + type FeatureOptions, + type FreeTrial, + type FullCusProduct, + type FullProduct, + type InsertReplaceable, + type Price, + ProcessorType, + type ProductOptions, } from "@autumn/shared"; -import { generateId, notNullish, nullish } from "@/utils/genUtils.js"; - -import { Customer } from "@autumn/shared"; -import { FullProduct } from "@autumn/shared"; -import { getEntOptions } from "@/internal/products/prices/priceUtils.js"; -import { CustomerPrice } from "@autumn/shared"; -import { CusProductService } from "../cusProducts/CusProductService.js"; -import { InsertCusProductParams } from "../cusProducts/AttachParams.js"; -import { freeTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js"; -import { getEntRelatedPrice } from "@/internal/products/entitlements/entitlementUtils.js"; - -import { getExistingCusProducts } from "../cusProducts/cusProductUtils/getExistingCusProducts.js"; -import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js"; -import { searchCusProducts } from "@/internal/customers/cusProducts/cusProductUtils.js"; -import { updateOneTimeCusProduct } from "./createOneTimeCusProduct.js"; -import { initCusEntitlement } from "./initCusEnt.js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; +import { searchCusProducts } from "@/internal/customers/cusProducts/cusProductUtils.js"; +import { getEntRelatedPrice } from "@/internal/products/entitlements/entitlementUtils.js"; +import { freeTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js"; +import { getEntOptions } from "@/internal/products/prices/priceUtils.js"; +import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js"; +import { generateId, notNullish, nullish } from "@/utils/genUtils.js"; +import type { InsertCusProductParams } from "../cusProducts/AttachParams.js"; +import { CusProductService } from "../cusProducts/CusProductService.js"; import { CusEntService } from "../cusProducts/cusEnts/CusEntitlementService.js"; -import { CusPriceService } from "../cusProducts/cusPrices/CusPriceService.js"; import { addExistingUsagesToCusEnts } from "../cusProducts/cusEnts/cusEntUtils/getExistingUsage.js"; -import { RepService } from "../cusProducts/cusEnts/RepService.js"; import { getNewProductRollovers } from "../cusProducts/cusEnts/cusRollovers/getNewProductRollovers.js"; import { RolloverService } from "../cusProducts/cusEnts/cusRollovers/RolloverService.js"; +import { RepService } from "../cusProducts/cusEnts/RepService.js"; +import { CusPriceService } from "../cusProducts/cusPrices/CusPriceService.js"; +import { getExistingCusProducts } from "../cusProducts/cusProductUtils/getExistingCusProducts.js"; +import { updateOneTimeCusProduct } from "./createOneTimeCusProduct.js"; +import { initCusEntitlement } from "./initCusEnt.js"; export const initCusPrice = ({ price, @@ -103,7 +101,7 @@ export const initCusProduct = ({ apiVersion?: APIVersion; quantity?: number; }) => { - let isFuture = startsAt && startsAt > Date.now(); + const isFuture = startsAt && startsAt > Date.now(); let trialEnds = trialEndsAt; if (!trialEndsAt && freeTrial) { @@ -197,7 +195,7 @@ export const expireOrDeleteCusProduct = async ({ }) => { // 1. If startsAt if (startsAt && startsAt > Date.now()) { - let curScheduledProduct = cusProducts?.find( + const curScheduledProduct = cusProducts?.find( (cp) => cp.product.group === product.group && cp.status === CusProductStatus.Scheduled && @@ -213,7 +211,7 @@ export const expireOrDeleteCusProduct = async ({ }); } } else { - let { curMainProduct } = getExistingCusProducts({ + const { curMainProduct } = getExistingCusProducts({ product, cusProducts: cusProducts as FullCusProduct[], internalEntityId, @@ -318,7 +316,7 @@ export const createFullCusProduct = async ({ attachParams; // Try to get current cus product or set to null... - let curCusProduct = await getExistingCusProduct({ + const curCusProduct = await getExistingCusProduct({ db, cusProducts: attachParams.cusProducts, product, @@ -333,7 +331,7 @@ export const createFullCusProduct = async ({ trialEndsAt = curCusProduct.trial_ends_at || undefined; } - let attachReplaceables = attachParams.replaceables || []; + const attachReplaceables = attachParams.replaceables || []; const existingCusProduct = searchCusProducts({ internalProductId: product.internal_id, @@ -388,7 +386,7 @@ export const createFullCusProduct = async ({ cusEnts.push(cusEnt); - let newReplaceables_ = attachReplaceables + const newReplaceables_ = attachReplaceables .filter((r) => r.ent.id === entitlement.id) .map((r) => ({ ...r, @@ -399,6 +397,7 @@ export const createFullCusProduct = async ({ } // 3. Deduct existing usages + let deductedCusEnts = addExistingUsagesToCusEnts({ cusEnts: cusEnts, entitlements: entitlements, @@ -410,7 +409,7 @@ export const createFullCusProduct = async ({ }); // 4. Get new rollovers - let rolloverOps = await getNewProductRollovers({ + const rolloverOps = await getNewProductRollovers({ db, curCusProduct: curCusProduct as FullCusProduct, cusEnts, @@ -492,7 +491,7 @@ export const createFullCusProduct = async ({ replaceables: newReplaceables, }); - let rolloverInserts: any = []; + const rolloverInserts: any = []; for (const operation of rolloverOps) { rolloverInserts.push( @@ -504,7 +503,7 @@ export const createFullCusProduct = async ({ ); } - let finalRollovers = (await Promise.all(rolloverInserts)).flatMap((r) => r); + const finalRollovers = (await Promise.all(rolloverInserts)).flatMap((r) => r); // Get rollovers for each entitlement const cusEntsWithRollovers = await Promise.all( @@ -525,7 +524,7 @@ export const createFullCusProduct = async ({ })) ); - let fullCusProduct = { + const fullCusProduct = { ...cusProd, product, customer_entitlements: cusEntsWithRollovers, diff --git a/server/src/internal/customers/attach/attachFunctions/addProductFlow/createStripeSub2.ts b/server/src/internal/customers/attach/attachFunctions/addProductFlow/createStripeSub2.ts index 578d877bf..d4da5b36e 100644 --- a/server/src/internal/customers/attach/attachFunctions/addProductFlow/createStripeSub2.ts +++ b/server/src/internal/customers/attach/attachFunctions/addProductFlow/createStripeSub2.ts @@ -1,21 +1,22 @@ -import { freeTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js"; -import RecaseError from "@/utils/errorUtils.js"; -import { ErrCode, Reward, IntervalConfig, AttachConfig } from "@autumn/shared"; -import Stripe from "stripe"; +import { type AttachConfig, ErrCode } from "@autumn/shared"; +import type Stripe from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; -import { SubService } from "@/internal/subscriptions/SubService.js"; -import { generateId } from "@/utils/genUtils.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; - import { - getLatestPeriodStart, getEarliestPeriodEnd, + getLatestPeriodStart, } from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; - -import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; import { sanitizeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js"; -import { ItemSet } from "@/utils/models/ItemSet.js"; +import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; import { buildInvoiceMemoFromEntitlements } from "@/internal/invoices/invoiceMemoUtils.js"; +import { + freeTrialToStripeTimestamp, + rewardTrialToStripeTimestamp, +} from "@/internal/products/free-trials/freeTrialUtils.js"; +import { SubService } from "@/internal/subscriptions/SubService.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { generateId } from "@/utils/genUtils.js"; +import type { ItemSet } from "@/utils/models/ItemSet.js"; // Get payment method @@ -36,16 +37,17 @@ export const createStripeSub2 = async ({ itemSet: ItemSet; logger: any; }) => { - const { customer, invoiceOnly, freeTrial, org, now, rewards } = attachParams; - const isDefaultTrial = freeTrial && !freeTrial.card_required; + const { customer, invoiceOnly, freeTrial, org, now, rewards, rewardTrial } = + attachParams; + // const isDefaultTrial = freeTrial && !freeTrial.card_required; + // let shouldErrorIfNoPm = !invoiceOnly; + // if (isDefaultTrial) shouldErrorIfNoPm = false; + // if (rewardTrial) shouldErrorIfNoPm = false; - let shouldErrorIfNoPm = !invoiceOnly; - if (isDefaultTrial) shouldErrorIfNoPm = false; - - let paymentMethod = await getCusPaymentMethod({ + const paymentMethod = await getCusPaymentMethod({ stripeCli, stripeId: customer.processor.id, - errorIfNone: shouldErrorIfNoPm, + errorIfNone: config.requirePaymentMethod, }); let paymentMethodData = {}; @@ -68,7 +70,6 @@ export const createStripeSub2 = async ({ items: sanitizeSubItems(subItems), billing_mode: { type: "flexible" }, - trial_end: freeTrialToStripeTimestamp({ freeTrial, now }), payment_behavior: "error_if_incomplete", add_invoice_items: invoiceItems, collection_method: invoiceOnly ? "send_invoice" : "charge_automatically", @@ -80,14 +81,30 @@ export const createStripeSub2 = async ({ discounts, expand: ["latest_invoice"], - trial_settings: - freeTrial && !freeTrial.card_required - ? { - end_behavior: { - missing_payment_method: "cancel", - }, - } - : undefined, + ...{ + trial_settings: + freeTrial && !freeTrial.card_required + ? { + end_behavior: { + missing_payment_method: "cancel", + }, + } + : undefined, + + trial_end: freeTrialToStripeTimestamp({ freeTrial, now }), + }, + + // ...{ + // trial_settings: rewardTrial + // ? { + // end_behavior: { + // missing_payment_method: "cancel", + // }, + // } + // : undefined, + + // trial_end: rewardTrialToStripeTimestamp({ rewardTrial, now }), + // }, }); const latestInvoice = subscription.latest_invoice as Stripe.Invoice; diff --git a/server/src/internal/customers/attach/attachFunctions/addProductFlow/handleAddProduct.ts b/server/src/internal/customers/attach/attachFunctions/addProductFlow/handleAddProduct.ts index 5be2d8dc9..b0063a390 100644 --- a/server/src/internal/customers/attach/attachFunctions/addProductFlow/handleAddProduct.ts +++ b/server/src/internal/customers/attach/attachFunctions/addProductFlow/handleAddProduct.ts @@ -1,111 +1,114 @@ -import { createFullCusProduct } from "../../../add-product/createFullCusProduct.js"; - import { - AttachParams, + APIVersion, + AttachBranch, + type AttachConfig, + SuccessCode, +} from "@autumn/shared"; +import { subToPeriodStartEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; +import { attachToInsertParams } from "@/internal/products/productUtils.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; +import { createFullCusProduct } from "../../../add-product/createFullCusProduct.js"; +import { + type AttachParams, AttachResultSchema, } from "../../../cusProducts/AttachParams.js"; -import { APIVersion, AttachBranch, AttachConfig } from "@autumn/shared"; -import { attachToInsertParams } from "@/internal/products/productUtils.js"; -import { SuccessCode } from "@autumn/shared"; -import { ExtendedRequest } from "@/utils/models/Request.js"; -import { handlePaidProduct } from "./handlePaidProduct.js"; import { attachParamsToCurCusProduct } from "../../attachUtils/convertAttachParams.js"; import { getDefaultAttachConfig } from "../../attachUtils/getAttachConfig.js"; import { getMergeCusProduct } from "./getMergeCusProduct.js"; -import { subToPeriodStartEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; +import { handlePaidProduct } from "./handlePaidProduct.js"; export const handleAddProduct = async ({ - req, - res, - attachParams, - config, - branch, + req, + res, + attachParams, + config, + branch, }: { - req: ExtendedRequest; - res?: any; - attachParams: AttachParams; - config?: AttachConfig; - branch?: AttachBranch; + req: ExtendedRequest; + res?: any; + attachParams: AttachParams; + config?: AttachConfig; + branch?: AttachBranch; }) => { - const logger = req.logtail; - const { customer, products, prices } = attachParams; + const logger = req.logtail; + const { customer, products, prices } = attachParams; - const defaultConfig: AttachConfig = getDefaultAttachConfig(); + const defaultConfig: AttachConfig = getDefaultAttachConfig(); - // 1. If paid product + // 1. If paid product - if (prices.length > 0) { - await handlePaidProduct({ - req, - res, - attachParams, - config: config || defaultConfig, - }); + if (prices.length > 0) { + await handlePaidProduct({ + req, + res, + attachParams, + config: config || defaultConfig, + }); - return; - } + return; + } - logger.info("Inserting free product in handleAddProduct"); + logger.info("Inserting free product in handleAddProduct"); - const batchInsert = []; + const batchInsert = []; - const { mergeCusProduct, mergeSub } = await getMergeCusProduct({ - attachParams, - config: config || defaultConfig, - products, - }); + const { mergeSub } = await getMergeCusProduct({ + attachParams, + config: config || defaultConfig, + products, + }); - // console.log("Free trial:", attachParams.freeTrial); - // throw new Error("test"); + // console.log("Free trial:", attachParams.freeTrial); + // throw new Error("test"); - for (const product of products) { - let curCusProduct = attachParamsToCurCusProduct({ attachParams }); - let anchorToUnix = undefined; + for (const product of products) { + const curCusProduct = attachParamsToCurCusProduct({ attachParams }); + let anchorToUnix; - if (curCusProduct && config?.branch == AttachBranch.NewVersion) { - anchorToUnix = curCusProduct.created_at; - } + if (curCusProduct && config?.branch === AttachBranch.NewVersion) { + anchorToUnix = curCusProduct.created_at; + } - if (mergeSub) { - const { end } = subToPeriodStartEnd({ sub: mergeSub }); - anchorToUnix = end * 1000; - } + if (mergeSub) { + const { end } = subToPeriodStartEnd({ sub: mergeSub }); + anchorToUnix = end * 1000; + } - // Expire previous product + // Expire previous product - batchInsert.push( - createFullCusProduct({ - db: req.db, - attachParams: attachToInsertParams(attachParams, product), - billLaterOnly: true, - carryExistingUsages: config?.carryUsage || false, - anchorToUnix, - logger, - }) - ); - } - await Promise.all(batchInsert); + batchInsert.push( + createFullCusProduct({ + db: req.db, + attachParams: attachToInsertParams(attachParams, product), + billLaterOnly: true, + carryExistingUsages: config?.carryUsage || false, + anchorToUnix, + logger, + }), + ); + } + await Promise.all(batchInsert); - logger.info("Successfully created full cus product"); + logger.info("Successfully created full cus product"); - if (res) { - let apiVersion = attachParams.org.api_version || APIVersion.v1; - const productNames = products.map((p) => p.name).join(", "); - const customerName = customer.name || customer.email || customer.id; - if (apiVersion >= APIVersion.v1_1) { - res.status(200).json( - AttachResultSchema.parse({ - success: true, - code: SuccessCode.FreeProductAttached, - message: `Successfully attached ${productNames} to ${customerName}`, - product_ids: products.map((p) => p.id), - customer_id: customer.id || customer.internal_id, - }) - ); - } else { - res.status(200).json({ - success: true, - }); - } - } + if (res) { + const apiVersion = attachParams.org.api_version || APIVersion.v1; + const productNames = products.map((p) => p.name).join(", "); + const customerName = customer.name || customer.email || customer.id; + if (apiVersion >= APIVersion.v1_1) { + res.status(200).json( + AttachResultSchema.parse({ + success: true, + code: SuccessCode.FreeProductAttached, + message: `Successfully attached ${productNames} to ${customerName}`, + product_ids: products.map((p) => p.id), + customer_id: customer.id || customer.internal_id, + }), + ); + } else { + res.status(200).json({ + success: true, + }); + } + } }; diff --git a/server/src/internal/customers/attach/attachFunctions/addProductFlow/handlePaidProduct.ts b/server/src/internal/customers/attach/attachFunctions/addProductFlow/handlePaidProduct.ts index b7af249fc..6c2b0fa1f 100644 --- a/server/src/internal/customers/attach/attachFunctions/addProductFlow/handlePaidProduct.ts +++ b/server/src/internal/customers/attach/attachFunctions/addProductFlow/handlePaidProduct.ts @@ -1,9 +1,18 @@ -import RecaseError from "@/utils/errorUtils.js"; - +import { + APIVersion, + type AttachConfig, + AttachScenario, + ErrCode, + isTrialing, + SuccessCode, +} from "@autumn/shared"; +import type Stripe from "stripe"; +import { getEarliestPeriodEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; import { getStripeSubItems2 } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js"; import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js"; +import { handleCreateCheckout } from "@/internal/customers/add-product/handleCreateCheckout.js"; import { - AttachParams, + type AttachParams, AttachResultSchema, } from "@/internal/customers/cusProducts/AttachParams.js"; import { @@ -12,30 +21,23 @@ import { } from "@/internal/invoices/invoiceUtils.js"; import { attachToInsertParams } from "@/internal/products/productUtils.js"; import { ExtendedRequest } from "@/utils/models/Request.js"; -import { - APIVersion, - AttachBranch, - AttachConfig, - AttachScenario, - ErrCode, - SuccessCode, -} from "@autumn/shared"; -import Stripe from "stripe"; -import { - getEarliestPeriodEnd, - subToPeriodStartEnd, -} from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; import { createStripeSub2 } from "./createStripeSub2.js"; import { getSmallestInterval } from "@/internal/products/prices/priceUtils/priceIntervalUtils.js"; -import { handleCreateCheckout } from "@/internal/customers/add-product/handleCreateCheckout.js"; -import { getCustomerSub } from "../../attachUtils/convertAttachParams.js"; +import { + getCustomerSchedule, + getCustomerSub, + paramsToCurSubSchedule, +} from "../../attachUtils/convertAttachParams.js"; import { paramsToSubItems } from "../../mergeUtils/paramsToSubItems.js"; import { updateStripeSub2 } from "../upgradeFlow/updateStripeSub2.js"; import { subToNewSchedule } from "../../mergeUtils/subToNewSchedule.js"; -import { isTrialing } from "@autumn/shared"; import { getNextStartOfMonthUnix } from "@/internal/products/prices/billingIntervalUtils.js"; import { addIntervalToAnchor } from "@/internal/products/prices/billingIntervalUtils2.js"; +import { handleUpgradeFlowSchedule } from "../upgradeFlow/handleUpgradeFlowSchedule.js"; +import { subIsCanceled } from "@/external/stripe/stripeSubUtils.js"; +import { rewardTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; export const handlePaidProduct = async ({ req, @@ -50,7 +52,7 @@ export const handlePaidProduct = async ({ }) => { const logger = req.logtail; - let { + const { org, customer, products, @@ -69,17 +71,19 @@ export const handlePaidProduct = async ({ config, }); - let subscriptions: Stripe.Subscription[] = []; + const subscriptions: Stripe.Subscription[] = []; const { sub: mergeSub, cusProduct: mergeCusProduct } = await getCustomerSub({ attachParams, }); let sub: Stripe.Subscription | null = null; - let schedule: Stripe.SubscriptionSchedule | null = null; + let schedule: Stripe.SubscriptionSchedule | null | undefined = null; + let invoice: Stripe.Invoice | undefined; let trialEndsAt = undefined; // 1. If merge sub + if (mergeSub && !config.disableMerge) { if (mergeCusProduct?.free_trial) { trialEndsAt = isTrialing({ @@ -89,9 +93,7 @@ export const handlePaidProduct = async ({ ? mergeCusProduct.trial_ends_at : undefined; } - attachParams.freeTrial = null; - // 1. If merged sub is canceled, also add to current schedule const newItemSet = await paramsToSubItems({ req, @@ -100,7 +102,7 @@ export const handlePaidProduct = async ({ config, }); - const { updatedSub } = await updateStripeSub2({ + const { updatedSub, latestInvoice } = await updateStripeSub2({ req, attachParams, curSub: mergeSub, @@ -111,19 +113,45 @@ export const handlePaidProduct = async ({ sub = updatedSub; - if (mergeSub.cancel_at) { - logger.info("ADD PRODUCT FLOW,CREATING NEW SCHEDULE"); + if (latestInvoice) { + invoice = await insertInvoiceFromAttach({ + db: req.db, + stripeInvoice: latestInvoice, + attachParams, + logger, + }); + } + if (subIsCanceled({ sub: mergeSub })) { + logger.info("ADD PRODUCT FLOW, CREATING NEW SCHEDULE"); schedule = await subToNewSchedule({ req, sub: mergeSub, attachParams, config, - endOfBillingPeriod: mergeSub.cancel_at, + endOfBillingPeriod: mergeSub.cancel_at!, removeCusProducts: attachParams.cusProducts.filter((cp) => cp.canceled), }); + } else { + const res = await getCustomerSchedule({ + attachParams, + subId: mergeSub.id, + logger, + }); + schedule = res.schedule; + logger.info(`ADD PRODUCT FLOW, SCHEDULE ID: ${schedule?.id}`); + if (schedule) { + await handleUpgradeFlowSchedule({ + req, + logger, + attachParams, + config, + schedule, + curSub: mergeSub, + removeCusProducts: [], + fromAddProduct: true, + }); + } } - - // 1. } else { let billingCycleAnchorUnix = undefined; const smallestInterval = getSmallestInterval({ @@ -152,6 +180,7 @@ export const handlePaidProduct = async ({ billingCycleAnchorUnix = attachParams.billingAnchor; } + // console.log("Item set: ", itemSet); try { sub = await createStripeSub2({ db: req.db, @@ -162,6 +191,15 @@ export const handlePaidProduct = async ({ config, logger, }); + + if (sub?.latest_invoice) { + invoice = await insertInvoiceFromAttach({ + db: req.db, + stripeInvoice: sub.latest_invoice as Stripe.Invoice, + attachParams, + logger, + }); + } } catch (error: any) { if ( error instanceof RecaseError && @@ -182,16 +220,6 @@ export const handlePaidProduct = async ({ subscriptions.push(sub); - let invoice: Stripe.Invoice | undefined; - if (sub?.latest_invoice) { - invoice = await insertInvoiceFromAttach({ - db: req.db, - stripeInvoice: sub.latest_invoice as Stripe.Invoice, - attachParams, - logger, - }); - } - const anchorToUnix = getEarliestPeriodEnd({ sub }) * 1000; if (config.invoiceCheckout) { @@ -216,7 +244,14 @@ export const handlePaidProduct = async ({ anchorToUnix, carryExistingUsages: config.carryUsage, scenario: AttachScenario.New, - trialEndsAt: trialEndsAt || undefined, + trialEndsAt: + trialEndsAt || + (attachParams.rewardTrial + ? (rewardTrialToStripeTimestamp({ + rewardTrial: attachParams.rewardTrial, + now: attachParams.now, + }) || 0) * 1000 + : undefined), logger, }) ); @@ -224,7 +259,7 @@ export const handlePaidProduct = async ({ await Promise.all(batchInsert); if (res) { - let apiVersion = attachParams.apiVersion || APIVersion.v1; + const apiVersion = attachParams.apiVersion || APIVersion.v1; const productNames = products.map((p) => p.name).join(", "); const customerName = customer.name || customer.email || customer.id; if (apiVersion >= APIVersion.v1_1) { diff --git a/server/src/internal/customers/attach/attachFunctions/handleRenewProduct.ts b/server/src/internal/customers/attach/attachFunctions/handleRenewProduct.ts index a6764f036..fafa0eec3 100644 --- a/server/src/internal/customers/attach/attachFunctions/handleRenewProduct.ts +++ b/server/src/internal/customers/attach/attachFunctions/handleRenewProduct.ts @@ -9,7 +9,7 @@ import { paramsToCurSub, } from "../attachUtils/convertAttachParams.js"; import { paramsToScheduleItems } from "../mergeUtils/paramsToScheduleItems.js"; -import { AttachConfig, SuccessCode } from "@autumn/shared"; +import { AttachConfig, AttachScenario, SuccessCode } from "@autumn/shared"; import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js"; import { cusProductToSchedule, @@ -19,6 +19,8 @@ import { subToNewSchedule } from "../mergeUtils/subToNewSchedule.js"; import { getLatestPeriodEnd } from "@/external/stripe/stripeSubUtils/convertSubUtils.js"; import { updateCurSchedule } from "../mergeUtils/updateCurSchedule.js"; import { subItemInCusProduct } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; +import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js"; +import { addSubIdToCache } from "../../cusCache/subCacheUtils.js"; export const handleRenewProduct = async ({ req, @@ -33,9 +35,9 @@ export const handleRenewProduct = async ({ }) => { const logger = req.logtail; const { stripeCli, customer: fullCus } = attachParams; + const { curScheduledProduct } = attachParamToCusProducts({ attachParams }); const curCusProduct = attachParamsToCurCusProduct({ attachParams }); - const { curScheduledProduct } = attachParamToCusProducts({ attachParams }); const product = attachParams.products[0]; const cusProducts = attachParams.customer.customer_products; @@ -91,6 +93,11 @@ export const handleRenewProduct = async ({ } if (curSubId) { + // Add sub id to upstash + await addSubIdToCache({ + subId: curSubId, + scenario: AttachScenario.Renew, + }); await stripeCli.subscriptions.update(curSubId, { cancel_at: null, }); @@ -193,6 +200,24 @@ export const handleRenewProduct = async ({ } } + if (curCusProduct) { + try { + await addProductsUpdatedWebhookTask({ + req, + internalCustomerId: curCusProduct.internal_customer_id, + org: attachParams.org, + env: attachParams.customer.env, + customerId: + attachParams.customer.id || attachParams.customer.internal_id, + scenario: AttachScenario.Renew, + cusProduct: curCusProduct, + logger, + }); + } catch (error) { + logger.error("RENEW FLOW: failed to add to webhook queue", { error }); + } + } + if (curScheduledProduct) { await CusProductService.delete({ db: req.db, diff --git a/server/src/internal/customers/attach/attachFunctions/scheduleFlow/handleScheduleFlow2.ts b/server/src/internal/customers/attach/attachFunctions/scheduleFlow/handleScheduleFlow2.ts index 59363f826..1fa90b73d 100644 --- a/server/src/internal/customers/attach/attachFunctions/scheduleFlow/handleScheduleFlow2.ts +++ b/server/src/internal/customers/attach/attachFunctions/scheduleFlow/handleScheduleFlow2.ts @@ -26,6 +26,7 @@ import { subToNewSchedule } from "../../mergeUtils/subToNewSchedule.js"; import { updateCurSchedule } from "../../mergeUtils/updateCurSchedule.js"; import { subItemInCusProduct } from "@/external/stripe/stripeSubUtils/stripeSubItemUtils.js"; import { getCurrentPhaseIndex } from "../../mergeUtils/phaseUtils/phaseUtils.js"; +import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js"; export const handleScheduleFunction2 = async ({ req, @@ -138,6 +139,9 @@ export const handleScheduleFunction2 = async ({ logger.info(`SCHEDULE FLOW: no schedule, canceling sub ${curSub?.id}`); await stripeCli.subscriptions.update(curSub!.id, { cancel_at: expectedEnd!, + cancellation_details: { + comment: "autumn_downgrade", + }, }); } @@ -150,13 +154,36 @@ export const handleScheduleFunction2 = async ({ nextResetAt: expectedEnd! * 1000, disableFreeTrial: true, isDowngrade: true, - scenario: newProductFree - ? AttachScenario.Cancel - : AttachScenario.Downgrade, + sendWebhook: false, + // scenario: newProductFree + // ? AttachScenario.Cancel + // : AttachScenario.Downgrade, logger, }); } + if (curCusProduct) { + try { + await addProductsUpdatedWebhookTask({ + req, + internalCustomerId: curCusProduct.internal_customer_id, + org: attachParams.org, + env: attachParams.customer.env, + customerId: + attachParams.customer.id || attachParams.customer.internal_id, + + scenario: isFreeProduct(attachParams.prices) + ? AttachScenario.Cancel + : AttachScenario.Downgrade, + + cusProduct: curCusProduct, + logger, + }); + } catch (error) { + logger.error("SCHEDULE FLOW: failed to add to webhook queue", { error }); + } + } + let apiVersion = attachParams.apiVersion || APIVersion.v1; if (res) { diff --git a/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityDowngrade.ts b/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityDowngrade.ts index ee4db0f67..30a7e9a1f 100644 --- a/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityDowngrade.ts +++ b/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityDowngrade.ts @@ -1,4 +1,5 @@ import { + calculateProrationAmount, Feature, FeatureOptions, FullCusProduct, @@ -76,9 +77,12 @@ export const handleQuantityDowngrade = async ({ ) .toNumber(); - const diffWithBillingUnits = new Decimal(difference) - .mul((cusPrice.price.config as UsagePriceConfig).billing_units || 1) - .toNumber(); + const billingUnits = + (cusPrice.price.config as UsagePriceConfig).billing_units || 1; + + // const diffWithBillingUnits = new Decimal(difference) + // .mul((cusPrice.price.config as UsagePriceConfig).billing_units || 1) + // .toNumber(); const newSubItemQuantity = new Decimal(subItem.quantity || 0) .plus(subItemDifference) @@ -91,14 +95,24 @@ export const handleQuantityDowngrade = async ({ const createDowngradeInvoice = async () => { const { start, end } = subToPeriodStartEnd({ sub: stripeSub }); - const amount = priceToInvoiceAmount({ + const prevAmount = priceToInvoiceAmount({ price: cusPrice.price, - quantity: diffWithBillingUnits, - proration: { - start: start * 1000, - end: end * 1000, - }, - now: attachParams.now, + quantity: new Decimal(oldOptions.quantity).mul(billingUnits!).toNumber(), + }); + + const newAmount = priceToInvoiceAmount({ + price: cusPrice.price, + quantity: new Decimal(newOptions.quantity).mul(billingUnits!).toNumber(), + }); + + let amount = new Decimal(newAmount).minus(prevAmount).toNumber(); + + amount = calculateProrationAmount({ + periodEnd: end * 1000, + periodStart: start * 1000, + now: attachParams.now || Date.now(), + amount, + allowNegative: true, }); const product = cusProductToProduct({ cusProduct }); @@ -186,7 +200,6 @@ export const handleQuantityDowngrade = async ({ if (cusEnt) { const config = cusPrice.price.config as UsagePriceConfig; - const billingUnits = config.billing_units || 1; let decrementBy = new Decimal(oldOptions.quantity) .minus(new Decimal(newOptions.quantity)) .mul(billingUnits) diff --git a/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityUpgrade.ts b/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityUpgrade.ts index a22322788..0677f5afe 100644 --- a/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityUpgrade.ts +++ b/server/src/internal/customers/attach/attachFunctions/updateQuantityFlow/handleQuantityUpgrade.ts @@ -1,8 +1,10 @@ import { + calculateProrationAmount, Feature, FeatureOptions, FullCusProduct, FullCustomerPrice, + getAmountForQuantity, getFeatureInvoiceDescription, OnIncrease, UsagePriceConfig, @@ -69,6 +71,9 @@ export const handleQuantityUpgrade = async ({ OnIncrease.ProrateImmediately; const prorate = shouldProrate(onIncrease); + const config = cusPrice.price.config as UsagePriceConfig; + const billingUnits = config.billing_units || 1; + const diffWithBillingUnits = new Decimal(difference) .mul((cusPrice.price.config as UsagePriceConfig).billing_units || 1) .toNumber(); @@ -76,20 +81,38 @@ export const handleQuantityUpgrade = async ({ if (prorate && stripeSub?.status !== "trialing") { const { start, end } = subToPeriodStartEnd({ sub: stripeSub }); - const amount = priceToInvoiceAmount({ + const prevAmount = priceToInvoiceAmount({ price: cusPrice.price, - quantity: diffWithBillingUnits, - proration: prorate - ? { - start: start * 1000, - end: end * 1000, - } - : undefined, - now, + quantity: new Decimal(oldOptions.quantity).mul(billingUnits!).toNumber(), }); - const config = cusPrice.price.config as UsagePriceConfig; - const billingUnits = config.billing_units; + const newAmount = priceToInvoiceAmount({ + price: cusPrice.price, + quantity: new Decimal(newOptions.quantity).mul(billingUnits!).toNumber(), + }); + + let amount = new Decimal(newAmount).minus(prevAmount).toNumber(); + if (prorate) { + amount = calculateProrationAmount({ + periodEnd: end * 1000, + periodStart: start * 1000, + now: now || Date.now(), + amount, + }); + } + + // const amount = priceToInvoiceAmount({ + // price: cusPrice.price, + // quantity: diffWithBillingUnits, + // proration: prorate + // ? { + // start: start * 1000, + // end: end * 1000, + // } + // : undefined, + // now, + // }); + const feature = features.find( (f: Feature) => f.internal_id == newOptions.internal_feature_id )!; @@ -160,8 +183,7 @@ export const handleQuantityUpgrade = async ({ }); // Update cus ent - const config = cusPrice.price.config as UsagePriceConfig; - const billingUnits = config.billing_units || 1; + let cusEnt = getRelatedCusEnt({ cusPrice, cusEnts: cusProduct.customer_entitlements, diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlow.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlow.ts index 756f63a6d..25744e4f6 100644 --- a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlow.ts +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlow.ts @@ -4,6 +4,7 @@ import { } from "@/internal/customers/cusProducts/AttachParams.js"; import { attachParamsToCurCusProduct, + attachParamsToProduct, paramsToCurSub, paramsToCurSubSchedule, } from "../../attachUtils/convertAttachParams.js"; @@ -16,6 +17,8 @@ import { AttachConfig, AttachScenario, CusProductStatus, + cusProductToProduct, + logCusProducts, ProrationBehavior, } from "@autumn/shared"; import { ExtendedRequest } from "@/utils/models/Request.js"; @@ -72,13 +75,11 @@ export const handleUpgradeFlow = async ({ const { subItems } = newItemSet; - // for (const item of subItems) { - // const { autumnPrice, ...rest } = item; - // console.log("ITEM:", rest); - // } + const products = attachParams.fromCancel + ? [cusProductToProduct({ cusProduct: attachParams.cusProduct! })] + : attachParams.products; - // Delete scheduled products if needed - for (const product of attachParams.products) { + for (const product of products) { if (product.is_add_on) continue; const { curScheduledProduct } = getExistingCusProducts({ @@ -207,6 +208,7 @@ export const handleUpgradeFlow = async ({ carryExistingUsages: config.carryUsage, carryOverTrial: config.carryTrial, anchorToUnix: anchorToUnix, + scenario: AttachScenario.Upgrade, logger, }); } diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlowSchedule.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlowSchedule.ts index d254e0276..044224a6f 100644 --- a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlowSchedule.ts +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/handleUpgradeFlowSchedule.ts @@ -24,6 +24,7 @@ export const handleUpgradeFlowSchedule = async ({ curSub, removeCusProducts, logger, + fromAddProduct = false, }: { req: ExtendedRequest; attachParams: AttachParams; @@ -32,17 +33,17 @@ export const handleUpgradeFlowSchedule = async ({ curSub: Stripe.Subscription; removeCusProducts?: FullCusProduct[]; logger: any; + fromAddProduct?: boolean; }) => { - logger.info(`UPGRADE FLOW, updating schedule ${schedule?.id}`); + if (fromAddProduct) { + logger.info(`ADD PRODUCT FLOW, updating schedule ${schedule?.id}`); + } else { + logger.info(`UPGRADE FLOW, updating schedule ${schedule?.id}`); + } + const { stripeCli, customer, prices } = attachParams; const curCusProduct = attachParamsToCurCusProduct({ attachParams }); - // console.log("CUR ITEMS:"); - // await logPhases({ - // phases: schedule.phases as any, - // db: req.db, - // }); - const currentPhaseIndex = getCurrentPhaseIndex({ schedule, now: attachParams.now, diff --git a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSub2.ts b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSub2.ts index 614063fbf..8381d617c 100644 --- a/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSub2.ts +++ b/server/src/internal/customers/attach/attachFunctions/upgradeFlow/updateStripeSub2.ts @@ -1,155 +1,163 @@ -import Stripe from "stripe"; -import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; -import { AttachConfig, ProrationBehavior } from "@autumn/shared"; -import { attachParamToCusProducts } from "../../attachUtils/convertAttachParams.js"; -import { - createUsageInvoiceItems, - resetUsageBalances, -} from "../upgradeDiffIntFlow/createUsageInvoiceItems.js"; -import { ExtendedRequest } from "@/utils/models/Request.js"; -import { createProrationInvoice } from "@/external/stripe/stripeSubUtils/updateStripeSub/createProrationinvoice.js"; -import { createAndFilterContUseItems } from "../../attachUtils/getContUseItems/createContUseInvoiceItems.js"; -import { freeTrialToStripeTimestamp } from "@/internal/products/free-trials/freeTrialUtils.js"; -import { getContUseInvoiceItems } from "../../attachUtils/getContUseItems/getContUseInvoiceItems.js"; -import { ItemSet } from "@/utils/models/ItemSet.js"; +import { type AttachConfig, ProrationBehavior } from "@autumn/shared"; +import type Stripe from "stripe"; import { sanitizeSubItems } from "@/external/stripe/stripeSubUtils/getStripeSubItems.js"; +import { createProrationInvoice } from "@/external/stripe/stripeSubUtils/updateStripeSub/createProrationinvoice.js"; +import type { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; +import { + freeTrialToStripeTimestamp, + rewardTrialToStripeTimestamp, +} from "@/internal/products/free-trials/freeTrialUtils.js"; import { SubService } from "@/internal/subscriptions/SubService.js"; -import { createStripeCli } from "@/external/stripe/utils.js"; -import { nullish } from "@/utils/genUtils.js"; import RecaseError from "@/utils/errorUtils.js"; +import { nullish } from "@/utils/genUtils.js"; +import type { ItemSet } from "@/utils/models/ItemSet.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; +import { attachParamToCusProducts } from "../../attachUtils/convertAttachParams.js"; +import { createAndFilterContUseItems } from "../../attachUtils/getContUseItems/createContUseInvoiceItems.js"; +import { + createUsageInvoiceItems, + resetUsageBalances, +} from "../upgradeDiffIntFlow/createUsageInvoiceItems.js"; export const updateStripeSub2 = async ({ - req, - attachParams, - config, - curSub, - itemSet, - fromCreate = false, + req, + attachParams, + config, + curSub, + itemSet, + fromCreate = false, }: { - req: ExtendedRequest; - attachParams: AttachParams; - config: AttachConfig; - curSub: Stripe.Subscription; - itemSet: ItemSet; - fromCreate?: boolean; + req: ExtendedRequest; + attachParams: AttachParams; + config: AttachConfig; + curSub: Stripe.Subscription; + itemSet: ItemSet; + fromCreate?: boolean; }) => { - const { db, logger } = req; + const { db, logger } = req; - const { stripeCli, customer, org, paymentMethod } = attachParams; - const { invoiceOnly, proration } = config; + const { stripeCli, customer, org, paymentMethod, rewardTrial } = attachParams; + const { invoiceOnly, proration } = config; - if (!invoiceOnly && !attachParams.fromCancel && nullish(paymentMethod)) { - throw new RecaseError({ - message: "Payment method is required", - code: "payment_method_required", - }); - } + if ( + !invoiceOnly && + !attachParams.fromCancel && + !rewardTrial && + nullish(paymentMethod) + ) { + throw new RecaseError({ + message: "Payment method is required", + code: "payment_method_required", + }); + } - if (curSub.billing_mode.type !== "flexible") { - curSub = await stripeCli.subscriptions.migrate(curSub.id, { - billing_mode: { type: "flexible" }, - }); - } + if (curSub.billing_mode.type !== "flexible") { + curSub = await stripeCli.subscriptions.migrate(curSub.id, { + billing_mode: { type: "flexible" }, + }); + } - let trialEnd = - config.disableTrial || config.carryTrial - ? undefined - : freeTrialToStripeTimestamp({ - freeTrial: attachParams.freeTrial, - now: attachParams.now, - }); + const trialEnd = + config.disableTrial || config.carryTrial + ? undefined + : rewardTrial?.duration_value + ? rewardTrialToStripeTimestamp({ rewardTrial, now: attachParams.now }) + : freeTrialToStripeTimestamp({ + freeTrial: attachParams.freeTrial, + now: attachParams.now, + }); - // 1. Update subscription + // 1. Update subscription - let updatedSub = await stripeCli.subscriptions.update(curSub.id, { - items: sanitizeSubItems(itemSet.subItems), - proration_behavior: - proration == ProrationBehavior.None - ? "none" - : fromCreate - ? "always_invoice" - : "create_prorations", - // proration_behavior: "create_prorations", - trial_end: trialEnd, - // default_payment_method: paymentMethod?.id, - add_invoice_items: itemSet.invoiceItems, - ...((invoiceOnly && { - collection_method: "send_invoice", - days_until_due: 30, - }) as any), - payment_behavior: "error_if_incomplete", - expand: ["latest_invoice"], - }); + const updatedSub = await stripeCli.subscriptions.update(curSub.id, { + items: sanitizeSubItems(itemSet.subItems), + proration_behavior: + proration === ProrationBehavior.None + ? "none" + : fromCreate + ? "always_invoice" + : "create_prorations", + // proration_behavior: "create_prorations", + trial_end: trialEnd, + // default_payment_method: paymentMethod?.id, + add_invoice_items: itemSet.invoiceItems, + ...((invoiceOnly && { + collection_method: "send_invoice", + days_until_due: 30, + }) as any), + payment_behavior: "error_if_incomplete", + expand: ["latest_invoice"], + }); - let latestInvoice = updatedSub.latest_invoice as Stripe.Invoice | null; + let latestInvoice = updatedSub.latest_invoice as Stripe.Invoice | null; - await SubService.updateFromStripe({ db, stripeSub: updatedSub }); + await SubService.updateFromStripe({ db, stripeSub: updatedSub }); - if (proration == ProrationBehavior.None) { - return { - updatedSub, - latestInvoice: null, - }; - } + if (proration === ProrationBehavior.None) { + return { + updatedSub, + latestInvoice: null, + }; + } - if (fromCreate) { - return { - updatedSub, - latestInvoice: updatedSub.latest_invoice as Stripe.Invoice, - }; - } + if (fromCreate) { + return { + updatedSub, + latestInvoice: updatedSub.latest_invoice as Stripe.Invoice, + }; + } - const { curMainProduct } = attachParamToCusProducts({ attachParams }); + const { curMainProduct } = attachParamToCusProducts({ attachParams }); - // 2. Create prorations for single use items - let { invoiceItems, cusEntIds } = await createUsageInvoiceItems({ - db, - attachParams, - cusProduct: curMainProduct!, - sub: curSub, - logger, - }); + // 2. Create prorations for single use items + const { invoiceItems, cusEntIds } = await createUsageInvoiceItems({ + db, + attachParams, + cusProduct: curMainProduct!, + sub: curSub, + logger, + }); - // // // 3. Create prorations for continuous use items - // let { replaceables, newItems } = await getContUseInvoiceItems({ - // attachParams, - // cusProduct: curMainProduct!, - // sub: curSub, - // logger, - // }); + // // // 3. Create prorations for continuous use items + // let { replaceables, newItems } = await getContUseInvoiceItems({ + // attachParams, + // cusProduct: curMainProduct!, + // sub: curSub, + // logger, + // }); - const { replaceables } = await createAndFilterContUseItems({ - attachParams, - curMainProduct: curMainProduct!, - sub: curSub, - logger, - }); + const { replaceables } = await createAndFilterContUseItems({ + attachParams, + curMainProduct: curMainProduct!, + sub: curSub, + logger, + }); - if (proration === ProrationBehavior.Immediately) { - latestInvoice = await createProrationInvoice({ - attachParams, - invoiceOnly, - curSub, - updatedSub, - logger, - }); + if (proration === ProrationBehavior.Immediately) { + latestInvoice = await createProrationInvoice({ + attachParams, + invoiceOnly, + curSub, + updatedSub, + logger, + }); - console.log(`FINALIZED INVOICE ${latestInvoice?.id}`); - console.log(latestInvoice?.lines.data.map((line) => line.description)); - } + console.log(`FINALIZED INVOICE ${latestInvoice?.id}`); + console.log(latestInvoice?.lines.data.map((line) => line.description)); + } - await resetUsageBalances({ - db, - cusEntIds, - cusProduct: curMainProduct!, - }); + await resetUsageBalances({ + db, + cusEntIds, + cusProduct: curMainProduct!, + }); - return { - updatedSub, - latestInvoice: latestInvoice, - cusEntIds, - replaceables, - }; + return { + updatedSub, + latestInvoice: latestInvoice, + cusEntIds, + replaceables, + }; }; // await SubService.addUsageFeatures({ diff --git a/server/src/internal/customers/attach/attachUtils/attachParams/convertToParams.ts b/server/src/internal/customers/attach/attachUtils/attachParams/convertToParams.ts index 8ff2bf59d..7651abbe2 100644 --- a/server/src/internal/customers/attach/attachUtils/attachParams/convertToParams.ts +++ b/server/src/internal/customers/attach/attachUtils/attachParams/convertToParams.ts @@ -1,23 +1,29 @@ import { - AttachParams, - InsertCusProductParams, -} from "@/internal/customers/cusProducts/AttachParams.js"; -import { + type Customer, cusProductToEnts, cusProductToPrices, cusProductToProduct, + type Entity, + type FreeTrial, + type FullCusProduct, + type FullCustomer, + type FullProduct, + type FullRewardProgram, + type Organization, } from "@autumn/shared"; +import type Stripe from "stripe"; +import { createStripeCli } from "@/external/stripe/utils.js"; +import type { + AttachParams, + InsertCusProductParams, +} from "@/internal/customers/cusProducts/AttachParams.js"; import { newCusToFullCus } from "@/internal/customers/cusUtils/cusUtils.js"; -import { ExtendedRequest } from "@/utils/models/Request.js"; import { - Customer, - Entity, - FullCusProduct, - FullCustomer, - FullProduct, - FreeTrial, -} from "@autumn/shared"; -import Stripe from "stripe"; + isFreeProduct, + isOneOff, + itemsAreOneOff, +} from "@/internal/products/productUtils.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; export const webhookToAttachParams = ({ req, @@ -156,3 +162,44 @@ export const newCusToInsertParams = ({ features: [], } satisfies InsertCusProductParams; }; + +export const rewardProgramToAttachParams = ({ + req, + rewardProgram, + customer, + product, + org, +}: { + req: ExtendedRequest; + rewardProgram: FullRewardProgram; + customer: FullCustomer; + product: FullProduct; + org?: Organization; +}): AttachParams => { + const reward = rewardProgram.reward; + const isPaid = !isFreeProduct(product.prices); + const isRecurring = + !isOneOff(product.prices) && !itemsAreOneOff(product.entitlements); + + return { + req, + org: org || req.org, + customer, + products: [product], + prices: product.prices, + entitlements: product.entitlements, + freeTrial: null, + // rewardTrial: + // (isPaid && isRecurring && reward.free_product_config) + // ? reward.free_product_config + // : null, + rewards: [reward], + optionsList: [], + cusProducts: customer.customer_products, + entities: [], + features: req.features, + stripeCli: createStripeCli({ org: org || req.org, env: req.env }), + paymentMethod: null, + replaceables: [], + } satisfies AttachParams; +}; diff --git a/server/src/internal/customers/attach/attachUtils/convertAttachParams.ts b/server/src/internal/customers/attach/attachUtils/convertAttachParams.ts index c590d8a1a..f315d0db6 100644 --- a/server/src/internal/customers/attach/attachUtils/convertAttachParams.ts +++ b/server/src/internal/customers/attach/attachUtils/convertAttachParams.ts @@ -1,6 +1,6 @@ import { AttachParams } from "@/internal/customers/cusProducts/AttachParams.js"; import { getExistingCusProducts } from "../../cusProducts/cusProductUtils/getExistingCusProducts.js"; -import { cusProductToProduct } from "@autumn/shared"; +import { CusProductStatus, cusProductToProduct } from "@autumn/shared"; import Stripe from "stripe"; export const attachParamsToCurCusProduct = ({ @@ -134,19 +134,6 @@ export const getCustomerSub = async ({ // If there's only one customer product on sub, and it's still trialing, return undefined, because should just replace sub. const curCusProduct = attachParamsToCurCusProduct({ attachParams }); - // const cusProductsOnSub = cusProducts.filter( - // (cp) => - // cp.subscription_ids?.includes(cusProduct.subscription_ids![0]) && - // curCusProduct?.id !== cp.id - // ); - - // if ( - // cusProductsOnSub.length === 1 && - // isTrialing({ cusProduct, now: attachParams.now }) - // ) { - // return { sub: undefined, cusProduct: undefined }; - // } - const sub = await stripeCli.subscriptions.retrieve(subId, { expand: [ "items.data.price.tiers", @@ -158,6 +145,80 @@ export const getCustomerSub = async ({ return { subId, sub, cusProduct }; }; +export const getCustomerSchedule = async ({ + attachParams, + subId, + logger, +}: { + attachParams: AttachParams; + subId?: string; + logger: any; +}) => { + const { stripeCli } = attachParams; + const fullCus = attachParams.customer; + let cusProducts = fullCus.customer_products; + + const targetGroup = attachParams.products[0].group; + const targetEntityId = attachParams.internalEntityId || null; + const targetProductId = attachParams.products[0].id; + + cusProducts.sort((a, b) => { + // 1. Check same group + const aGroupMatches = a.product.group === targetGroup; + const bGroupMatches = b.product.group === targetGroup; + + if (aGroupMatches && !bGroupMatches) return -1; + if (!aGroupMatches && bGroupMatches) return 1; + + // 2. Check main product + const aMain = !a.product.is_add_on; + const bMain = !b.product.is_add_on; + + if (aMain && !bMain) return -1; + if (!aMain && bMain) return 1; + + // 3. Check same product + const aProductIdMatches = a.product.id === targetProductId; + const bProductIdMatches = b.product.id === targetProductId; + + if (aProductIdMatches && !bProductIdMatches) return -1; + if (!aProductIdMatches && bProductIdMatches) return 1; + + // 4. Check same entity + const aEntityIdMatches = (a.internal_entity_id || null) === targetEntityId; + const bEntityIdMatches = (b.internal_entity_id || null) === targetEntityId; + + if (aEntityIdMatches && !bEntityIdMatches) return -1; + if (!aEntityIdMatches && bEntityIdMatches) return 1; + + return 0; + }); + + // const subId = cusProducts.flatMap((cp) => cp.subscription_ids || [])?.[0]; + const scheduleIds = cusProducts.flatMap((cp) => cp.scheduled_ids || []); + if (scheduleIds.length === 0) return { schedule: undefined }; + + try { + const schedules = await stripeCli.subscriptionSchedules.list({ + customer: fullCus.processor.id!, + expand: ["data.phases.items.price"], + }); + + const schedule = schedules.data.find( + (schedule) => + scheduleIds.includes(schedule.id) && + (subId ? schedule.subscription === subId : true) + ); + + return { schedule }; + } catch (error: any) { + logger.error(`Error getting schedule, ids: ${scheduleIds}`, { + message: error.message, + }); + return { schedule: undefined }; + } +}; + export const paramsToCurSub = async ({ attachParams, }: { @@ -206,11 +267,5 @@ export const paramsToCurSubSchedule = async ({ return undefined; } - // const batchPricesGet = []; - // for (const item of schedule.phases[0].items) { - // batchPricesGet.push(stripeCli.prices.retrieve(item.price as string)); - // } - // const prices = await Promise.all(batchPricesGet); - return schedule as Stripe.SubscriptionSchedule; }; diff --git a/server/src/internal/customers/attach/attachUtils/getAttachBranch.ts b/server/src/internal/customers/attach/attachUtils/getAttachBranch.ts index 315d8e546..ef80b235f 100644 --- a/server/src/internal/customers/attach/attachUtils/getAttachBranch.ts +++ b/server/src/internal/customers/attach/attachUtils/getAttachBranch.ts @@ -99,9 +99,7 @@ const getOptionsToUpdate = ({ internalFeatureId: internalFeatureId!, }); - if (price?.config.interval == BillingInterval.OneOff) { - continue; - } + if (price?.config.interval == BillingInterval.OneOff) continue; if (existingOptions && existingOptions.quantity !== newOptions.quantity) { optionsToUpdate.push({ @@ -117,9 +115,13 @@ const getOptionsToUpdate = ({ export const checkSameCustom = async ({ attachParams, curSameProduct, + fromPreview, + optionsToUpdate, }: { attachParams: AttachParams; curSameProduct: FullCusProduct; + fromPreview?: boolean; + optionsToUpdate: { new: FeatureOptions; old: FeatureOptions }[]; }) => { let product = attachParams.products[0]; @@ -136,6 +138,19 @@ export const checkSameCustom = async ({ }); if (itemsSame && freeTrialsSame) { + if ( + fromPreview && + hasPrepaidPrice({ prices: attachParams.prices, excludeOneOff: true }) + ) { + return AttachBranch.UpdatePrepaidQuantity; + } + + // 1. If prepaid quantity changed + if (optionsToUpdate.length > 0) { + attachParams.optionsToUpdate = optionsToUpdate; + return AttachBranch.UpdatePrepaidQuantity; + } + throw new RecaseError({ message: `Items specified for ${product.name} are the same as the existing product, can't attach again`, code: ErrCode.InvalidRequest, @@ -175,29 +190,25 @@ const getSameProductBranch = async ({ return AttachBranch.NewVersion; } - // 2. Same custom? - - if (attachParams.isCustom && curScheduledProduct?.product.id !== product.id) { - return await checkSameCustom({ attachParams, curSameProduct }); - } - let optionsToUpdate = getOptionsToUpdate({ oldOptionsList: curSameProduct.options, newOptionsList: attachParams.optionsList, curSameProduct, }); + // 2. Same custom? + if (attachParams.isCustom && curScheduledProduct?.product.id !== product.id) { + return await checkSameCustom({ + attachParams, + curSameProduct, + fromPreview, + optionsToUpdate, + }); + } + // 1. If prepaid quantity changed if (optionsToUpdate.length > 0) { attachParams.optionsToUpdate = optionsToUpdate; - if (attachParams.isCustom) { - throw new RecaseError({ - message: `Not allowed to update prepaid quantity for current product if is_custom is true`, - code: ErrCode.InternalError, - statusCode: 500, - }); - } - return AttachBranch.UpdatePrepaidQuantity; } diff --git a/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts b/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts index f8f34fcc4..7167a3411 100644 --- a/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts +++ b/server/src/internal/customers/attach/attachUtils/getAttachConfig.ts @@ -124,9 +124,21 @@ export const getAttachConfig = async ({ branch != AttachBranch.MultiAttachUpdate); const onlyCheckout = !isFree && checkoutFlow && !freeTrialWithoutCardRequired; - const disableMerge = branch == AttachBranch.MainIsTrial || onlyCheckout; + // Require payment method... + let paymentMethodRequired = true; + if ( + !disableTrial && + attachParams.freeTrial && + attachParams.freeTrial.card_required === false + ) { + paymentMethodRequired = false; + } + if (attachParams.invoiceOnly) { + paymentMethodRequired = false; + } + let config: AttachConfig = { branch, onlyCheckout, @@ -141,6 +153,7 @@ export const getAttachConfig = async ({ finalizeInvoice: notNullish(attachBody.finalize_invoice) ? attachBody.finalize_invoice! : true, + requirePaymentMethod: paymentMethodRequired, }; return { flags, config }; @@ -159,6 +172,7 @@ export const getDefaultAttachConfig = () => { carryTrial: false, invoiceCheckout: false, finalizeInvoice: true, + requirePaymentMethod: true, }; return config; diff --git a/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts b/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts index 1f06e5ee8..5729d904c 100644 --- a/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts +++ b/server/src/internal/customers/attach/attachUtils/getAttachFunction.ts @@ -1,23 +1,26 @@ +import { + type AttachBody, + AttachBranch, + type AttachConfig, + AttachFunction, + CusProductStatus, +} from "@autumn/shared"; import chalk from "chalk"; -import { AttachParams } from "../../cusProducts/AttachParams.js"; -import { AttachBranch, AttachFunction, CusProductStatus } from "@autumn/shared"; import { handleCreateCheckout } from "../../add-product/handleCreateCheckout.js"; +import { handleCreateInvoiceCheckout } from "../../add-product/handleCreateInvoiceCheckout.js"; +import type { AttachParams } from "../../cusProducts/AttachParams.js"; +import { CusProductService } from "../../cusProducts/CusProductService.js"; import { handleAddProduct } from "../attachFunctions/addProductFlow/handleAddProduct.js"; -import { AttachBody } from "@autumn/shared"; -import { AttachConfig } from "@autumn/shared"; +import { handleOneOffFunction } from "../attachFunctions/addProductFlow/handleOneOffFunction.js"; +import { handleRenewProduct } from "../attachFunctions/handleRenewProduct.js"; +import { handleMultiAttachFlow } from "../attachFunctions/multiAttach/handleMultiAttachFlow.js"; +import { handleScheduleFunction2 } from "../attachFunctions/scheduleFlow/handleScheduleFlow2.js"; import { handleUpdateQuantityFunction } from "../attachFunctions/updateQuantityFlow/updateQuantityFlow.js"; - +import { handleUpgradeFlow } from "../attachFunctions/upgradeFlow/handleUpgradeFlow.js"; import { attachParamsToCurCusProduct, attachParamToCusProducts, } from "./convertAttachParams.js"; -import { handleOneOffFunction } from "../attachFunctions/addProductFlow/handleOneOffFunction.js"; -import { CusProductService } from "../../cusProducts/CusProductService.js"; -import { handleCreateInvoiceCheckout } from "../../add-product/handleCreateInvoiceCheckout.js"; -import { handleUpgradeFlow } from "../attachFunctions/upgradeFlow/handleUpgradeFlow.js"; -import { handleScheduleFunction2 } from "../attachFunctions/scheduleFlow/handleScheduleFlow2.js"; -import { handleRenewProduct } from "../attachFunctions/handleRenewProduct.js"; -import { handleMultiAttachFlow } from "../attachFunctions/multiAttach/handleMultiAttachFlow.js"; /* 1. If from new version, free trial should just carry over @@ -27,252 +30,252 @@ import { handleMultiAttachFlow } from "../attachFunctions/multiAttach/handleMult */ export const getAttachFunction = async ({ - branch, - attachParams, - attachBody, - config, + branch, + attachParams, + attachBody, + config, }: { - branch: AttachBranch; - attachParams: AttachParams; - attachBody: AttachBody; - config: AttachConfig; + branch: AttachBranch; + attachParams: AttachParams; + attachBody: AttachBody; + config: AttachConfig; }) => { - const { onlyCheckout } = config; - const { curCusProduct } = attachParamToCusProducts({ - attachParams, - }); + const { onlyCheckout } = config; + const { curCusProduct } = attachParamToCusProducts({ + attachParams, + }); - // 1. Checkout function - const newScenario = [ - AttachBranch.MultiAttach, - AttachBranch.MultiProduct, - AttachBranch.OneOff, - AttachBranch.New, - AttachBranch.AddOn, - AttachBranch.MainIsFree, - AttachBranch.MainIsTrial, - ].includes(branch); + // 1. Checkout function + const newScenario = [ + AttachBranch.MultiAttach, + AttachBranch.MultiProduct, + AttachBranch.OneOff, + AttachBranch.New, + AttachBranch.AddOn, + AttachBranch.MainIsFree, + AttachBranch.MainIsTrial, + ].includes(branch); - if (newScenario && onlyCheckout) { - return AttachFunction.CreateCheckout; - } else if (branch == AttachBranch.OneOff) { - return AttachFunction.OneOff; - } else if ( - branch == AttachBranch.MultiAttach || - branch == AttachBranch.MultiAttachUpdate - ) { - return AttachFunction.MultiAttach; - } else if (newScenario) { - return AttachFunction.AddProduct; - } + if (newScenario && onlyCheckout) { + return AttachFunction.CreateCheckout; + } else if (branch == AttachBranch.OneOff) { + return AttachFunction.OneOff; + } else if ( + branch == AttachBranch.MultiAttach || + branch == AttachBranch.MultiAttachUpdate + ) { + return AttachFunction.MultiAttach; + } else if (newScenario) { + return AttachFunction.AddProduct; + } - // 2. Upgrade scenarios - let updateScenarios = [ - AttachBranch.NewVersion, - AttachBranch.SameCustom, - AttachBranch.SameCustomEnts, - AttachBranch.Upgrade, - ]; + // 2. Upgrade scenarios + const updateScenarios = [ + AttachBranch.NewVersion, + AttachBranch.SameCustom, + AttachBranch.SameCustomEnts, + AttachBranch.Upgrade, + ]; - if (updateScenarios.includes(branch)) { - if (config.sameIntervals) { - return AttachFunction.UpgradeSameInterval; - } else { - return AttachFunction.UpgradeDiffInterval; - } - } + if (updateScenarios.includes(branch)) { + if (config.sameIntervals) { + return AttachFunction.UpgradeSameInterval; + } else { + return AttachFunction.UpgradeDiffInterval; + } + } - // 3. Downgrade scenarios - if (branch == AttachBranch.Downgrade) { - return AttachFunction.ScheduleProduct; - } + // 3. Downgrade scenarios + if (branch == AttachBranch.Downgrade) { + return AttachFunction.ScheduleProduct; + } - // 4. Prepaid scenarios - if (branch == AttachBranch.UpdatePrepaidQuantity) { - let curSameProduct = attachParamsToCurCusProduct({ attachParams }); - if (curSameProduct?.free_trial) { - attachParams.freeTrial = curSameProduct.free_trial; - } - return AttachFunction.UpdatePrepaidQuantity; - } + // 4. Prepaid scenarios + if (branch == AttachBranch.UpdatePrepaidQuantity) { + const curSameProduct = attachParamsToCurCusProduct({ attachParams }); + if (curSameProduct?.free_trial) { + attachParams.freeTrial = curSameProduct.free_trial; + } + return AttachFunction.UpdatePrepaidQuantity; + } - if (branch == AttachBranch.Renew) { - return AttachFunction.Renew; - } + if (branch == AttachBranch.Renew) { + return AttachFunction.Renew; + } - return AttachFunction.AddProduct; + return AttachFunction.AddProduct; }; export const runAttachFunction = async ({ - req, - res, - branch, - attachParams, - attachBody, - config, + req, + res, + branch, + attachParams, + attachBody, + config, }: { - req: any; - res: any; - branch: AttachBranch; - attachParams: AttachParams; - attachBody: AttachBody; - config: AttachConfig; + req: any; + res: any; + branch: AttachBranch; + attachParams: AttachParams; + attachBody: AttachBody; + config: AttachConfig; }) => { - const { logtail: logger, db } = req; - const { stripeCli } = attachParams; + const { logtail: logger, db } = req; + const { stripeCli } = attachParams; - const attachFunction = await getAttachFunction({ - branch, - attachParams, - attachBody, - config, - }); + const attachFunction = await getAttachFunction({ + branch, + attachParams, + attachBody, + config, + }); - // console.log("Attach Function:", attachFunction); - // throw new Error("Attach Function:"); + // console.log("Attach Function:", attachFunction); + // throw new Error("Attach Function:"); - const customer = attachParams.customer; - const org = attachParams.org; + const customer = attachParams.customer; + const org = attachParams.org; - const productIdsStr = attachParams.products.map((p) => p.id).join(", "); - const { curMainProduct, curSameProduct, curScheduledProduct } = - attachParamToCusProducts({ - attachParams, - }); + const productIdsStr = attachParams.products.map((p) => p.id).join(", "); + const { curMainProduct, curSameProduct, curScheduledProduct } = + attachParamToCusProducts({ + attachParams, + }); - const curCusProduct = attachParamsToCurCusProduct({ attachParams }); + const curCusProduct = attachParamsToCurCusProduct({ attachParams }); - logger.info(`--------------------------------`); - logger.info( - `ATTACHING ${productIdsStr} to ${customer.name} (${customer.id || customer.email}), org: ${org.slug}\n` - ); - if (customer.entity) { - logger.info(`Entity: ${customer.entity.name} (${customer.entity.id})`); - } - logger.info( - `Branch: ${chalk.yellow(branch)}, Function: ${chalk.yellow(attachFunction)}` - ); + logger.info(`--------------------------------`); + logger.info( + `ATTACHING ${productIdsStr} to ${customer.name} (${customer.id || customer.email}), org: ${org.slug}\n`, + ); + if (customer.entity) { + logger.info(`Entity: ${customer.entity.name} (${customer.entity.id})`); + } + logger.info( + `Branch: ${chalk.yellow(branch)}, Function: ${chalk.yellow(attachFunction)}`, + ); - if (curMainProduct) { - logger.info(`→ Current Main Product: ${curMainProduct.product.id}`); - } - if (curSameProduct) { - logger.info(`→ Current Same Product: ${curSameProduct.product.id}`); - } - if (curScheduledProduct) { - logger.info( - `→ Current Scheduled Product: ${curScheduledProduct.product.id}` - ); - } + if (curMainProduct) { + logger.info(`→ Current Main Product: ${curMainProduct.product.id}`); + } + if (curSameProduct) { + logger.info(`→ Current Same Product: ${curSameProduct.product.id}`); + } + if (curScheduledProduct) { + logger.info( + `→ Current Scheduled Product: ${curScheduledProduct.product.id}`, + ); + } - if (attachFunction == AttachFunction.OneOff) { - return await handleOneOffFunction({ - req, - res, - attachParams, - config, - }); - } + if (attachFunction === AttachFunction.OneOff) { + return await handleOneOffFunction({ + req, + res, + attachParams, + config, + }); + } - if (attachFunction == AttachFunction.Renew) { - return await handleRenewProduct({ - req, - res, - attachParams, - config, - }); - } + if (attachFunction === AttachFunction.Renew) { + return await handleRenewProduct({ + req, + res, + attachParams, + config, + }); + } - // 2. If main is trial, cancel it... - if (branch == AttachBranch.MainIsTrial) { - await CusProductService.update({ - db, - cusProductId: curMainProduct!.id, - updates: { - ended_at: attachParams.now, - canceled: true, - status: CusProductStatus.Expired, - }, - }); + // 2. If main is trial, cancel it... + if (branch === AttachBranch.MainIsTrial) { + await CusProductService.update({ + db, + cusProductId: curMainProduct!.id, + updates: { + ended_at: attachParams.now, + canceled: true, + status: CusProductStatus.Expired, + }, + }); - const subId = curMainProduct?.subscription_ids?.[0]; - if (subId) { - await stripeCli.subscriptions.cancel(subId, { - cancellation_details: { - comment: "autumn_downgrade,trial_canceled", - }, - }); - } - } + const subId = curMainProduct?.subscription_ids?.[0]; + if (subId) { + await stripeCli.subscriptions.cancel(subId, { + cancellation_details: { + comment: "autumn_downgrade,trial_canceled", + }, + }); + } + } - if (attachFunction == AttachFunction.MultiAttach) { - return await handleMultiAttachFlow({ - req, - res, - attachParams, - attachBody, - branch, - config, - }); - } + if (attachFunction === AttachFunction.MultiAttach) { + return await handleMultiAttachFlow({ + req, + res, + attachParams, + attachBody, + branch, + config, + }); + } - if (attachFunction == AttachFunction.CreateCheckout) { - if (config.invoiceCheckout) { - return await handleCreateInvoiceCheckout({ - req, - res, - attachParams, - attachBody, - config, - branch, - }); - } - return await handleCreateCheckout({ - req, - res, - attachParams, - config, - }); - } + if (attachFunction === AttachFunction.CreateCheckout) { + if (config.invoiceCheckout) { + return await handleCreateInvoiceCheckout({ + req, + res, + attachParams, + attachBody, + config, + branch, + }); + } + return await handleCreateCheckout({ + req, + res, + attachParams, + config, + }); + } - if (attachFunction == AttachFunction.AddProduct) { - return await handleAddProduct({ - req, - res, - attachParams, - config, - branch, - }); - } + if (attachFunction === AttachFunction.AddProduct) { + return await handleAddProduct({ + req, + res, + attachParams, + config, + branch, + }); + } - if (attachFunction == AttachFunction.ScheduleProduct) { - return await handleScheduleFunction2({ - req, - res, - attachParams, - config, - }); - } + if (attachFunction === AttachFunction.ScheduleProduct) { + return await handleScheduleFunction2({ + req, + res, + attachParams, + config, + }); + } - if ( - attachFunction == AttachFunction.UpgradeDiffInterval || - attachFunction == AttachFunction.UpgradeSameInterval - ) { - return await handleUpgradeFlow({ - req, - res, - attachParams, - config, - branch, - }); - } + if ( + attachFunction === AttachFunction.UpgradeDiffInterval || + attachFunction === AttachFunction.UpgradeSameInterval + ) { + return await handleUpgradeFlow({ + req, + res, + attachParams, + config, + branch, + }); + } - if (attachFunction == AttachFunction.UpdatePrepaidQuantity) { - return await handleUpdateQuantityFunction({ - req, - res, - attachParams, - config, - }); - } + if (attachFunction === AttachFunction.UpdatePrepaidQuantity) { + return await handleUpdateQuantityFunction({ + req, + res, + attachParams, + config, + }); + } }; diff --git a/server/src/internal/customers/attach/checkout/handleCheckout.ts b/server/src/internal/customers/attach/checkout/handleCheckout.ts index c5a5c0243..410fd1764 100644 --- a/server/src/internal/customers/attach/checkout/handleCheckout.ts +++ b/server/src/internal/customers/attach/checkout/handleCheckout.ts @@ -184,8 +184,6 @@ export const handleCheckout = (req: any, res: any) => if (checkoutRes.next_cycle) { const nextCycle = checkoutRes.next_cycle; - console.log("Due at:", formatUnixToDate(nextCycle.starts_at!)); - console.log("Total:", nextCycle.total); } res.status(200).json({ diff --git a/server/src/internal/customers/attach/handleAttachPreview/handleAttachPreview.ts b/server/src/internal/customers/attach/handleAttachPreview/handleAttachPreview.ts index 777c4ad69..01285787b 100644 --- a/server/src/internal/customers/attach/handleAttachPreview/handleAttachPreview.ts +++ b/server/src/internal/customers/attach/handleAttachPreview/handleAttachPreview.ts @@ -30,9 +30,6 @@ export const handleAttachPreview = (req: any, res: any) => logger, }); - // console.log("Branch:", attachPreview.branch); - // console.log("Func:", attachPreview.func); - res.status(200).json(attachPreview); return; diff --git a/server/src/internal/customers/cancel/handleCancelProduct.ts b/server/src/internal/customers/cancel/handleCancelProduct.ts index 07154048c..39d9673b5 100644 --- a/server/src/internal/customers/cancel/handleCancelProduct.ts +++ b/server/src/internal/customers/cancel/handleCancelProduct.ts @@ -4,7 +4,6 @@ import { ErrCode, FullCusProduct, FullCustomer, - APIVersion, ProrationBehavior, AttachBranch, Price, @@ -16,9 +15,9 @@ import { createStripeCli } from "@/external/stripe/utils.js"; import RecaseError from "@/utils/errorUtils.js"; import { StatusCodes } from "http-status-codes"; import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js"; -import { cancelEndOfCycle } from "./cancelEndOfCycle.js"; + import { cancelImmediately } from "./cancelImmediately.js"; -import { cancelScheduledProduct } from "./cancelScheduledProduct.js"; + import { handleRenewProduct } from "../attach/attachFunctions/handleRenewProduct.js"; import { getDefaultAttachConfig } from "../attach/attachUtils/getAttachConfig.js"; import { handleScheduleFunction2 } from "../attach/attachFunctions/scheduleFlow/handleScheduleFlow2.js"; diff --git a/server/src/internal/customers/cusCache/subCacheUtils.ts b/server/src/internal/customers/cusCache/subCacheUtils.ts new file mode 100644 index 000000000..918c4fcda --- /dev/null +++ b/server/src/internal/customers/cusCache/subCacheUtils.ts @@ -0,0 +1,21 @@ +import { initUpstash } from "./upstashUtils.js"; + +export const addSubIdToCache = async ({ + subId, + scenario, +}: { + subId: string; + scenario: string; +}) => { + const upstash = await initUpstash(); + if (!upstash) return; + + await upstash.set(`sub:${subId}`, scenario, { + ex: 180, // 3 minutes + }); +}; +export const getSubScenarioFromCache = async ({ subId }: { subId: string }) => { + const upstash = await initUpstash(); + if (!upstash) return null; + return (await upstash.get(`sub:${subId}`)) as string | null; +}; diff --git a/server/src/internal/customers/cusProducts/AttachParams.ts b/server/src/internal/customers/cusProducts/AttachParams.ts index 104480130..375c06533 100644 --- a/server/src/internal/customers/cusProducts/AttachParams.ts +++ b/server/src/internal/customers/cusProducts/AttachParams.ts @@ -1,22 +1,23 @@ -import { +import type { + APIVersion, + AttachReplaceable, + AttachScenario, Customer, EntitlementWithFeature, Entity, - FeatureOptions, Feature, + FeatureOptions, + FreeProductConfig, FreeTrial, FullCusProduct, + FullCustomer, FullProduct, Organization, Price, - AttachScenario, - APIVersion, - FullCustomer, - AttachReplaceable, - Reward, ProductOptions, + Reward, } from "@autumn/shared"; -import Stripe from "stripe"; +import type Stripe from "stripe"; import { z } from "zod"; @@ -39,6 +40,7 @@ export type AttachParams = { entitlements: EntitlementWithFeature[]; freeTrial: FreeTrial | null; + rewardTrial?: FreeProductConfig | null; optionsList: FeatureOptions[]; productsList?: ProductOptions[]; diff --git a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils.ts b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils.ts index bcf0ed9ff..ffb460abc 100644 --- a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils.ts +++ b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils.ts @@ -119,132 +119,6 @@ export const getCusEntBalance = ({ }; }; -// export const sortCusEntsForDeduction = ( -// cusEnts: (FullCustomerEntitlement & { -// customer_product?: FullCusProduct; -// })[], -// reverseOrder: boolean = false -// ) => { -// let intervalOrder: Record = { -// [EntInterval.Minute]: 0, // 1 minute -// [EntInterval.Hour]: 1, // 1 hour -// [EntInterval.Day]: 2, // 1 day -// [EntInterval.Week]: 3, // 1 week -// [EntInterval.Month]: 4, // 1 month -// [EntInterval.Quarter]: 5, // 3 months -// [EntInterval.Year]: 6, // 1 year -// [EntInterval.SemiAnnual]: 7, // 6 months -// [EntInterval.Lifetime]: 8, // 1 time -// }; - -// // console.log( -// // `Cus ents before (${reverseOrder ? "reversed" : "normal"})`, -// // cusEnts.map( -// // (ce) => `${ce.entitlement.feature_id} - ${ce.entitlement.interval}` -// // ) -// // ); -// cusEnts.sort((a, b) => { -// const aEnt = a.entitlement; -// const bEnt = b.entitlement; - -// // 1. If boolean, go first -// if (aEnt.feature.type == FeatureType.Boolean) { -// return -1; -// } - -// if (bEnt.feature.type == FeatureType.Boolean) { -// return 1; -// } - -// // 1. If a is credit system and b is not, a should go last -// if ( -// aEnt.feature.type == FeatureType.CreditSystem && -// bEnt.feature.type != FeatureType.CreditSystem -// ) { -// return 1; -// } - -// // 2. If a is not credit system and b is, a should go first -// if ( -// aEnt.feature.type != FeatureType.CreditSystem && -// bEnt.feature.type == FeatureType.CreditSystem -// ) { -// return -1; -// } - -// // 2. Sort by unlimited (unlimited goes first) -// if ( -// aEnt.allowance_type == AllowanceType.Unlimited && -// bEnt.allowance_type != AllowanceType.Unlimited -// ) { -// return -1; -// } - -// if ( -// aEnt.allowance_type != AllowanceType.Unlimited && -// bEnt.allowance_type == AllowanceType.Unlimited -// ) { -// return 1; -// } - -// // If one has usage_allowed, it should go last -// if (!a.usage_allowed && b.usage_allowed) { -// return -1; -// } - -// if (!b.usage_allowed && a.usage_allowed) { -// return 1; -// } - -// // If one has a next_reset_at, it should go first -// let nextResetFirst = reverseOrder ? 1 : -1; - -// if (a.next_reset_at && !b.next_reset_at) { -// return nextResetFirst; -// } - -// // If b has a next_reset_at, it should go first -// if (!a.next_reset_at && b.next_reset_at) { -// return -nextResetFirst; -// } - -// // 3. Sort by interval -// let aVal = entIntervalToValue(aEnt.interval, aEnt.interval_count); -// let bVal = entIntervalToValue(bEnt.interval, bEnt.interval_count); -// if (aEnt.interval && bEnt.interval && !aVal.eq(bVal)) { -// if (reverseOrder) { -// return bVal.sub(aVal).toNumber(); -// // return intervalOrder[bEnt.interval] - intervalOrder[aEnt.interval]; -// } else { -// return aVal.sub(bVal).toNumber(); -// // return intervalOrder[aEnt.interval] - intervalOrder[bEnt.interval]; -// } -// } - -// // Check if a is main product -// let aIsAddOn = a.customer_product?.product?.is_add_on; -// let bIsAddOn = b.customer_product?.product?.is_add_on; - -// if (aIsAddOn && !bIsAddOn) { -// return 1; -// } - -// if (!aIsAddOn && bIsAddOn) { -// return -1; -// } - -// // 4. Sort by created_at -// return a.created_at - b.created_at; -// }); - -// // console.log( -// // `Cus ents after (${reverseOrder ? "reversed" : "normal"})`, -// // cusEnts.map( -// // (ce) => `${ce.entitlement.feature_id} - ${ce.entitlement.interval}` -// // ) -// // ); -// }; - // Get related cusPrice export const getRelatedCusPrice = ( cusEnt: FullCustomerEntitlement, diff --git a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/getExistingUsage.ts b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/getExistingUsage.ts index 975f0f3c8..8ae6319e3 100644 --- a/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/getExistingUsage.ts +++ b/server/src/internal/customers/cusProducts/cusEnts/cusEntUtils/getExistingUsage.ts @@ -221,7 +221,6 @@ export const addExistingUsagesToCusEnts = ({ for (const cusEnt of fullCusEnts) { let ent = cusEnt.entitlement; - // let cusEntKey = `${ent.feature_id}-${ent.interval}-${ent.interval_count || 1}`; let fromEntities = existingUsages[key].fromEntities; // if (cusEntKey !== key) continue; diff --git a/server/src/internal/customers/cusProducts/cusEnts/entBalanceUtils.ts b/server/src/internal/customers/cusProducts/cusEnts/entBalanceUtils.ts index bdab7db6d..a18fb668e 100644 --- a/server/src/internal/customers/cusProducts/cusEnts/entBalanceUtils.ts +++ b/server/src/internal/customers/cusProducts/cusEnts/entBalanceUtils.ts @@ -1,7 +1,6 @@ import RecaseError from "@/utils/errorUtils.js"; import { nullish } from "@/utils/genUtils.js"; -import { ErrCode, FullCustomerEntitlement } from "@autumn/shared"; -import { StatusCodes } from "http-status-codes"; +import { FullCustomerEntitlement } from "@autumn/shared"; export const getEntityBalance = ({ cusEnt, @@ -13,14 +12,7 @@ export const getEntityBalance = ({ let entityBalance = cusEnt.entities?.[entityId!]?.balance; let adjustment = cusEnt.entities?.[entityId!]?.adjustment || 0; - if (nullish(entityBalance)) { - return { balance: 0, adjustment: 0 }; - // throw new RecaseError({ - // message: `Entity balance not found for entityId: ${entityId}`, - // code: ErrCode.EntityBalanceNotFound, - // statusCode: StatusCodes.BAD_REQUEST, - // }); - } + if (nullish(entityBalance)) return { balance: 0, adjustment: 0 }; return { balance: entityBalance, @@ -45,11 +37,11 @@ export const getSummedEntityBalances = ({ return { balance: Object.values(cusEnt.entities!).reduce( (acc, curr) => acc + curr.balance, - 0, + 0 ), adjustment: Object.values(cusEnt.entities!).reduce( (acc, curr) => acc + curr.adjustment, - 0, + 0 ), unused: 0, count: Object.values(cusEnt.entities!).length, diff --git a/server/src/internal/customers/cusUtils/cusFeatureResponseUtils/getCusBalances.ts b/server/src/internal/customers/cusUtils/cusFeatureResponseUtils/getCusBalances.ts index dc123ec3f..e8acd526d 100644 --- a/server/src/internal/customers/cusUtils/cusFeatureResponseUtils/getCusBalances.ts +++ b/server/src/internal/customers/cusUtils/cusFeatureResponseUtils/getCusBalances.ts @@ -15,11 +15,11 @@ import { notNullish, notNullOrUndefined } from "@/utils/genUtils.js"; import { BREAK_API_VERSION } from "@/utils/constants.js"; import { - getCusEntBalance, getRelatedCusPrice, getResetBalance, getUnlimitedAndUsageAllowed, } from "../../cusProducts/cusEnts/cusEntUtils.js"; +import { getCusEntBalance } from "@autumn/shared"; export interface CusFeatureBalance { feature_id: string; diff --git a/server/src/internal/customers/cusUtils/cusProductResponseUtils/getCusProductResponse.ts b/server/src/internal/customers/cusUtils/cusProductResponseUtils/getCusProductResponse.ts index e055b3430..184aeb7e5 100644 --- a/server/src/internal/customers/cusUtils/cusProductResponseUtils/getCusProductResponse.ts +++ b/server/src/internal/customers/cusUtils/cusProductResponseUtils/getCusProductResponse.ts @@ -41,16 +41,16 @@ export const getCusProductResponse = async ({ cusProduct, subs, org, - entities = [], apiVersion, features, + entity, }: { cusProduct: FullCusProduct; org: Organization; subs?: Subscription[]; - entities?: Entity[]; apiVersion: number; features: Feature[]; + entity?: Entity; }) => { // Process prices @@ -184,11 +184,12 @@ export const getCusProductResponse = async ({ // stripe_subscription_ids: cusProduct.subscription_ids || [], started_at: cusProduct.starts_at, - entity_id: cusProduct.internal_entity_id - ? entities?.find( - (e: Entity) => e.internal_id == cusProduct.internal_entity_id - )?.id - : cusProduct.entity_id || undefined, + entity_id: entity?.id || cusProduct.entity_id || undefined, + // entity_id: cusProduct.internal_entity_id + // ? entities?.find( + // (e: Entity) => e.internal_id == cusProduct.internal_entity_id + // )?.id + // : cusProduct.entity_id || undefined, ...stripeSubData, items: v2Product.items, diff --git a/server/src/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.ts b/server/src/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.ts index 4a17db42f..99c062b5c 100644 --- a/server/src/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.ts +++ b/server/src/internal/customers/cusUtils/cusProductResponseUtils/processFullCusProducts.ts @@ -48,14 +48,14 @@ export const processFullCusProducts = async ({ fullCusProducts, subs, org, - entities = [], + entity, apiVersion, features, }: { fullCusProducts: any; subs: any; org: Organization; - entities?: Entity[]; + entity?: Entity; apiVersion: number; features: Feature[]; }) => { @@ -67,7 +67,7 @@ export const processFullCusProducts = async ({ cusProduct, subs, org, - entities, + entity, apiVersion, features, }); diff --git a/server/src/internal/customers/cusUtils/cusUtils.ts b/server/src/internal/customers/cusUtils/cusUtils.ts index 84e13c48f..fea88176f 100644 --- a/server/src/internal/customers/cusUtils/cusUtils.ts +++ b/server/src/internal/customers/cusUtils/cusUtils.ts @@ -132,7 +132,7 @@ export const getCusEntsInFeatures = async ({ if (internalFeatureIds) { cusEnts = cusEnts.filter((cusEnt) => - internalFeatureIds.includes(cusEnt.internal_feature_id) + internalFeatureIds.includes(cusEnt.entitlement.internal_feature_id) ); } @@ -142,7 +142,6 @@ export const getCusEntsInFeatures = async ({ (cusEnt) => nullish(cusEnt.customer_product.internal_entity_id) || cusEnt.customer_product.internal_entity_id === entity.internal_id - // || cusEnt.entities ); } diff --git a/server/src/internal/customers/cusUtils/getCustomerDetails.ts b/server/src/internal/customers/cusUtils/getCustomerDetails.ts index cf74533ea..e99339f11 100644 --- a/server/src/internal/customers/cusUtils/getCustomerDetails.ts +++ b/server/src/internal/customers/cusUtils/getCustomerDetails.ts @@ -82,7 +82,6 @@ export const getCustomerDetails = async ({ subs, org, apiVersion, - entities: customer.entities, features, }); diff --git a/server/src/internal/customers/getFullCusQuery.ts b/server/src/internal/customers/getFullCusQuery.ts index 472faa6d2..318872e63 100644 --- a/server/src/internal/customers/getFullCusQuery.ts +++ b/server/src/internal/customers/getFullCusQuery.ts @@ -175,6 +175,7 @@ const buildInvoicesCTE = (hasEntityCTE: boolean) => { ? sql`AND ( NOT EXISTS (SELECT 1 FROM entity_record) OR i.internal_entity_id = (SELECT internal_id FROM entity_record LIMIT 1) + OR i.internal_entity_id IS NULL )` : sql``; diff --git a/server/src/internal/customers/handlers/handleCreateBillingPortal.ts b/server/src/internal/customers/handlers/handleCreateBillingPortal.ts index eccf88d20..160402a31 100644 --- a/server/src/internal/customers/handlers/handleCreateBillingPortal.ts +++ b/server/src/internal/customers/handlers/handleCreateBillingPortal.ts @@ -7,6 +7,40 @@ import RecaseError, { handleRequestError } from "@/utils/errorUtils.js"; import { routeHandler } from "@/utils/routerUtils.js"; import { ErrCode } from "@autumn/shared"; import { StatusCodes } from "http-status-codes"; +import Stripe from "stripe"; + +const createDefaultBillingPortalConfiguration = async (stripeCli: Stripe) => { + try { + const configuration = await stripeCli.billingPortal.configurations.create({ + features: { + customer_update: { + allowed_updates: ["email", "address"], + enabled: true, + }, + invoice_history: { + enabled: true, + }, + payment_method_update: { + enabled: true, + }, + subscription_cancel: { + enabled: true, + }, + }, + // business_profile: { + // privacy_policy_url: "https://example.com/privacy", + // terms_of_service_url: "https://example.com/terms", + // }, + }); + return configuration; + } catch (error: any) { + throw new RecaseError({ + message: `Failed to create billing portal configuration: ${error.message}`, + code: ErrCode.StripeError, + statusCode: StatusCodes.INTERNAL_SERVER_ERROR, + }); + } +}; export const handleCreateBillingPortal = async (req: any, res: any) => routeHandler({ @@ -37,23 +71,19 @@ export const handleCreateBillingPortal = async (req: any, res: any) => const stripeCli = createStripeCli({ org, env: req.env }); + // Determine the Stripe customer ID to use + let stripeCustomerId: string; + if (!customer.processor?.id) { - let newCus; try { - newCus = await createStripeCusIfNotExists({ + const newCus = await createStripeCusIfNotExists({ db: req.db, org, env: req.env, customer, logger: req.logtail, }); - } catch (error: any) { - throw new RecaseError({ - message: `Failed to create Stripe customer`, - code: ErrCode.StripeError, - statusCode: StatusCodes.INTERNAL_SERVER_ERROR, - }); - } finally { + if (!newCus) { throw new RecaseError({ message: `Failed to create Stripe customer`, @@ -62,26 +92,78 @@ export const handleCreateBillingPortal = async (req: any, res: any) => }); } - const portal = await stripeCli.billingPortal.sessions.create({ - customer: newCus.id, - return_url: returnUrl || toSuccessUrl({ org, env: req.env }), - }); - - res.status(200).json({ - customer_id: customer.id, - url: portal.url, + stripeCustomerId = newCus.id; + } catch (error: any) { + throw new RecaseError({ + message: `Failed to create Stripe customer`, + code: ErrCode.StripeError, + statusCode: StatusCodes.INTERNAL_SERVER_ERROR, }); } } else { - const portal = await stripeCli.billingPortal.sessions.create({ - customer: customer.processor.id, + stripeCustomerId = customer.processor.id; + } + + // Create billing portal session + let portal; + try { + portal = await stripeCli.billingPortal.sessions.create({ + customer: stripeCustomerId, return_url: returnUrl || toSuccessUrl({ org, env: req.env }), }); + } catch (error: any) { + console.log(`Code: ${error.code}, Message: ${error.message}`); - res.status(200).json({ - customer_id: customer.id, - url: portal.url, - }); + // Check if the error is due to missing default configuration + if ( + error.message && + error.message.includes("default configuration has not been created") + ) { + try { + // Create a default billing portal configuration + req.logtail?.info( + `Creating default billing portal configuration for customer ${customer.id}` + ); + + const configuration = + await createDefaultBillingPortalConfiguration(stripeCli); + + req.logtail?.info( + "Successfully created billing portal configuration", + { + configurationId: configuration.id, + orgId: org.id, + } + ); + + // Retry creating the portal session with the new configuration + portal = await stripeCli.billingPortal.sessions.create({ + customer: stripeCustomerId, + return_url: returnUrl || toSuccessUrl({ org, env: req.env }), + configuration: configuration.id, + }); + } catch (configError: any) { + req.logtail?.error( + "Failed to create billing portal configuration", + { + error: configError.message, + orgId: org.id, + } + ); + throw new RecaseError({ + message: `Failed to create billing portal configuration: ${configError.message}`, + code: ErrCode.StripeError, + statusCode: StatusCodes.INTERNAL_SERVER_ERROR, + }); + } + } else { + throw error; + } } + + res.status(200).json({ + customer_id: customer.id, + url: portal.url, + }); }, }); diff --git a/server/src/internal/customers/handlers/handleCreateCustomer.ts b/server/src/internal/customers/handlers/handleCreateCustomer.ts index d8c313361..568cd3666 100644 --- a/server/src/internal/customers/handlers/handleCreateCustomer.ts +++ b/server/src/internal/customers/handlers/handleCreateCustomer.ts @@ -78,7 +78,6 @@ const handleIdIsNull = async ({ } // 2. Check if email already exists - let existingCustomers = await CusService.getByEmail({ db, email: newCus.email, diff --git a/server/src/internal/customers/handlers/handleGetCustomer.ts b/server/src/internal/customers/handlers/handleGetCustomer.ts index 7a7716b42..9b5314c1c 100644 --- a/server/src/internal/customers/handlers/handleGetCustomer.ts +++ b/server/src/internal/customers/handlers/handleGetCustomer.ts @@ -1,11 +1,5 @@ -import { CusService } from "@/internal/customers/CusService.js"; import { routeHandler } from "@/utils/routerUtils.js"; -import { - APIVersion, - CusExpand, - CusProductStatus, - ErrCode, -} from "@autumn/shared"; +import { APIVersion, CusExpand, ErrCode } from "@autumn/shared"; import { StatusCodes } from "http-status-codes"; import { getCustomerDetails } from "../cusUtils/getCustomerDetails.js"; import { parseCusExpand } from "../cusUtils/cusUtils.js"; @@ -30,9 +24,7 @@ export const handleGetCustomer = async (req: any, res: any) => }); let getInvoices = apiVersion < APIVersion.v1_1; - if (getInvoices) { - expandArray.push(CusExpand.Invoices); - } + if (getInvoices) expandArray.push(CusExpand.Invoices); logger.info(`getting customer ${customerId} for org ${org.slug}`); const startTime = Date.now(); @@ -46,16 +38,6 @@ export const handleGetCustomer = async (req: any, res: any) => logger, }); - // const customer = await CusService.getFull({ - // db, - // idOrInternalId: customerId, - // orgId: org.id, - // env: env, - // withEntities: true, - // expand: expandArray, - // allowNotFound: true, - // withSubs: true, - // }); logger.info(`get customer took ${Date.now() - startTime}ms`); if (!customer) { diff --git a/server/src/internal/customers/handlers/handleTransferProduct.ts b/server/src/internal/customers/handlers/handleTransferProduct.ts index 343d46a57..47d8fdb45 100644 --- a/server/src/internal/customers/handlers/handleTransferProduct.ts +++ b/server/src/internal/customers/handlers/handleTransferProduct.ts @@ -3,12 +3,13 @@ import { z } from "zod"; import { ExtendedRequest, ExtendedResponse } from "@/utils/models/Request.js"; import { routeHandler } from "@/utils/routerUtils.js"; import { CusService } from "../CusService.js"; -import { ErrCode } from "@autumn/shared"; +import { AttachScenario, ErrCode } from "@autumn/shared"; import { CusProductService } from "../cusProducts/CusProductService.js"; import { nullish } from "@/utils/genUtils.js"; import { handleDecreaseAndTransfer } from "./handleTransferProduct/handleDecreaseAndTransfer.js"; import { ProductService } from "@/internal/products/ProductService.js"; import { deleteCusCache } from "../cusCache/updateCachedCus.js"; +import { addProductsUpdatedWebhookTask } from "@/internal/analytics/handlers/handleProductsUpdated.js"; const TransferProductSchema = z.object({ from_entity_id: z.string().nullish(), to_entity_id: z.string(), @@ -119,6 +120,21 @@ export const handleTransferProduct = async (req: any, res: any) => internal_entity_id: toEntity.internal_id, }, }); + + await addProductsUpdatedWebhookTask({ + req, + internalCustomerId: customer.internal_id, + org: req.org, + env: req.env, + customerId: customer.id || customer.internal_id, + scenario: AttachScenario.New, + cusProduct: { + ...cusProduct, + entity_id: toEntity.id, + internal_entity_id: toEntity.internal_id, + }, + logger: req.logger, + }); } await deleteCusCache({ diff --git a/server/src/internal/customers/handlers/handleTransferProduct/handleDecreaseAndTransfer.ts b/server/src/internal/customers/handlers/handleTransferProduct/handleDecreaseAndTransfer.ts index 664d8b2cc..bbf726be4 100644 --- a/server/src/internal/customers/handlers/handleTransferProduct/handleDecreaseAndTransfer.ts +++ b/server/src/internal/customers/handlers/handleTransferProduct/handleDecreaseAndTransfer.ts @@ -100,6 +100,6 @@ export const handleDecreaseAndTransfer = async ({ }, product ), - sendWebhook: false, + scenario: AttachScenario.New, }); }; diff --git a/server/src/internal/customers/handlers/handleUpdateBalances.ts b/server/src/internal/customers/handlers/handleUpdateBalances.ts index bef4248e4..b12b937f7 100644 --- a/server/src/internal/customers/handlers/handleUpdateBalances.ts +++ b/server/src/internal/customers/handlers/handleUpdateBalances.ts @@ -13,10 +13,8 @@ import { } from "@/trigger/updateBalanceTask.js"; import { OrgService } from "@/internal/orgs/OrgService.js"; -import { - getCusEntBalance, - getUnlimitedAndUsageAllowed, -} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { getUnlimitedAndUsageAllowed } from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { getCusEntBalance } from "@autumn/shared"; import { CusEntService } from "@/internal/customers/cusProducts/cusEnts/CusEntitlementService.js"; import { notNullish } from "@/utils/genUtils.js"; diff --git a/server/src/internal/customers/handlers/handleUpdateEntitlement.ts b/server/src/internal/customers/handlers/handleUpdateEntitlement.ts index 16e51791e..8a25358be 100644 --- a/server/src/internal/customers/handlers/handleUpdateEntitlement.ts +++ b/server/src/internal/customers/handlers/handleUpdateEntitlement.ts @@ -9,10 +9,7 @@ import { adjustAllowance } from "@/trigger/adjustAllowance.js"; import { CusPriceService } from "@/internal/customers/cusProducts/cusPrices/CusPriceService.js"; import { CusService } from "@/internal/customers/CusService.js"; import { OrgService } from "@/internal/orgs/OrgService.js"; -import { - getCusEntBalance, - getRelatedCusPrice, -} from "@/internal/customers/cusProducts/cusEnts/cusEntUtils.js"; +import { getCusEntBalance } from "@autumn/shared"; import { performDeductionOnCusEnt } from "@/trigger/updateBalanceTask.js"; import { ExtendedRequest } from "@/utils/models/Request.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; diff --git a/server/src/internal/features/FeatureService.ts b/server/src/internal/features/FeatureService.ts index 39e603a77..99cc587d3 100644 --- a/server/src/internal/features/FeatureService.ts +++ b/server/src/internal/features/FeatureService.ts @@ -123,7 +123,7 @@ export class FeatureService { env: updatedFeatures[0].env as AppEnv, }); - return updatedFeatures.length > 0 ? updatedFeatures[0] : null; + return updatedFeatures.length > 0 ? (updatedFeatures[0] as Feature) : null; } static async insert({ diff --git a/server/src/internal/features/featureRouter.ts b/server/src/internal/features/featureRouter.ts index 1566d60fc..b10794b44 100644 --- a/server/src/internal/features/featureRouter.ts +++ b/server/src/internal/features/featureRouter.ts @@ -44,6 +44,29 @@ featureRouter.get("", async (req: any, res: any) => }) ); +featureRouter.get("/:featureId", async (req: any, res: any) => + routeHandler({ + req, + res, + action: "Get feature", + handler: async () => { + const feature = req.features.find( + (f: Feature) => f.id == req.params.featureId + ); + + if (!feature) { + throw new RecaseError({ + message: `Feature with id ${req.params.featureId} not found`, + code: ErrCode.FeatureNotFound, + statusCode: 404, + }); + } + + res.status(200).json(toAPIFeature({ feature })); + }, + }) +); + featureRouter.post("", async (req: any, res: any) => routeHandler({ req, diff --git a/server/src/internal/features/handlers/handleUpdateFeature.ts b/server/src/internal/features/handlers/handleUpdateFeature.ts index e4ff03ece..77ca1e896 100644 --- a/server/src/internal/features/handlers/handleUpdateFeature.ts +++ b/server/src/internal/features/handlers/handleUpdateFeature.ts @@ -289,7 +289,13 @@ export const handleUpdateFeature = async ( }); if (res) { - res.status(200).json({ success: true, feature_id: featureId }); + res + .status(200) + .json( + updatedFeature + ? toAPIFeature({ feature: updatedFeature }) + : undefined + ); } return; } @@ -394,20 +400,10 @@ export const handleUpdateFeature = async ( }); } - if (res && fromApi) { - let newFeature = await FeatureService.get({ - db: req.db, - id: featureId, - orgId: req.orgId, - env: req.env, - }); - res.status(200).json(toAPIFeature({ feature: newFeature })); - } else { - res.status(200).json({ success: true, feature_id: featureId }); - } - - // if (res) { - // res.status(200).json({ success: true, feature_id: featureId }); - // } + res + .status(200) + .json( + updatedFeature ? toAPIFeature({ feature: updatedFeature }) : undefined + ); }, }); diff --git a/server/src/internal/invoices/invoiceMemoUtils.ts b/server/src/internal/invoices/invoiceMemoUtils.ts index 3745dcb08..111066cb4 100644 --- a/server/src/internal/invoices/invoiceMemoUtils.ts +++ b/server/src/internal/invoices/invoiceMemoUtils.ts @@ -1,130 +1,124 @@ import { - Organization, - Feature, - FullProduct, - EntitlementWithFeature, - AllowanceType, - getFeatureName, - FeatureType, - Price, + type EntitlementWithFeature, + type Feature, type FullProduct, type Organization, + type Price } from "@autumn/shared"; import { mapToProductItems } from "@/internal/products/productV2Utils.js"; -import { getProductItemDisplay } from "../products/productUtils/productResponseUtils/getProductItemDisplay.js"; -import { getEntRelatedPrice } from "../products/entitlements/entitlementUtils.js"; import { isFeatureItem } from "../products/product-items/productItemUtils/getItemType.js"; +import { getProductItemDisplay } from "../products/productUtils/productResponseUtils/getProductItemDisplay.js"; export const buildInvoiceMemo = async ({ - org, - product, - products, - features, + org, + product, + products, + features, }: { - org: Organization; - product?: FullProduct; - products?: FullProduct[]; - features: Feature[]; + org: Organization; + product?: FullProduct; + products?: FullProduct[]; + features: Feature[]; }): Promise => { - if (product) { - const items = mapToProductItems({ - prices: product.prices, - entitlements: product.entitlements, - features, - }); + if (product) { + const items = mapToProductItems({ + prices: product.prices, + entitlements: product.entitlements, + features, + }); - const itemsToDisplay = ["Included features:"]; - for (const item of items) { - if (!item.feature_id) continue; - const display = getProductItemDisplay({ - item, - features, - currency: org.default_currency, - }); - if (display?.primary_text) itemsToDisplay.push(display.primary_text); - } + const itemsToDisplay = ["Included features:"]; + for (const item of items) { + if (!item.feature_id) continue; + const display = getProductItemDisplay({ + item, + features, + currency: org.default_currency, + }); + if (display?.primary_text) itemsToDisplay.push(display.primary_text); + } - return itemsToDisplay.join("\n"); - } else if (products) { - const itemsToDisplay = ["Included features:"]; + return itemsToDisplay.join("\n"); + } else if (products) { + const itemsToDisplay = ["Included features:"]; - for (const p of products) { - const items = mapToProductItems({ - prices: p.prices, - entitlements: p.entitlements, - features, - }); + for (const p of products) { + const items = mapToProductItems({ + prices: p.prices, + entitlements: p.entitlements, + features, + }); - console.log( - "Items: %s", - items.map((i) => i.feature_id) - ); + console.log( + "Items: %s", + items.map((i) => i.feature_id), + ); - for (const item of items) { - if (!item.feature_id) continue; - const display = getProductItemDisplay({ - item, - features, - currency: org.default_currency, - }); - console.log( - "Display for item %s: %s", - item.feature_id, - display?.primary_text - ); - if (display?.primary_text) itemsToDisplay.push(display.primary_text); - } - } + for (const item of items) { + if (!item.feature_id) continue; + const display = getProductItemDisplay({ + item, + features, + currency: org.default_currency, + }); + console.log( + "Display for item %s: %s", + item.feature_id, + display?.primary_text, + ); + if (display?.primary_text) itemsToDisplay.push(display.primary_text); + } + } - console.log("Items to display: %s", itemsToDisplay); + console.log("Items to display: %s", itemsToDisplay); - let memo = itemsToDisplay.join("\n"); - if (memo.length > 490) { - memo = memo.slice(0, 490) + "..."; - } - return memo; - } else return ""; + let memo = itemsToDisplay.join("\n"); + if (memo.length > 490) { + memo = memo.slice(0, 490) + "..."; + } + return memo; + } else return ""; }; export const buildInvoiceMemoFromEntitlements = async ({ - org, - entitlements, - prices, - features, - logger, + org, + entitlements, + prices, + features, + logger, }: { - org: Organization; - entitlements: EntitlementWithFeature[]; - prices: Price[]; - features: Feature[]; - logger: any; + org: Organization; + entitlements: EntitlementWithFeature[]; + prices: Price[]; + features: Feature[]; + logger: any; }) => { - // Get item from price and ent - const items = mapToProductItems({ - entitlements, - prices, - features, - }); + // Get item from price and ent + const items = mapToProductItems({ + entitlements, + prices, + features, + }); - if (items.filter(isFeatureItem).length === 0) return ""; + if (items.filter(isFeatureItem).length === 0) return ""; - const itemsToDisplay = ["Included:"]; + const itemsToDisplay = ["Included:"]; - for (const item of items) { - if (!isFeatureItem(item)) continue; + for (const item of items) { + if (!isFeatureItem(item)) continue; - const display = getProductItemDisplay({ - item, - features, - currency: org.default_currency, - }); + const display = getProductItemDisplay({ + item, + features, + currency: org.default_currency, + }); - itemsToDisplay.push( - `- ${display?.primary_text}${display?.secondary_text ? ` ${display?.secondary_text}` : ""}` - ); - } + itemsToDisplay.push( + `- ${display?.primary_text}${display?.secondary_text ? ` ${display?.secondary_text}` : ""}`, + ); + } - let memo = itemsToDisplay.join("\n"); - if (memo.length > 490) { - memo = memo.slice(0, 490) + "..."; - } - return memo; + let memo = itemsToDisplay.join("\n"); + if (memo.length > 490) { + memo = `${memo.slice(0, 490)}...`; + } + return memo; }; diff --git a/server/src/internal/mainRouter.ts b/server/src/internal/mainRouter.ts index 506d7eaf8..5319c42cb 100644 --- a/server/src/internal/mainRouter.ts +++ b/server/src/internal/mainRouter.ts @@ -59,9 +59,7 @@ mainRouter.use( id: invoiceId, }); - if (!invoice) { - return res.status(404).json({ error: "Invoice not found" }); - } + if (!invoice) return res.status(404).json({ error: "Invoice not found" }); try { let org = invoice.customer.org; @@ -72,6 +70,12 @@ mainRouter.use( }); let stripeInvoice = await stripeCli.invoices.retrieve(invoice.stripe_id); + if (stripeInvoice.status == "draft") { + return res + .status(404) + .json({ error: "This invoice is in draft status and has no URL" }); + } + res.redirect(stripeInvoice.hosted_invoice_url); } catch (e) { console.error(e); diff --git a/server/src/internal/orgs/handlers/handleDeleteOrg.ts b/server/src/internal/orgs/handlers/handleDeleteOrg.ts index f3690d6c7..6726d68c0 100644 --- a/server/src/internal/orgs/handlers/handleDeleteOrg.ts +++ b/server/src/internal/orgs/handlers/handleDeleteOrg.ts @@ -44,22 +44,20 @@ const deleteStripeWebhooks = async ({ org: Organization; logger: any; }) => { - if (org.stripe_config) { - try { - await deleteStripeWebhook({ - org: org, - env: AppEnv.Sandbox, - }); + try { + await deleteStripeWebhook({ + org: org, + env: AppEnv.Sandbox, + }); - await deleteStripeWebhook({ - org: org, - env: AppEnv.Live, - }); - } catch (error: any) { - logger.error( - `Failed to delete stripe webhooks for ${org.id}, ${org.slug}. ${error.message})` - ); - } + await deleteStripeWebhook({ + org: org, + env: AppEnv.Live, + }); + } catch (error: any) { + logger.error( + `Failed to delete stripe webhooks for ${org.id}, ${org.slug}. ${error.message})` + ); } }; diff --git a/server/src/internal/orgs/orgUtils.ts b/server/src/internal/orgs/orgUtils.ts index d41b22292..96f23aa14 100644 --- a/server/src/internal/orgs/orgUtils.ts +++ b/server/src/internal/orgs/orgUtils.ts @@ -90,15 +90,18 @@ export const deleteStripeWebhook = async ({ org: Organization; env: AppEnv; }) => { + if (!isStripeConnected({ org, env })) return; + const stripeCli = createStripeCli({ org, env }); const webhookEndpoints = await stripeCli.webhookEndpoints.list({ limit: 100, }); for (const webhook of webhookEndpoints.data) { - if (webhook.url.includes(org.id)) { + if (webhook.url.includes(org.id) && webhook.url.includes(env)) { try { await stripeCli.webhookEndpoints.del(webhook.id); + console.log(`Deleted stripe webhook (${env}) ${webhook.url}`); } catch (error: any) { console.log(`Failed to delete stripe webhook (${env}) ${webhook.url}`); console.log(error.message); @@ -209,3 +212,30 @@ export const updateOrgConfig = async ({ await CacheManager.disconnect(); } }; + +export const unsetOrgStripeKeys = async ({ + org, + env, + db, +}: { + org: Organization; + env: AppEnv; + db: DrizzleCli; +}) => { + const newStripeConfig: any = structuredClone(org.stripe_config) || {}; + if (env === AppEnv.Sandbox) { + newStripeConfig.test_api_key = null; + newStripeConfig.test_webhook_secret = null; + } else { + newStripeConfig.live_api_key = null; + newStripeConfig.live_webhook_secret = null; + } + + await OrgService.update({ + db, + orgId: org.id, + updates: { + stripe_config: newStripeConfig, + }, + }); +}; diff --git a/server/src/internal/platform/platformRouter.ts b/server/src/internal/platform/platformRouter.ts index 9b2c26a04..a5ab46c04 100644 --- a/server/src/internal/platform/platformRouter.ts +++ b/server/src/internal/platform/platformRouter.ts @@ -12,15 +12,15 @@ import { import { ExtendedRequest } from "@/utils/models/Request.js"; import { routeHandler } from "@/utils/routerUtils.js"; -import { slugify } from "@/utils/genUtils.js"; + import { and, eq } from "drizzle-orm"; import { connectStripe } from "../orgs/handlers/handleConnectStripe.js"; import { z } from "zod"; import { createKey } from "../dev/api-keys/apiKeyUtils.js"; import { afterOrgCreated } from "@/utils/authUtils/afterOrgCreated.js"; import { Autumn } from "autumn-js"; -import { organizationSchema } from "better-auth/plugins"; -import { isStripeConnected, shouldReconnectStripe } from "../orgs/orgUtils.js"; + +import { shouldReconnectStripe } from "../orgs/orgUtils.js"; const platformRouter = Router(); @@ -215,7 +215,6 @@ platformRouter.post("/exchange", (req: any, res: any) => }); if (reconnectStripe) { - console.log("Reconnecting stripe sandbox"); let { test_api_key, test_webhook_secret, diff --git a/server/src/internal/products/free-trials/freeTrialUtils.ts b/server/src/internal/products/free-trials/freeTrialUtils.ts index eb4bf83dc..557f4791a 100644 --- a/server/src/internal/products/free-trials/freeTrialUtils.ts +++ b/server/src/internal/products/free-trials/freeTrialUtils.ts @@ -1,198 +1,214 @@ -import RecaseError from "@/utils/errorUtils.js"; -import { generateId, notNullish } from "@/utils/genUtils.js"; import { - CreateFreeTrial, + type CreateFreeTrial, CreateFreeTrialSchema, ErrCode, - FreeTrial, + type FreeProductConfig, + type FreeTrial, FreeTrialDuration, - Price, + type Price } from "@autumn/shared"; import { addDays, addMinutes, addMonths, addYears } from "date-fns"; -import { FreeTrialService } from "./FreeTrialService.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { CusProductService } from "@/internal/customers/cusProducts/CusProductService.js"; -import { isFreeProduct, isOneOff } from "../productUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import { generateId } from "@/utils/genUtils.js"; +import { isOneOff } from "../productUtils.js"; +import { FreeTrialService } from "./FreeTrialService.js"; export const validateOneOffTrial = async ({ - prices, - freeTrial, + prices, + freeTrial, }: { - prices: Price[]; - freeTrial: FreeTrial | null; + prices: Price[]; + freeTrial: FreeTrial | null; }) => { - if(isOneOff(prices) && freeTrial) { - throw new RecaseError({ - message: "One-off products cannot have a free trial", - code: ErrCode.InvalidRequest, - statusCode: 400, - }); - } -} + if (isOneOff(prices) && freeTrial) { + throw new RecaseError({ + message: "One-off products cannot have a free trial", + code: ErrCode.InvalidRequest, + statusCode: 400, + }); + } +}; export const validateAndInitFreeTrial = ({ - freeTrial, - internalProductId, - isCustom = false, + freeTrial, + internalProductId, + isCustom = false, }: { - freeTrial: CreateFreeTrial; - internalProductId: string; - isCustom?: boolean; + freeTrial: CreateFreeTrial; + internalProductId: string; + isCustom?: boolean; }): FreeTrial => { - const freeTrialSchema = CreateFreeTrialSchema.parse(freeTrial); + const freeTrialSchema = CreateFreeTrialSchema.parse(freeTrial); - return { - ...freeTrialSchema, - id: generateId("ft"), - created_at: Date.now(), - duration: freeTrial.duration || FreeTrialDuration.Day, - internal_product_id: internalProductId, - is_custom: isCustom, - card_required: freeTrial.card_required ?? true, - }; + return { + ...freeTrialSchema, + id: generateId("ft"), + created_at: Date.now(), + duration: freeTrial.duration || FreeTrialDuration.Day, + internal_product_id: internalProductId, + is_custom: isCustom, + card_required: freeTrial.card_required ?? true, + }; }; export const freeTrialsAreSame = ({ - ft1, - ft2, + ft1, + ft2, }: { - ft1?: FreeTrial | CreateFreeTrial | null; - ft2?: FreeTrial | CreateFreeTrial | null; + ft1?: FreeTrial | CreateFreeTrial | null; + ft2?: FreeTrial | CreateFreeTrial | null; }) => { - if (!ft1 && !ft2) return true; - if (!ft1 || !ft2) return false; - return ( - ft1.length === ft2.length && - ft1.unique_fingerprint === ft2.unique_fingerprint && - ft1.duration === ft2.duration && - ft1.card_required === ft2.card_required - ); + if (!ft1 && !ft2) return true; + if (!ft1 || !ft2) return false; + return ( + ft1.length === ft2.length && + ft1.unique_fingerprint === ft2.unique_fingerprint && + ft1.duration === ft2.duration && + ft1.card_required === ft2.card_required + ); }; export const freeTrialToStripeTimestamp = ({ - freeTrial, - now, + freeTrial, + now, }: { - freeTrial: FreeTrial | null | undefined; - now?: number | undefined; + freeTrial: FreeTrial | null | undefined; + now?: number | undefined; }) => { - now = now || Date.now(); + now = now || Date.now(); - if (!freeTrial) return undefined; + if (!freeTrial) return undefined; - let duration = freeTrial.duration || FreeTrialDuration.Day; - let length = freeTrial.length; + const duration = freeTrial.duration || FreeTrialDuration.Day; + const length = freeTrial.length; - let trialEnd: Date; - if (duration === FreeTrialDuration.Day) { - trialEnd = addDays(new Date(now), length); - } else if (duration === FreeTrialDuration.Month) { - trialEnd = addMonths(new Date(now), length); - } else if (duration === FreeTrialDuration.Year) { - trialEnd = addYears(new Date(now), length); - } else { - throw new RecaseError({ - message: `Invalid free trial duration: ${duration}`, - code: "invalid_free_trial_duration", - statusCode: 400, - }); - } + let trialEnd: Date; + if (duration === FreeTrialDuration.Day) { + trialEnd = addDays(new Date(now), length); + } else if (duration === FreeTrialDuration.Month) { + trialEnd = addMonths(new Date(now), length); + } else if (duration === FreeTrialDuration.Year) { + trialEnd = addYears(new Date(now), length); + } else { + throw new RecaseError({ + message: `Invalid free trial duration: ${duration}`, + code: "invalid_free_trial_duration", + statusCode: 400, + }); + } - // trialEnd = addMinutes(trialEnd, 5); - trialEnd = addMinutes(trialEnd, 10); + // trialEnd = addMinutes(trialEnd, 5); + trialEnd = addMinutes(trialEnd, 10); - return Math.ceil(trialEnd.getTime() / 1000); + return Math.ceil(trialEnd.getTime() / 1000); +}; + +export const rewardTrialToStripeTimestamp = ({ + rewardTrial, + now, +}: { + rewardTrial: FreeProductConfig | null | undefined; + now?: number | undefined; +}) => { + now = now || Date.now(); + if (!rewardTrial) return undefined; + const length = rewardTrial.duration_value; + const trialEnd: Date = addMonths(new Date(now), length); + + return Math.ceil(trialEnd.getTime() / 1000); }; export const getFreeTrialAfterFingerprint = async ({ - db, - freeTrial, - productId, - fingerprint, - internalCustomerId, - multipleAllowed, + db, + freeTrial, + productId, + fingerprint, + internalCustomerId, + multipleAllowed, }: { - db: DrizzleCli; - freeTrial: FreeTrial | null | undefined; - productId: string; - fingerprint: string | null | undefined; - internalCustomerId: string; - multipleAllowed: boolean; + db: DrizzleCli; + freeTrial: FreeTrial | null | undefined; + productId: string; + fingerprint: string | null | undefined; + internalCustomerId: string; + multipleAllowed: boolean; }): Promise => { - if (!freeTrial) return null; + if (!freeTrial) return null; - if (multipleAllowed) { - return freeTrial; - } + if (multipleAllowed) { + return freeTrial; + } - let uniqueFreeTrial: FreeTrial | null = freeTrial; + let uniqueFreeTrial: FreeTrial | null = freeTrial; - const data = await CusProductService.getByFingerprint({ - db, - productId, - internalCustomerId, - fingerprint: uniqueFreeTrial.unique_fingerprint ? fingerprint! : undefined, - }); + const data = await CusProductService.getByFingerprint({ + db, + productId, + internalCustomerId, + fingerprint: uniqueFreeTrial.unique_fingerprint ? fingerprint! : undefined, + }); - const exists = data && data.length > 0; + const exists = data && data.length > 0; - if (exists) { - console.log("Free trial fingerprint exists"); - uniqueFreeTrial = null; - } + if (exists) { + console.log("Free trial fingerprint exists"); + uniqueFreeTrial = null; + } - return uniqueFreeTrial; + return uniqueFreeTrial; }; export const handleNewFreeTrial = async ({ - db, - newFreeTrial, - curFreeTrial, - internalProductId, - isCustom = false, - product, + db, + newFreeTrial, + curFreeTrial, + internalProductId, + isCustom = false, + product, }: { - db: DrizzleCli; - newFreeTrial: CreateFreeTrial | null; - curFreeTrial: FreeTrial | null | undefined; - internalProductId: string; - isCustom: boolean; - product?: any; // Add product parameter for validation + db: DrizzleCli; + newFreeTrial: CreateFreeTrial | null; + curFreeTrial: FreeTrial | null | undefined; + internalProductId: string; + isCustom: boolean; + product?: any; // Add product parameter for validation }) => { - // If new free trial is null - if (!newFreeTrial) { - if (!isCustom && curFreeTrial) { - await FreeTrialService.delete({ - db, - id: curFreeTrial.id, - }); - } - return null; - } + // If new free trial is null + if (!newFreeTrial) { + if (!isCustom && curFreeTrial) { + await FreeTrialService.delete({ + db, + id: curFreeTrial.id, + }); + } + return null; + } - if (freeTrialsAreSame({ ft1: curFreeTrial, ft2: newFreeTrial })) { - return curFreeTrial; - } + if (freeTrialsAreSame({ ft1: curFreeTrial, ft2: newFreeTrial })) { + return curFreeTrial; + } - const createdFreeTrial = validateAndInitFreeTrial({ - freeTrial: newFreeTrial, - internalProductId, - isCustom, - }); + const createdFreeTrial = validateAndInitFreeTrial({ + freeTrial: newFreeTrial, + internalProductId, + isCustom, + }); - if (isCustom && newFreeTrial) { - await FreeTrialService.insert({ - db, - data: createdFreeTrial, - }); - } else if (!isCustom) { - createdFreeTrial.id = curFreeTrial?.id || createdFreeTrial.id; + if (isCustom && newFreeTrial) { + await FreeTrialService.insert({ + db, + data: createdFreeTrial, + }); + } else if (!isCustom) { + createdFreeTrial.id = curFreeTrial?.id || createdFreeTrial.id; - await FreeTrialService.upsert({ - db, - data: createdFreeTrial, - }); - } + await FreeTrialService.upsert({ + db, + data: createdFreeTrial, + }); + } - return createdFreeTrial; + return createdFreeTrial; }; diff --git a/server/src/internal/products/handlers/handleListProductsBeta.ts b/server/src/internal/products/handlers/handleListProductsBeta.ts index 84cf742c3..1cd0eeed9 100644 --- a/server/src/internal/products/handlers/handleListProductsBeta.ts +++ b/server/src/internal/products/handlers/handleListProductsBeta.ts @@ -44,11 +44,6 @@ export const handleListProductsBeta = async (req: any, res: any) => })(), ]); - console.log( - "Products:", - products.map((p) => `${p.internal_id} - ${p.id} - ${p.archived}`) - ); - if (req.query.v1_schema === "true") { res.status(200).json({ list: products, diff --git a/server/src/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.ts b/server/src/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.ts index f201f4a7b..9c25269eb 100644 --- a/server/src/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.ts +++ b/server/src/internal/products/prices/priceUtils/usagePriceUtils/classifyUsagePrice.ts @@ -8,7 +8,7 @@ import { Price, UsagePriceConfig, } from "@autumn/shared"; -import { getBillingType } from "../../priceUtils.js"; +import { formatPrice, getBillingType } from "../../priceUtils.js"; import { notNullish } from "@/utils/genUtils.js"; import Stripe from "stripe"; import { Decimal } from "decimal.js"; @@ -82,6 +82,7 @@ export const hasPrepaidPrice = ({ return prices.some((price) => { let isUsage = getBillingType(price.config) == BillingType.UsageInAdvance; let isOneOff = price.config.interval == BillingInterval.OneOff; + return isUsage && (excludeOneOff ? !isOneOff : true); }); }; diff --git a/server/src/internal/products/productUtils.ts b/server/src/internal/products/productUtils.ts index 92a896b0f..a87f6ea59 100644 --- a/server/src/internal/products/productUtils.ts +++ b/server/src/internal/products/productUtils.ts @@ -1,49 +1,46 @@ import { - AppEnv, + type AppEnv, BillingInterval, BillingType, - CreateProduct, - Entitlement, + type CreateProduct, + EntInterval, + type Entitlement, EntitlementSchema, ErrCode, - Feature, - FixedPriceConfig, + type Feature, + type FixedPriceConfig, + type FullProduct, intervalsSame, - Organization, - Price, + type Organization, + type Price, PriceSchema, PriceType, ProcessorType, - Product, - ProductOptions, + type Product, ProductSchema, - UsagePriceConfig, + type UsagePriceConfig, } from "@autumn/shared"; -import { FullProduct } from "@autumn/shared"; +import { Decimal } from "decimal.js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import { createStripePriceIFNotExist } from "@/external/stripe/createStripePrice/createStripePrice.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; import { getBillingInterval, getBillingType, } from "@/internal/products/prices/priceUtils.js"; -import { createStripeCli } from "@/external/stripe/utils.js"; -import { ProductService } from "./ProductService.js"; -import { +import RecaseError from "@/utils/errorUtils.js"; +import { generateId, notNullish } from "@/utils/genUtils.js"; +import type { AttachParams, InsertCusProductParams, } from "../customers/cusProducts/AttachParams.js"; -import { - getEntitlementsForProduct, - getEntsWithFeature, -} from "./entitlements/entitlementUtils.js"; -import { Decimal } from "decimal.js"; -import { generateId, notNullish } from "@/utils/genUtils.js"; -import { PriceService } from "./prices/PriceService.js"; -import { EntitlementService } from "./entitlements/EntitlementService.js"; -import RecaseError from "@/utils/errorUtils.js"; -import { createStripePriceIFNotExist } from "@/external/stripe/createStripePrice/createStripePrice.js"; -import { FreeTrialService } from "./free-trials/FreeTrialService.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { compareBillingIntervals } from "./prices/priceUtils/priceIntervalUtils.js"; import { isStripeConnected } from "../orgs/orgUtils.js"; +import { EntitlementService } from "./entitlements/EntitlementService.js"; +import { getEntitlementsForProduct } from "./entitlements/entitlementUtils.js"; +import { FreeTrialService } from "./free-trials/FreeTrialService.js"; +import { ProductService } from "./ProductService.js"; +import { PriceService } from "./prices/PriceService.js"; +import { compareBillingIntervals } from "./prices/priceUtils/priceIntervalUtils.js"; import { isDefaultTrialFullProduct } from "./productUtils/classifyProduct.js"; export const getLatestProducts = (products: FullProduct[]) => { @@ -86,7 +83,7 @@ export const constructProduct = ({ processor?: any; baseVariantId?: string | null; }) => { - let newProduct: Product = { + const newProduct: Product = { ...productData, org_id: orgId, env, @@ -129,8 +126,8 @@ export const isProductUpgrade = ({ return true; } - let billingInterval1 = getBillingInterval(prices1); // pro quarter - let billingInterval2 = getBillingInterval(prices2); // premium + const billingInterval1 = getBillingInterval(prices1); // pro quarter + const billingInterval2 = getBillingInterval(prices2); // premium // 2. Get total price for each product const getTotalPrice = (prices: Price[]) => { @@ -186,7 +183,7 @@ export const isFreeProduct = (prices: Price[]) => { export const getOptionsFromPrices = (prices: Price[], features: Feature[]) => { const featureToOptions: { [key: string]: any } = {}; for (const price of prices) { - if (price.config!.type == PriceType.Fixed) { + if (price.config!.type === PriceType.Fixed) { continue; } @@ -231,7 +228,7 @@ export const checkStripeProductExists = async ({ logger: any; }) => { let createNew = false; - let stripeCli = createStripeCli({ + const stripeCli = createStripeCli({ org, env, }); @@ -240,12 +237,14 @@ export const checkStripeProductExists = async ({ createNew = true; } else { try { - let stripeProduct = await stripeCli.products.retrieve( + const stripeProduct = await stripeCli.products.retrieve( product.processor!.id ); if (!stripeProduct.active) { - createNew = true; + await stripeCli.products.update(product.processor!.id, { + active: true, + }); } } catch (error) { createNew = true; @@ -289,7 +288,9 @@ export const attachToInsertParams = ( // Get entity let { internalEntityId, entityId: attachEntityId } = attachParams; if (notNullish(entityId)) { - let entity = attachParams.customer.entities.find((e) => e.id === entityId); + const entity = attachParams.customer.entities.find( + (e) => e.id === entityId + ); if (entity) { internalEntityId = entity.internal_id; @@ -341,7 +342,7 @@ export const copyProduct = async ({ org_id: toOrgId, env: toEnv, processor: null, - base_variant_id: fromEnv == toEnv ? null : product.base_variant_id, + base_variant_id: fromEnv === toEnv ? null : product.base_variant_id, }; const newEntitlements: Entitlement[] = []; @@ -349,12 +350,12 @@ export const copyProduct = async ({ for (const entitlement of product.entitlements) { // 1. Get from feature - let fromFeature = fromFeatures.find( + const fromFeature = fromFeatures.find( (f) => f.internal_id === entitlement.internal_feature_id ); // 2. Get to feature - let toFeature = toFeatures.find((f) => f.id === fromFeature?.id); + const toFeature = toFeatures.find((f) => f.id === fromFeature?.id); if (!toFeature) { throw new RecaseError({ @@ -364,7 +365,7 @@ export const copyProduct = async ({ }); } - let newId = generateId("ent"); + const newId = generateId("ent"); newEntitlements.push( EntitlementSchema.parse({ ...entitlement, @@ -380,12 +381,12 @@ export const copyProduct = async ({ newEntIds[entitlement.id!] = newId; } - let newPrices: Price[] = []; + const newPrices: Price[] = []; for (const price of product.prices) { // 1. Copy price - let newPrice = structuredClone(price); + const newPrice = structuredClone(price); - let config = newPrice.config as UsagePriceConfig; + const config = newPrice.config as UsagePriceConfig; // Clear Stripe IDs config.stripe_meter_id = undefined; @@ -394,11 +395,11 @@ export const copyProduct = async ({ config.stripe_price_id = undefined; if (config.type === PriceType.Usage) { - let fromFeature = fromFeatures.find( + const fromFeature = fromFeatures.find( (f) => f.internal_id === config.internal_feature_id ); - let toFeature = toFeatures.find((f) => f.id === fromFeature?.id); + const toFeature = toFeatures.find((f) => f.id === fromFeature?.id); if (!toFeature) { throw new RecaseError({ @@ -412,7 +413,7 @@ export const copyProduct = async ({ config.feature_id = toFeature.id; // Update entitlement id - let entitlementId = newEntIds[price.entitlement_id!]; + const entitlementId = newEntIds[price.entitlement_id!]; if (!entitlementId) { throw new RecaseError({ message: `Failed to swap entitlement id for price ${price.id}`, @@ -486,16 +487,25 @@ export const isOneOff = (prices: Price[]) => { prices.every((p) => p.config?.interval === BillingInterval.OneOff) && prices.some((p) => { if (p.config?.type === PriceType.Usage) { - let config = p.config as UsagePriceConfig; + const config = p.config as UsagePriceConfig; return config.usage_tiers.some((t) => t.amount > 0); } else { - let config = p.config as FixedPriceConfig; + const config = p.config as FixedPriceConfig; return config.amount > 0; } }) ); }; +export const itemsAreOneOff = (items: Entitlement[]) => { + return items.every( + (item) => + item.interval === null || + item.interval === undefined || + item.interval === EntInterval.Lifetime + ); +}; + export const initProductInStripe = async ({ db, org, diff --git a/server/src/internal/rewards/RewardRedemptionService.ts b/server/src/internal/rewards/RewardRedemptionService.ts index e3fcfc63e..24bc9b5f3 100644 --- a/server/src/internal/rewards/RewardRedemptionService.ts +++ b/server/src/internal/rewards/RewardRedemptionService.ts @@ -1,17 +1,16 @@ -import { DrizzleCli } from "@/db/initDrizzle.js"; -import RecaseError from "@/utils/errorUtils.js"; -import { notNullish } from "@/utils/genUtils.js"; import { customers, ErrCode, + type RewardRedemption, + type RewardTriggerEvent, referralCodes, rewardPrograms, - RewardRedemption, rewardRedemptions, rewards, - RewardTriggerEvent, } from "@autumn/shared"; -import { and, eq } from "drizzle-orm"; +import { and, eq, inArray, or } from "drizzle-orm"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import RecaseError from "@/utils/errorUtils.js"; export class RewardRedemptionService { static async getById({ db, id }: { db: DrizzleCli; id: string }) { @@ -55,10 +54,10 @@ export class RewardRedemptionService { internalRewardProgramId ? eq( rewardRedemptions.internal_reward_program_id, - internalRewardProgramId, + internalRewardProgramId ) : undefined, - triggered ? eq(rewardRedemptions.triggered, triggered) : undefined, + triggered ? eq(rewardRedemptions.triggered, triggered) : undefined ), with: { reward_program: { @@ -92,11 +91,11 @@ export class RewardRedemptionService { .from(rewardRedemptions) .innerJoin( referralCodes, - eq(rewardRedemptions.referral_code_id, referralCodes.id), + eq(rewardRedemptions.referral_code_id, referralCodes.id) ) .innerJoin( customers, - eq(rewardRedemptions.internal_customer_id, customers.internal_id), + eq(rewardRedemptions.internal_customer_id, customers.internal_id) ); if (withRewardProgram) { @@ -104,15 +103,15 @@ export class RewardRedemptionService { rewardPrograms, eq( rewardRedemptions.internal_reward_program_id, - rewardPrograms.internal_id, - ), + rewardPrograms.internal_id + ) ); } const data = await query .where(eq(referralCodes.internal_customer_id, internalCustomerId)) .limit(limit); - let processed = data.map((d) => ({ + const processed = data.map((d) => ({ ...d.reward_redemptions, referral_code: d.referral_codes, customer: d.customers, @@ -184,34 +183,65 @@ export class RewardRedemptionService { .from(rewardRedemptions) .innerJoin( referralCodes, - eq(rewardRedemptions.referral_code_id, referralCodes.id), + eq(rewardRedemptions.referral_code_id, referralCodes.id) + ) + .innerJoin( + customers, + eq(rewardRedemptions.internal_customer_id, customers.internal_id) ) .innerJoin( rewardPrograms, eq( rewardRedemptions.internal_reward_program_id, - rewardPrograms.internal_id, - ), + rewardPrograms.internal_id + ) + ) + .innerJoin( + rewards, + eq(rewardPrograms.internal_reward_id, rewards.internal_id) ) .where( - and( - eq(referralCodes.internal_customer_id, internalCustomerId), - eq(rewardRedemptions.triggered, true), - eq(rewardRedemptions.applied, false), - ), + or( + and( + eq(referralCodes.internal_customer_id, internalCustomerId), + eq(rewardRedemptions.triggered, true), + eq(rewardRedemptions.applied, false) + ), + and( + eq(rewardRedemptions.internal_customer_id, internalCustomerId), + eq(rewardRedemptions.triggered, true), + eq(rewardRedemptions.redeemer_applied, false) + ) + ) ); - if (data.length == 0) return []; + if (data.length === 0) return []; - let processed = data.map((d) => ({ + const processed = data.map((d) => ({ ...d.reward_redemptions, referral_code: d.referral_codes, reward_program: { ...d.reward_programs, - // reward: d.rewards, + reward: d.rewards, }, })); return processed; } + + static async _resetCustomerRedemptions({ + db, + internalCustomerId, + }: { + db: DrizzleCli; + internalCustomerId: string | string[]; + }) { + if (!Array.isArray(internalCustomerId)) + internalCustomerId = [internalCustomerId]; + return await db + .delete(rewardRedemptions) + .where( + inArray(rewardRedemptions.internal_customer_id, internalCustomerId) + ); + } } diff --git a/server/src/internal/rewards/RewardService.ts b/server/src/internal/rewards/RewardService.ts index 6ebb63b1a..ff7e417b3 100644 --- a/server/src/internal/rewards/RewardService.ts +++ b/server/src/internal/rewards/RewardService.ts @@ -2,175 +2,175 @@ import { type AppEnv, ErrCode, type Reward, - type RewardType, rewards, + RewardType, } from "@autumn/shared"; import { and, desc, eq, inArray, or, sql } from "drizzle-orm"; import type { DrizzleCli } from "@/db/initDrizzle.js"; import RecaseError from "@/utils/errorUtils.js"; export class RewardService { - static async get({ - db, - idOrInternalId, - orgId, - env, - }: { - db: DrizzleCli; - idOrInternalId: string; - orgId: string; - env: AppEnv; - }) { - const result = await db.query.rewards.findFirst({ - where: and( - or( - eq(rewards.id, idOrInternalId), - eq(rewards.internal_id, idOrInternalId), - ), - eq(rewards.org_id, orgId), - eq(rewards.env, env), - ), - }); + static async get({ + db, + idOrInternalId, + orgId, + env, + }: { + db: DrizzleCli; + idOrInternalId: string; + orgId: string; + env: AppEnv; + }) { + const result = await db.query.rewards.findFirst({ + where: and( + or( + eq(rewards.id, idOrInternalId), + eq(rewards.internal_id, idOrInternalId) + ), + eq(rewards.org_id, orgId), + eq(rewards.env, env) + ), + }); - if (!result) { - return null; - } + if (!result) { + return null; + } - return result as Reward; - } + return result as Reward; + } - static async getByIdOrCode({ - db, - codes, - orgId, - env, - }: { - db: DrizzleCli; - codes: string[]; - orgId: string; - env: AppEnv; - }) { - const reward = await db.query.rewards.findMany({ - where: and( - eq(rewards.org_id, orgId), - eq(rewards.env, env), - or( - inArray(rewards.id, codes), - ...codes.map( - (code) => sql`EXISTS ( + static async getByIdOrCode({ + db, + codes, + orgId, + env, + }: { + db: DrizzleCli; + codes: string[]; + orgId: string; + env: AppEnv; + }) { + const reward = await db.query.rewards.findMany({ + where: and( + eq(rewards.org_id, orgId), + eq(rewards.env, env), + or( + inArray(rewards.id, codes), + ...codes.map( + (code) => sql`EXISTS ( SELECT 1 FROM unnest("promo_codes") AS elem WHERE elem->>'code' = ${code} - )`, - ), - ), - ), - }); + )` + ) + ) + ), + }); - return reward as Reward[]; - } + return reward as Reward[]; + } - static async insert({ - db, - data, - }: { - db: DrizzleCli; - data: Reward | Reward[]; - }) { - const results = await db.insert(rewards).values(data as Reward); - return results as Reward[]; - } + static async insert({ + db, + data, + }: { + db: DrizzleCli; + data: Reward | Reward[]; + }) { + const results = await db.insert(rewards).values(data as Reward); + return results as Reward[]; + } - static async list({ - db, - orgId, - env, - inTypes, - }: { - db: DrizzleCli; - orgId: string; - env: AppEnv; - inTypes?: RewardType[]; - }) { - const results = await db.query.rewards.findMany({ - where: and( - eq(rewards.org_id, orgId), - eq(rewards.env, env), - inTypes ? inArray(rewards.type, inTypes) : undefined, - ), - orderBy: [desc(rewards.internal_id)], - }); + static async list({ + db, + orgId, + env, + inTypes, + }: { + db: DrizzleCli; + orgId: string; + env: AppEnv; + inTypes?: RewardType[]; + }) { + const results = await db.query.rewards.findMany({ + where: and( + eq(rewards.org_id, orgId), + eq(rewards.env, env), + inTypes ? inArray(rewards.type, inTypes) : undefined + ), + orderBy: [desc(rewards.internal_id)], + }); - return results as Reward[]; - } + return results as Reward[]; + } - static async delete({ - db, - internalId, - env, - orgId, - }: { - db: DrizzleCli; - internalId: string; - env: AppEnv; - orgId: string; - }) { - await db - .delete(rewards) - .where( - and( - eq(rewards.internal_id, internalId), - eq(rewards.env, env), - eq(rewards.org_id, orgId), - ), - ); - } + static async delete({ + db, + internalId, + env, + orgId, + }: { + db: DrizzleCli; + internalId: string; + env: AppEnv; + orgId: string; + }) { + await db + .delete(rewards) + .where( + and( + eq(rewards.internal_id, internalId), + eq(rewards.env, env), + eq(rewards.org_id, orgId) + ) + ); + } - static async update({ - db, - internalId, - env, - orgId, - update, - }: { - db: DrizzleCli; - internalId: string; - env: AppEnv; - orgId: string; - update: Partial; - }) { - const result = await db - .update(rewards) - .set(update) - .where( - and( - eq(rewards.internal_id, internalId), - eq(rewards.env, env), - eq(rewards.org_id, orgId), - ), - ) - .returning(); + static async update({ + db, + internalId, + env, + orgId, + update, + }: { + db: DrizzleCli; + internalId: string; + env: AppEnv; + orgId: string; + update: Partial; + }) { + const result = await db + .update(rewards) + .set(update) + .where( + and( + eq(rewards.internal_id, internalId), + eq(rewards.env, env), + eq(rewards.org_id, orgId) + ) + ) + .returning(); - if (result.length === 0) { - throw new RecaseError({ - message: `Reward ${internalId} not found`, - code: ErrCode.InvalidRequest, - }); - } + if (result.length === 0) { + throw new RecaseError({ + message: `Reward ${internalId} not found`, + code: ErrCode.InvalidRequest, + }); + } - return result[0] as Reward; - } + return result[0] as Reward; + } - static async deleteByOrgId({ - db, - orgId, - env, - }: { - db: DrizzleCli; - orgId: string; - env: AppEnv; - }) { - await db - .delete(rewards) - .where(and(eq(rewards.org_id, orgId), eq(rewards.env, env))); - } + static async deleteByOrgId({ + db, + orgId, + env, + }: { + db: DrizzleCli; + orgId: string; + env: AppEnv; + }) { + await db + .delete(rewards) + .where(and(eq(rewards.org_id, orgId), eq(rewards.env, env))); + } } diff --git a/server/src/internal/rewards/referralUtils.ts b/server/src/internal/rewards/referralUtils.ts index 98d592889..cd49d06b3 100644 --- a/server/src/internal/rewards/referralUtils.ts +++ b/server/src/internal/rewards/referralUtils.ts @@ -1,31 +1,43 @@ import { - AppEnv, - CusProductStatus, - Customer, + type AppEnv, + AttachBranch, + type Customer, ErrCode, - FullRewardProgram, - ReferralCode, - Reward, - RewardCategory, + type FullRewardProgram, + type ReferralCode, + type Reward, RewardReceivedBy, - RewardRedemption, + type RewardRedemption, } from "@autumn/shared"; - -import { CusService } from "../customers/CusService.js"; +import { StatusCodes } from "http-status-codes"; +import type Stripe from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { createStripeCusIfNotExists } from "@/external/stripe/stripeCusUtils.js"; import { createStripeCli } from "@/external/stripe/utils.js"; -import Stripe from "stripe"; -import { RewardRedemptionService } from "./RewardRedemptionService.js"; -import { ProductService } from "../products/ProductService.js"; -import { createFullCusProduct } from "../customers/add-product/createFullCusProduct.js"; -import { InsertCusProductParams } from "../customers/cusProducts/AttachParams.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { CusProductService } from "../customers/cusProducts/CusProductService.js"; import RecaseError from "@/utils/errorUtils.js"; -import { StatusCodes } from "http-status-codes"; -import { getRewardCat } from "./rewardUtils.js"; -import { ExtendedRequest } from "@/utils/models/Request.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; +import { createFullCusProduct } from "../customers/add-product/createFullCusProduct.js"; +import { handleAddProduct } from "../customers/attach/attachFunctions/addProductFlow/handleAddProduct.js"; +import { rewardProgramToAttachParams } from "../customers/attach/attachUtils/attachParams/convertToParams.js"; +import { CusService } from "../customers/CusService.js"; import { deleteCusCache } from "../customers/cusCache/updateCachedCus.js"; +import type { InsertCusProductParams } from "../customers/cusProducts/AttachParams.js"; +import { ProductService } from "../products/ProductService.js"; +import { + isFreeProduct, + isOneOff, + itemsAreOneOff, +} from "../products/productUtils.js"; +import { RewardRedemptionService } from "./RewardRedemptionService.js"; +import { triggerFreePaidProduct } from "./referralUtils/triggerFreePaidProduct.js"; + +export const ReferralResponseCodes = { + OwnsProduct: "has_product_already", + Success: "success", + Unknown: "unknown", + NotConfigured: "not_configured", + InternalError: "internal_error", +}; export const generateReferralCode = () => { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -62,7 +74,7 @@ export const triggerRedemption = async ({ `Triggering redemption ${redemption.id} for referral code ${referralCode.code}` ); - let applyToCustomer = await CusService.getByInternalId({ + const applyToCustomer = await CusService.getByInternalId({ db, internalId: referralCode.internal_customer_id, }); @@ -75,7 +87,7 @@ export const triggerRedemption = async ({ }); } - let stripeCli = createStripeCli({ + const stripeCli = createStripeCli({ org, env, legacyVersion: true, @@ -89,15 +101,15 @@ export const triggerRedemption = async ({ logger, }); - let stripeCusId = applyToCustomer.processor.id; - let stripeCus = (await stripeCli.customers.retrieve( + const stripeCusId = applyToCustomer.processor.id; + const stripeCus = (await stripeCli.customers.retrieve( stripeCusId )) as Stripe.Customer; let applied = false; if (!stripeCus.discount) { await stripeCli.customers.update(stripeCusId, { - // @ts-ignore + // @ts-expect-error coupon: reward.id, }); @@ -105,7 +117,7 @@ export const triggerRedemption = async ({ logger.info(`Applied coupon to customer in Stripe`); } - let updatedRedemption = await RewardRedemptionService.update({ + const updatedRedemption = await RewardRedemptionService.update({ db, id: redemption.id, updates: { @@ -118,143 +130,3 @@ export const triggerRedemption = async ({ return updatedRedemption; }; - -export const triggerFreeProduct = async ({ - req, - db, - referralCode, - redeemer, - redemption, - rewardProgram, - org, - env, - logger, -}: { - req?: ExtendedRequest; - db: DrizzleCli; - referralCode: ReferralCode; - redeemer: Customer; - redemption: RewardRedemption; - rewardProgram: FullRewardProgram; - org: any; - env: AppEnv; - logger: any; -}) => { - logger.info(`Triggering free product reward`); - let { received_by } = rewardProgram; - - let addToRedeemer = received_by === RewardReceivedBy.All; - let addToReferrer = - received_by === RewardReceivedBy.Referrer || - received_by === RewardReceivedBy.All; - - let productId = rewardProgram.reward.free_product_id!; - - let fullProduct = await ProductService.getFull({ - db, - idOrInternalId: productId, - orgId: org.id, - env, - }); - - if (!fullProduct) { - throw new RecaseError({ - message: `Product ${productId} not found`, - code: ErrCode.ProductNotFound, - statusCode: StatusCodes.NOT_FOUND, - }); - } - - let referrer = await CusService.getByInternalId({ - db, - internalId: referralCode.internal_customer_id, - }); - - if (!referrer) { - throw new RecaseError({ - message: `Referrer ${referralCode.internal_customer_id} not found`, - code: ErrCode.CustomerNotFound, - statusCode: StatusCodes.NOT_FOUND, - }); - } - - let [fullReferrer, fullRedeemer] = await Promise.all([ - CusService.getFull({ - db, - idOrInternalId: referrer.id!, - orgId: org.id, - env, - }), - CusService.getFull({ - db, - idOrInternalId: redeemer.id!, - orgId: org.id, - env, - }), - ]); - - let attachParams: InsertCusProductParams = { - req, - org, - product: fullProduct, - prices: fullProduct.prices, - entitlements: fullProduct.entitlements, - optionsList: [], - entities: [], - freeTrial: null, - features: [], - customer: fullReferrer, - cusProducts: fullReferrer.customer_products, - replaceables: [], - }; - - if (addToRedeemer) { - let redeemerAttachParams = structuredClone({ - ...attachParams, - customer: fullRedeemer, - cusProducts: fullRedeemer.customer_products, - }); - - await createFullCusProduct({ - db, - attachParams: redeemerAttachParams, - logger, - }); - logger.info(`✅ Added ${fullProduct.name} to redeemer`); - - await deleteCusCache({ - db, - customerId: fullRedeemer.id!, - org, - env, - }); - } - - if (addToReferrer) { - await createFullCusProduct({ - db, - attachParams: { - ...attachParams, - customer: fullReferrer, - cusProducts: fullReferrer.customer_products, - }, - logger, - }); - await deleteCusCache({ - db, - customerId: fullReferrer.id!, - org, - env, - }); - logger.info(`✅ Added ${fullProduct.name} to referrer`); - } - - await RewardRedemptionService.update({ - db, - id: redemption.id, - updates: { - triggered: true, - applied: true, - }, - }); -}; diff --git a/server/src/internal/rewards/referralUtils/triggerFreePaidProduct.ts b/server/src/internal/rewards/referralUtils/triggerFreePaidProduct.ts new file mode 100644 index 000000000..1e2350a89 --- /dev/null +++ b/server/src/internal/rewards/referralUtils/triggerFreePaidProduct.ts @@ -0,0 +1,191 @@ +import { + AttachBranch, + type Customer, + ErrCode, + type FullProduct, + type FullRewardProgram, + type ReferralCode, + type Reward, + RewardReceivedBy, + type RewardRedemption, +} from "@autumn/shared"; +import { StatusCodes } from "http-status-codes"; +import type Stripe from "stripe"; +import { createStripeCusIfNotExists } from "@/external/stripe/stripeCusUtils.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; +import { handleAddProduct } from "@/internal/customers/attach/attachFunctions/addProductFlow/handleAddProduct.js"; +import { rewardProgramToAttachParams } from "@/internal/customers/attach/attachUtils/attachParams/convertToParams.js"; +import { getCustomerSub } from "@/internal/customers/attach/attachUtils/convertAttachParams.js"; +import { getDefaultAttachConfig } from "@/internal/customers/attach/attachUtils/getAttachConfig.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { isStripeConnected } from "@/internal/orgs/orgUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; +import { RewardRedemptionService } from "../RewardRedemptionService.js"; +import { ReferralResponseCodes } from "../referralUtils.js"; + +export const receivedByReferrer = (received_by: RewardReceivedBy) => { + return ( + received_by === RewardReceivedBy.Referrer || + received_by === RewardReceivedBy.All + ); +}; + +export const receivedByRedeemer = (received_by: RewardReceivedBy) => { + return received_by === RewardReceivedBy.All; +}; + +export const triggerFreePaidProduct = async ({ + req, + referralCode, + redeemer, + rewardProgram, + fullProduct, + redemption, +}: { + req: ExtendedRequest; + referralCode: ReferralCode; + redeemer: Customer; + rewardProgram: FullRewardProgram & { reward: Reward }; + fullProduct: FullProduct; + redemption: RewardRedemption; +}) => { + const { db, org, env, logger } = req; + const { received_by } = rewardProgram; + + logger.info( + `Triggering free paid product reward for referral code ${referralCode.code}`, + ); + + const [fullReferrer, fullRedeemer] = await Promise.all([ + CusService.getFull({ + db, + idOrInternalId: referralCode.internal_customer_id, + orgId: org.id, + env, + withEntities: true, + withSubs: true, + }), + CusService.getFull({ + db, + idOrInternalId: redeemer.id!, + orgId: org.id, + env, + withEntities: true, + withSubs: true, + }), + ]); + + if (!isStripeConnected({ org, env })) { + throw new RecaseError({ + message: "Stripe is not connected", + code: ErrCode.StripeConfigNotFound, + statusCode: StatusCodes.BAD_REQUEST, + }); + } + + // Add to referrer / redeemer + const stripeCli = createStripeCli({ org, env }); + + const applied = [false, false]; // [referrerApplied, redeemerApplied] + for (let i = 0; i < 2; i++) { + if (i === 0 && !receivedByReferrer(received_by)) { + applied[i] = true; + continue; + } + if (i === 1 && !receivedByRedeemer(received_by)) { + applied[i] = true; + continue; + } + + const fullCus = [fullReferrer, fullRedeemer][i]; + const attachParams = rewardProgramToAttachParams({ + req, + rewardProgram, + customer: fullCus, + product: fullProduct, + }); + + const { sub } = await getCustomerSub({ attachParams }); + + if (sub) { + console.log("Detected sub", !!sub); + const curDiscounts = (sub.discounts as Stripe.Discount[]) || []; + + // If coupon already applied, don't add it again + if ( + !curDiscounts.some((d: any) => d.coupon?.id === rewardProgram.reward.id) + ) { + console.log("Detected no discount, adding it"); + try { + await stripeCli.subscriptions.update(sub.id, { + discounts: [ + ...curDiscounts.map((d: Stripe.Discount) => ({ + discount: d.id, + })), + { + coupon: rewardProgram.reward.id, + }, + ], + }); + } catch (error) { + console.log("Error adding discount", error); + } + applied[i] = true; + } + } else { + // Create stripe customer if not exists + await createStripeCusIfNotExists({ + db, + customer: fullCus, + org, + env, + logger, + }); + + await handleAddProduct({ + req, + attachParams, + branch: AttachBranch.New, + config: { + ...getDefaultAttachConfig(), + requirePaymentMethod: false, + }, + }); + applied[i] = true; + } + } + + const updates = { + triggered: true, + applied: applied?.[0] || false, // referrer applied + redeemer_applied: applied?.[1] || false, // redeemer applied + }; + + await RewardRedemptionService.update({ + db, + id: redemption.id, + updates, + }); + + return { + redeemer: { + applied: true, + cause: applied?.[0] + ? ReferralResponseCodes.Success + : ReferralResponseCodes.OwnsProduct, + meta: { + id: fullRedeemer.id, + name: fullRedeemer.name, + email: fullRedeemer.email, + created_at: fullRedeemer.created_at, + }, + }, + referrer: { + applied: true, + cause: applied?.[1] + ? ReferralResponseCodes.Success + : ReferralResponseCodes.OwnsProduct, + }, + }; +}; diff --git a/server/src/internal/rewards/referralUtils/triggerFreeProduct.ts b/server/src/internal/rewards/referralUtils/triggerFreeProduct.ts new file mode 100644 index 000000000..fc10fc6b6 --- /dev/null +++ b/server/src/internal/rewards/referralUtils/triggerFreeProduct.ts @@ -0,0 +1,208 @@ +import { + type AppEnv, + type Customer, + ErrCode, + type FullRewardProgram, + type ReferralCode, + type Reward, + RewardReceivedBy, + type RewardRedemption, +} from "@autumn/shared"; +import { StatusCodes } from "http-status-codes"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import { createFullCusProduct } from "@/internal/customers/add-product/createFullCusProduct.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { deleteCusCache } from "@/internal/customers/cusCache/updateCachedCus.js"; +import type { InsertCusProductParams } from "@/internal/customers/cusProducts/AttachParams.js"; +import { ProductService } from "@/internal/products/ProductService.js"; +import { isFreeProduct, isOneOff } from "@/internal/products/productUtils.js"; +import RecaseError from "@/utils/errorUtils.js"; +import type { ExtendedRequest } from "@/utils/models/Request.js"; +import { RewardRedemptionService } from "../RewardRedemptionService.js"; +import { ReferralResponseCodes } from "../referralUtils.js"; +import { triggerFreePaidProduct } from "./triggerFreePaidProduct.js"; + +export const triggerFreeProduct = async ({ + req, + db, + referralCode, + redeemer, + redemption, + rewardProgram, + org, + env, + logger, +}: { + req?: ExtendedRequest; + db: DrizzleCli; + referralCode: ReferralCode; + redeemer: Customer; + redemption: RewardRedemption; + rewardProgram: FullRewardProgram & { reward: Reward }; + org: any; + env: AppEnv; + logger: any; +}) => { + logger.info(`Triggering free product reward`); + const { received_by } = rewardProgram; + + const addToRedeemer = received_by === RewardReceivedBy.All; + const addToReferrer = + received_by === RewardReceivedBy.Referrer || + received_by === RewardReceivedBy.All; + + const productId = rewardProgram.reward.free_product_id!; + + const fullProduct = await ProductService.getFull({ + db, + idOrInternalId: productId, + orgId: org.id, + env, + }); + + function seedReq(req?: ExtendedRequest) { + // Seed in properties that aren't usually present dependent on the trigger type + return { + ...(req || {}), + db: req?.db ? req.db : db, + org: req?.org ? req.org : org, + env: req?.env ? req.env : env, + logger: req?.logger ? req.logger : logger, + logtail: req?.logtail ? req.logtail : logger, + } as ExtendedRequest; + } + + if (!isFreeProduct(fullProduct.prices) && !isOneOff(fullProduct.prices)) { + req = seedReq(req); + return await triggerFreePaidProduct({ + req, + referralCode, + redeemer, + rewardProgram, + fullProduct, + redemption, + }); + } + + // const isPaidProduct = !isFreeProduct(fullProduct.prices); + // const isRecurring = + // !isOneOff(fullProduct.prices) && !itemsAreOneOff(fullProduct.entitlements); + + if (!fullProduct) { + throw new RecaseError({ + message: `Product ${productId} not found`, + code: ErrCode.ProductNotFound, + statusCode: StatusCodes.NOT_FOUND, + }); + } + + const [fullReferrer, fullRedeemer] = await Promise.all([ + CusService.getFull({ + db, + idOrInternalId: referralCode.internal_customer_id, + orgId: org.id, + env, + allowNotFound: true, + }), + CusService.getFull({ + db, + idOrInternalId: redeemer.id!, + orgId: org.id, + env, + }), + ]); + + if (!fullReferrer) { + throw new RecaseError({ + message: `Referrer (internal ID: ${referralCode.internal_customer_id}) not found`, + code: ErrCode.CustomerNotFound, + statusCode: StatusCodes.NOT_FOUND, + }); + } + + const attachParams: InsertCusProductParams = { + req, + org, + product: fullProduct, + prices: fullProduct.prices, + entitlements: fullProduct.entitlements, + optionsList: [], + entities: [], + freeTrial: null, + features: [], + customer: fullReferrer, + cusProducts: fullReferrer.customer_products, + replaceables: [], + }; + + if (addToRedeemer) { + const redeemerAttachParams = { + ...structuredClone(attachParams), + customer: fullRedeemer, + cusProducts: fullRedeemer.customer_products, + }; + + await createFullCusProduct({ + db, + attachParams: redeemerAttachParams, + logger, + }); + logger.info(`✅ Added ${fullProduct.name} to redeemer`); + + await deleteCusCache({ + db, + customerId: fullRedeemer.id!, + org, + env, + }); + } + + if (addToReferrer) { + await createFullCusProduct({ + db, + attachParams: { + ...structuredClone(attachParams), + customer: fullReferrer, + cusProducts: fullReferrer.customer_products, + }, + logger, + }); + await deleteCusCache({ + db, + customerId: fullReferrer.id!, + org, + env, + }); + logger.info(`✅ Added ${fullProduct.name} to referrer`); + } + + await RewardRedemptionService.update({ + db, + id: redemption.id, + updates: { + triggered: true, + applied: true, + }, + }); + + return { + redeemer: { + applied: addToRedeemer, + cause: addToRedeemer + ? ReferralResponseCodes.Success + : ReferralResponseCodes.OwnsProduct, + meta: { + id: fullRedeemer.id, + name: fullRedeemer.name, + email: fullRedeemer.email, + created_at: fullRedeemer.created_at, + }, + }, + referrer: { + applied: addToReferrer, + cause: addToReferrer + ? ReferralResponseCodes.Success + : ReferralResponseCodes.OwnsProduct, + }, + }; +}; diff --git a/server/src/internal/rewards/triggerCheckoutReward.ts b/server/src/internal/rewards/triggerCheckoutReward.ts index f2ee85000..cfb93d981 100644 --- a/server/src/internal/rewards/triggerCheckoutReward.ts +++ b/server/src/internal/rewards/triggerCheckoutReward.ts @@ -1,115 +1,137 @@ -import { RewardRedemptionService } from "./RewardRedemptionService.js"; -import { RewardCategory, RewardTriggerEvent } from "@autumn/shared"; -import { triggerFreeProduct, triggerRedemption } from "./referralUtils.js"; -import { RewardProgramService } from "../rewards/RewardProgramService.js"; -import { getRewardCat } from "./rewardUtils.js"; +import { + type ReferralCode, + type Reward, + RewardCategory, + type RewardProgram, + type RewardRedemption, + RewardTriggerEvent, +} from "@autumn/shared"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { createStripeCli } from "@/external/stripe/utils.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; +import { RewardProgramService } from "../rewards/RewardProgramService.js"; +import { RewardRedemptionService } from "./RewardRedemptionService.js"; +import { triggerFreeProduct } from "./referralUtils/triggerFreeProduct.js"; +import { triggerRedemption } from "./referralUtils.js"; +import { getRewardCat } from "./rewardUtils.js"; export const runTriggerCheckoutReward = async ({ - db, - payload, - logger, + db, + payload, + logger, }: { - db: DrizzleCli; - payload: any; - logger: any; + db: DrizzleCli; + payload: any; + logger: any; }) => { - try { - // Customer redeeming code, product they're buying - let { customer, product, org, env, subId } = payload; + try { + // Customer redeeming code, product they're buying + const { customer, product, org, env, subId } = payload; + const stripeCli = createStripeCli({ + org, + env, + }); - let stripeCli = createStripeCli({ - org, - env, - }); + // 1. Check if redemption exists + const redemptions = await RewardRedemptionService.getByCustomer({ + db, + internalCustomerId: customer.internal_id, // customer that redeemed code + withRewardProgram: true, + triggered: false, + withReferralCode: true, + triggerWhen: RewardTriggerEvent.Checkout, + }); - // 1. Check if redemption exists - let redemptions = await RewardRedemptionService.getByCustomer({ - db, - internalCustomerId: customer.internal_id, // customer that redeemed code - withRewardProgram: true, - triggered: false, - withReferralCode: true, - triggerWhen: RewardTriggerEvent.Checkout, - }); + for (const redemption of redemptions) { + if ( + !redemption || + redemption.reward_program.when !== RewardTriggerEvent.Checkout + ) { + console.info( + "No redemption found or reward program not set to checkout, skipping", + ); + return; + } - for (let redemption of redemptions) { - if ( - !redemption || - redemption.reward_program.when !== RewardTriggerEvent.Checkout - ) { - return; - } + const { reward_program, referral_code: referralCode } = + redemption as RewardRedemption & { + reward_program: RewardProgram & { reward: Reward }; + referral_code: ReferralCode; + }; + const { reward } = reward_program; - let { reward_program, referral_code: referralCode } = redemption; - let { reward } = reward_program; + console.info(`--------------------------------`); + console.info(`CHECKING FOR CHECKOUT REWARD, ORG: ${org.slug}`); + console.info( + `Redeemed by: ${customer.name} (${customer.id}) for referral program: ${reward_program.id}`, + ); + console.info(`Referral code: ${referralCode.code} (${referralCode.id})`); + console.info( + `Products: ${reward_program.product_ids?.join(", ")}, ${reward_program.reward.free_product_id}`, + ); - logger.info(`--------------------------------`); - logger.info(`CHECKING FOR CHECKOUT REWARD, ORG: ${org.slug}`); - logger.info( - `Redeemed by: ${customer.name} (${customer.id}) for referral program: ${reward_program.id}` - ); - logger.info(`Referral code: ${referralCode.code} (${referralCode.id})`); + if (!reward_program.product_ids?.includes(product.id)) { + console.info( + `Product ${product.name} (${product.id}) not included in referral program, skipping`, + ); + if (reward_program.reward.free_product_id !== product.id) { + return; + } + } - if (!reward_program.product_ids.includes(product.id)) { - logger.info( - `Product ${product.name} (${product.id}) not included in referral program, skipping` - ); - return; - } + // Check for trial + let hasTrial = false; + if (subId) { + const sub = await stripeCli.subscriptions.retrieve(subId); + // hasTrial = Boolean(sub.trial_end && sub.trial_end > Date.now()); + hasTrial = sub.status === "trialing"; + } - // Check for trial - let hasTrial = false; - if (subId) { - let sub = await stripeCli.subscriptions.retrieve(subId); - // hasTrial = Boolean(sub.trial_end && sub.trial_end > Date.now()); - hasTrial = sub.status === "trialing"; - } + if (hasTrial) { + console.info(`Subscription is on trial, not triggering reward`); + return; + } - if (hasTrial) { - logger.info(`Subscription is on trial, not triggering reward`); - return; - } + // Get redemption count + const redemptionCount = await RewardProgramService.getCodeRedemptionCount( + { + db, + referralCodeId: referralCode.id, + }, + ); - // Get redemption count - let redemptionCount = await RewardProgramService.getCodeRedemptionCount({ - db, - referralCodeId: referralCode.id, - }); + if (redemptionCount >= reward_program.max_redemptions!) { + console.info( + `Max redemptions reached, not triggering latest redemption`, + ); + return; + } - if (redemptionCount >= reward_program.max_redemptions) { - logger.info( - `Max redemptions reached, not triggering latest redemption` - ); - return; - } - - let rewardCat = getRewardCat(reward); - if (rewardCat === RewardCategory.FreeProduct) { - await triggerFreeProduct({ - db, - referralCode, - redeemer: customer, - rewardProgram: reward_program, - org, - env, - logger, - redemption, - }); - } else { - await triggerRedemption({ - db, - referralCode, - org, - env, - logger, - reward, - redemption, - }); - } - } - } catch (error) { - logger.error("Failed to trigger checkout reward"); - logger.error(error); - } + const rewardCat = getRewardCat(reward); + if (rewardCat === RewardCategory.FreeProduct) { + await triggerFreeProduct({ + req: undefined, + db, + referralCode, + redeemer: customer, + rewardProgram: reward_program, + org, + env, + logger, + redemption, + }); + } else { + await triggerRedemption({ + db, + referralCode, + org, + env, + logger, + reward, + redemption, + }); + } + } + } catch (error) { + console.error("Failed to trigger checkout reward"); + console.error(error); + } }; diff --git a/server/src/middleware/refreshCacheMiddleware.ts b/server/src/middleware/refreshCacheMiddleware.ts index d511f80b3..0b3170d74 100644 --- a/server/src/middleware/refreshCacheMiddleware.ts +++ b/server/src/middleware/refreshCacheMiddleware.ts @@ -34,6 +34,11 @@ const cusPrefixedUrls = [ url: "/customers/:customer_id/entities", type: "delete", }, + { + method: "DELETE", + url: "/customers/:customer_id/entities/:entity_id", + type: "delete", + }, { method: "POST", url: "/customers/:customer_id/transfer_product", diff --git a/server/src/queue/workersInit.ts b/server/src/queue/workersInit.ts index 13c7ac6a8..cf59b314e 100644 --- a/server/src/queue/workersInit.ts +++ b/server/src/queue/workersInit.ts @@ -6,7 +6,6 @@ import { logger } from "@/external/logtail/logtailUtils.js"; import { runActionHandlerTask } from "@/internal/analytics/runActionHandlerTask.js"; import { runSaveFeatureDisplayTask } from "@/internal/features/featureUtils.js"; import { runMigrationTask } from "@/internal/migrations/runMigrationTask.js"; -import { runRewardMigrationTask } from "@/internal/migrations/runRewardMigrationTask.js"; import { detectBaseVariant } from "@/internal/products/productUtils/detectProductVariant.js"; import { runTriggerCheckoutReward } from "@/internal/rewards/triggerCheckoutReward.js"; import { runUpdateBalanceTask } from "@/trigger/updateBalanceTask.js"; @@ -15,229 +14,230 @@ import { generateId } from "@/utils/genUtils.js"; import { JobName } from "./JobName.js"; import { acquireLock, getRedisConnection, releaseLock } from "./lockUtils.js"; import { QueueManager } from "./QueueManager.js"; +import { runRewardMigrationTask } from "@/internal/migrations/runRewardMigrationTask.js"; const NUM_WORKERS = 10; const actionHandlers = [ - JobName.HandleProductsUpdated, - JobName.HandleCustomerCreated, + JobName.HandleProductsUpdated, + JobName.HandleCustomerCreated, ]; const { db } = initDrizzle({ maxConnections: 10 }); const initWorker = ({ - id, - queue, - useBackup, - db, + id, + queue, + useBackup, + db, }: { - id: number; - queue: Queue; - useBackup: boolean; - db: DrizzleCli; + id: number; + queue: Queue; + useBackup: boolean; + db: DrizzleCli; }) => { - const worker = new Worker( - "autumn", - async (job: Job) => { - const logtail = logger.child({ - context: { - worker: { - task: job.name, - data: job.data, - jobId: generateId("job"), - workerId: id, - }, - }, - }); + const worker = new Worker( + "autumn", + async (job: Job) => { + const logtail = logger.child({ + context: { + worker: { + task: job.name, + data: job.data, + jobId: generateId("job"), + workerId: id, + }, + }, + }); - try { - if (job.name === JobName.DetectBaseVariant) { - await detectBaseVariant({ - db, - curProduct: job.data.curProduct, - logger: logtail as Logger, - }); - return; - } + try { + if (job.name === JobName.DetectBaseVariant) { + await detectBaseVariant({ + db, + curProduct: job.data.curProduct, + logger: logtail as Logger, + }); + return; + } - if (job.name === JobName.GenerateFeatureDisplay) { - await runSaveFeatureDisplayTask({ - db, - feature: job.data.feature, - logger: logtail, - }); - return; - } + if (job.name === JobName.GenerateFeatureDisplay) { + await runSaveFeatureDisplayTask({ + db, + feature: job.data.feature, + logger: logtail, + }); + return; + } - if (job.name === JobName.Migration) { - await runMigrationTask({ - db, - payload: job.data, - logger: logtail, - }); - return; - } + if (job.name === JobName.Migration) { + await runMigrationTask({ + db, + payload: job.data, + logger: logtail, + }); + return; + } - if (actionHandlers.includes(job.name as JobName)) { - await runActionHandlerTask({ - queue, - job, - logger: logtail, - db, - useBackup, - }); - return; - } + if (actionHandlers.includes(job.name as JobName)) { + await runActionHandlerTask({ + queue, + job, + logger: logtail, + db, + useBackup, + }); + return; + } - if (job.name === JobName.RewardMigration) { - await runRewardMigrationTask({ - db, - payload: job.data, - logger: logtail, - }); - } - } catch (error: any) { - logtail.error(`Failed to process bullmq job: ${job.name}`, { - jobName: job.name, - error: { - message: error.message, - stack: error.stack, - }, - }); - } + if (job.name === JobName.RewardMigration) { + await runRewardMigrationTask({ + db, + payload: job.data, + logger: logtail, + }); + } + } catch (error: any) { + logtail.error(`Failed to process bullmq job: ${job.name}`, { + jobName: job.name, + error: { + message: error.message, + stack: error.stack, + }, + }); + } - // TRIGGER CHECKOUT REWARD - if (job.name === JobName.TriggerCheckoutReward) { - const lockKey = `reward_trigger:${job.data.customer?.internal_id}`; - if ( - !(await acquireLock({ - lockKey, - timeout: 10000, - useBackup, - })) - ) { - await queue.add(job.name, job.data, { - delay: 1000, - }); - return; - } + // TRIGGER CHECKOUT REWARD + if (job.name === JobName.TriggerCheckoutReward) { + const lockKey = `reward_trigger:${job.data.customer?.internal_id}`; + if ( + !(await acquireLock({ + lockKey, + timeout: 10000, + useBackup, + })) + ) { + await queue.add(job.name, job.data, { + delay: 1000, + }); + return; + } - try { - await runTriggerCheckoutReward({ - db, - payload: job.data, - logger: logtail, - }); - } catch (error) { - console.error("Error processing job:", error); - } finally { - await releaseLock({ lockKey, useBackup }); - } + try { + await runTriggerCheckoutReward({ + db, + payload: job.data, + logger: logtail, + }); + } catch (error) { + console.error("Error processing job:", error); + } finally { + await releaseLock({ lockKey, useBackup }); + } - return; - } + return; + } - // EVENT HANDLERS - const { internalCustomerId } = job.data; // customerId is internal customer id + // EVENT HANDLERS + const { internalCustomerId } = job.data; // customerId is internal customer id - while ( - !(await acquireLock({ - lockKey: `event:${internalCustomerId}`, - timeout: 10000, - useBackup, - })) - ) { - await queue.add(job.name, job.data, { - delay: 200, - }); - return; - } + while ( + !(await acquireLock({ + lockKey: `event:${internalCustomerId}`, + timeout: 10000, + useBackup, + })) + ) { + await queue.add(job.name, job.data, { + delay: 200, + }); + return; + } - try { - if (job.name === JobName.UpdateBalance) { - await runUpdateBalanceTask({ - payload: job.data, - logger: logtail, - db, - }); - } else if (job.name === JobName.UpdateUsage) { - await runUpdateUsageTask({ - payload: job.data, - logger: logtail, - db, - }); - } - } catch (error) { - console.error("Error processing job:", error); - } finally { - await releaseLock({ - lockKey: `event:${internalCustomerId}`, - useBackup, - }); - } - }, - { - ...getRedisConnection({ useBackup }), - concurrency: 1, - removeOnComplete: { - count: 0, - }, - removeOnFail: { - count: 0, - }, - drainDelay: 1000, - maxStalledCount: 0, - }, - ); + try { + if (job.name === JobName.UpdateBalance) { + await runUpdateBalanceTask({ + payload: job.data, + logger: logtail, + db, + }); + } else if (job.name === JobName.UpdateUsage) { + await runUpdateUsageTask({ + payload: job.data, + logger: logtail, + db, + }); + } + } catch (error) { + console.error("Error processing job:", error); + } finally { + await releaseLock({ + lockKey: `event:${internalCustomerId}`, + useBackup, + }); + } + }, + { + ...getRedisConnection({ useBackup }), + concurrency: 1, + removeOnComplete: { + count: 0, + }, + removeOnFail: { + count: 0, + }, + drainDelay: 1000, + maxStalledCount: 0, + } + ); - worker.on("ready", () => { - console.log(`Worker ${id} ready (${useBackup ? "BACKUP" : "MAIN"})`); - }); + worker.on("ready", () => { + console.log(`Worker ${id} ready (${useBackup ? "BACKUP" : "MAIN"})`); + }); - worker.on("stalled", (jobId: string) => { - console.log(`Worker ${id} stalled (${useBackup ? "BACKUP" : "MAIN"})`); - console.log("JOB ID:", jobId); - }); + worker.on("stalled", (jobId: string) => { + console.log(`Worker ${id} stalled (${useBackup ? "BACKUP" : "MAIN"})`); + console.log("JOB ID:", jobId); + }); - worker.on("error", async (error: any) => { - if (error.code !== "ECONNREFUSED") { - console.log("WORKER ERROR:", error.message); - } - }); + worker.on("error", async (error: any) => { + if (error.code !== "ECONNREFUSED") { + console.log("WORKER ERROR:", error.message); + } + }); - worker.on("failed", (_, error) => { - console.log("WORKER FAILED:", error.message); - }); + worker.on("failed", (_, error) => { + console.log("WORKER FAILED:", error.message); + }); }; export const initWorkers = async () => { - const workers = []; + const workers = []; - const mainQueue = await QueueManager.getQueue({ useBackup: false }); - const backupQueue = await QueueManager.getQueue({ useBackup: true }); - await CacheManager.getInstance(); + const mainQueue = await QueueManager.getQueue({ useBackup: false }); + const backupQueue = await QueueManager.getQueue({ useBackup: true }); + await CacheManager.getInstance(); - for (let i = 0; i < NUM_WORKERS; i++) { - workers.push( - initWorker({ - id: i, - queue: mainQueue, - useBackup: false, - db, - }), - ); - workers.push( - initWorker({ - id: i, - queue: backupQueue, - useBackup: true, + for (let i = 0; i < NUM_WORKERS; i++) { + workers.push( + initWorker({ + id: i, + queue: mainQueue, + useBackup: false, + db, + }) + ); + workers.push( + initWorker({ + id: i, + queue: backupQueue, + useBackup: true, - db, - }), - ); - } + db, + }) + ); + } - // Get stalled jobs + // Get stalled jobs - return workers; + return workers; }; diff --git a/server/src/trigger/handleThresholdReached.ts b/server/src/trigger/handleThresholdReached.ts index b156063fc..f4b50e7a8 100644 --- a/server/src/trigger/handleThresholdReached.ts +++ b/server/src/trigger/handleThresholdReached.ts @@ -70,16 +70,12 @@ export const sendSvixThresholdReachedEvent = async ({ }); if (fullCus.entity) { - const entities = await EntityService.list({ - db, - internalCustomerId: fullCus.internal_id, - }); - fullCus.entities = entities; await getSingleEntityResponse({ org, env, features, fullCus, + entity: fullCus.entity, entityId: fullCus.entity.id, }); } @@ -240,15 +236,11 @@ export const handleThresholdReached = async ({ }); if (fullCus.entity) { - const entities = await EntityService.list({ - db, - internalCustomerId: fullCus.internal_id, - }); - fullCus.entities = entities; await getSingleEntityResponse({ org, env, features, + entity: fullCus.entity, fullCus, entityId: fullCus.entity.id, }); diff --git a/server/src/trigger/updateBalanceTask.ts b/server/src/trigger/updateBalanceTask.ts index e1d677878..04ee93ac0 100644 --- a/server/src/trigger/updateBalanceTask.ts +++ b/server/src/trigger/updateBalanceTask.ts @@ -603,6 +603,7 @@ export const updateCustomerBalance = async ({ env, inStatuses: [CusProductStatus.Active, CusProductStatus.PastDue], entityId, + withSubs: true, }); const { cusEnts, cusPrices } = await getCusEntsInFeatures({ diff --git a/server/src/utils/errorUtils.ts b/server/src/utils/errorUtils.ts index 26701ba9b..461f146f2 100644 --- a/server/src/utils/errorUtils.ts +++ b/server/src/utils/errorUtils.ts @@ -111,7 +111,7 @@ export const handleRequestError = ({ logger.warn( `RECASE WARNING (${req.org?.slug || "unknown"}): ${error.message} [${error.code}]`, { - error: error.data, + error: error.data ?? error, } ); diff --git a/server/src/utils/routerUtils.ts b/server/src/utils/routerUtils.ts index 1af70d20d..2d05cb0fd 100644 --- a/server/src/utils/routerUtils.ts +++ b/server/src/utils/routerUtils.ts @@ -280,6 +280,18 @@ export const routeHandler = async ({ let originalUrl = req.originalUrl; if (error instanceof Stripe.errors.StripeError) { + if ( + originalUrl.includes("/exchange") && + error.message.includes("Invalid API Key provided") + ) { + req.logtail.warn(`Exchange router, invalid API Key provided`); + + return res.status(400).json({ + message: error.message, + code: ErrCode.InvalidRequest, + }); + } + if ( originalUrl.includes("/billing_portal") && error.message.includes("Provide a configuration or create your default") diff --git a/server/src/utils/scriptUtils/constructItem.ts b/server/src/utils/scriptUtils/constructItem.ts index e02b06f10..b86870e52 100644 --- a/server/src/utils/scriptUtils/constructItem.ts +++ b/server/src/utils/scriptUtils/constructItem.ts @@ -52,6 +52,7 @@ export const constructFeatureItem = ({ export const constructPrepaidItem = ({ featureId, price = 9, + tiers, billingUnits = 100, includedUsage = 0, isOneOff = false, @@ -65,6 +66,7 @@ export const constructPrepaidItem = ({ }: { featureId: string; price?: number; + tiers?: { amount: number; to: number }[]; billingUnits?: number; includedUsage?: number; isOneOff?: boolean; @@ -78,6 +80,7 @@ export const constructPrepaidItem = ({ usage_model: UsageModel.Prepaid, price: price, + tiers: tiers, billing_units: billingUnits || 100, interval: isOneOff ? null : ProductItemInterval.Month, interval_count: intervalCount, diff --git a/server/src/utils/scriptUtils/initCustomer.ts b/server/src/utils/scriptUtils/initCustomer.ts index bc0759141..930ebf3ee 100644 --- a/server/src/utils/scriptUtils/initCustomer.ts +++ b/server/src/utils/scriptUtils/initCustomer.ts @@ -1,14 +1,20 @@ -import { DrizzleCli } from "@/db/initDrizzle.js"; -import { AppEnv, Customer, Organization, ProcessorType } from "@autumn/shared"; -import { createStripeCli } from "../../external/stripe/utils.js"; -import { Autumn } from "autumn-js"; +import { + type AppEnv, + type Customer, + type Organization, + ProcessorType, +} from "@autumn/shared"; +import type { Autumn } from "autumn-js"; +import type Stripe from "stripe"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { deleteCusCache } from "@/internal/customers/cusCache/updateCachedCus.js"; import { attachPmToCus, createStripeCustomer, } from "../../external/stripe/stripeCusUtils.js"; -import { CusService } from "@/internal/customers/CusService.js"; -import Stripe from "stripe"; -import { deleteCusCache } from "@/internal/customers/cusCache/updateCachedCus.js"; +import { createStripeCli } from "../../external/stripe/utils.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; export const createCusInStripe = async ({ customer, @@ -59,7 +65,7 @@ export const initCustomer = async ({ attachPm, withTestClock = true, }: { - autumn: Autumn; + autumn: Autumn | AutumnInt; customerId: string; fingerprint?: string; org: Organization; @@ -68,14 +74,14 @@ export const initCustomer = async ({ attachPm?: "success" | "fail"; withTestClock?: boolean; }) => { - let customerData = { + const customerData = { id: customerId, name: customerId, email: `${customerId}@example.com`, fingerprint, }; - let customer = await CusService.get({ + const customer = await CusService.get({ db, idOrInternalId: customerId, orgId: org.id, @@ -95,7 +101,7 @@ export const initCustomer = async ({ try { const res = await autumn.customers.create(customerData); - let customer = (await CusService.get({ + const customer = (await CusService.get({ db, idOrInternalId: customerId, orgId: org.id, @@ -150,7 +156,7 @@ export const attachPaymentMethod = async ({ type: "success" | "fail"; }) => { try { - let token = type === "fail" ? "tok_chargeCustomerFail" : "tok_visa"; + const token = type === "fail" ? "tok_chargeCustomerFail" : "tok_visa"; const pm = await stripeCli.paymentMethods.create({ type: "card", card: { @@ -190,12 +196,12 @@ export const initCustomerV2 = async ({ attachPm?: "success" | "fail"; withTestClock?: boolean; }) => { - let name = customerId; - let email = `${customerId}@example.com`; - let fingerprint_ = ""; + const name = customerId; + const email = `${customerId}@example.com`; + const fingerprint_ = ""; const stripeCli = createStripeCli({ org, env }); - let testClockId = undefined; + let testClockId; if (withTestClock) { const testClock = await stripeCli.testHelpers.testClocks.create({ @@ -220,7 +226,7 @@ export const initCustomerV2 = async ({ name, email, fingerprint: fingerprint_, - // @ts-ignore + // @ts-expect-error stripe_id: stripeCus.id, }); diff --git a/server/src/utils/scriptUtils/scriptUtils.ts b/server/src/utils/scriptUtils/scriptUtils.ts index b8999c305..9679699bb 100644 --- a/server/src/utils/scriptUtils/scriptUtils.ts +++ b/server/src/utils/scriptUtils/scriptUtils.ts @@ -179,6 +179,7 @@ export const initScript = async ({ db, features, logger, + logtail: logger, } as ExtendedRequest; return { stripeCli, autumnProducts, req }; diff --git a/server/test.sh b/server/test.sh index d297cf094..24893f49c 100755 --- a/server/test.sh +++ b/server/test.sh @@ -40,6 +40,12 @@ if [ "$1" == "g4" ]; then 'tests/advanced/coupons/*.ts' fi +# Group 5 - Paid referrals +if [ "$1" == "paid-referrals" ]; then + $MOCHA_SETUP && $MOCHA_CMD \ + 'tests/advanced/referrals/paid/*.ts' +fi + # Group 4 if [ "$1" == "g4" ]; then $MOCHA_SETUP && $MOCHA_CMD \ @@ -68,8 +74,11 @@ elif [ "$1" == "custom" ]; then ARG3="$3" if [ "$ARG3" == "setup" ]; then npx mocha --bail --timeout 10000000 'tests/00_setup.ts' + elif [ "$ARG3" == "parallel" ]; then + npx mocha --parallel --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts" + else + npx mocha --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts" fi - npx mocha --bail --timeout 10000000 "tests/$FILE_TO_TEST.ts" else npx mocha --timeout 10000000 'tests/00_setup.ts' && npx mocha --timeout 10000000 \ 'tests/**/*.ts' \ @@ -77,14 +86,6 @@ else --ignore 'tests/alex/**/*.ts' fi - - - - - - - - # # TEST PARALLEL # if [ "$1" == "basic-parallel" ]; then # MOCHA_PARALLEL=true $MOCHA_SETUP \ @@ -116,5 +117,3 @@ fi # && $MOCHA_CMD 'tests/advanced/arrear_prorated/*.ts' 'tests/advanced/coupons/*.ts'\ # # && $MOCHA_CMD 'tests/advanced/usage/*.ts' \ # # && $MOCHA_CMD 'tests/advanced/coupons/*.ts'\ - - diff --git a/server/tests/advanced/referrals/paid/referrals13.ts b/server/tests/advanced/referrals/paid/referrals13.ts new file mode 100644 index 000000000..207447704 --- /dev/null +++ b/server/tests/advanced/referrals/paid/referrals13.ts @@ -0,0 +1,255 @@ +import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { + type AppEnv, + CusExpand, + CusProductStatus, + ErrCode, + type Organization, + type ReferralCode, + type RewardRedemption, +} from "@autumn/shared"; +import { assert } from "chai"; +import chalk from "chalk"; +import type { Stripe } from "stripe"; +import { setupBefore } from "tests/before.js"; +import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js"; +import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; + +import { CusService } from "@/internal/customers/CusService.js"; +import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { products, referralPrograms } from "../../../global.js"; + +export const group = "referrals13"; + +describe(`${chalk.yellowBright( + "referrals13: Testing referrals - referrer on Pro, gets discount on next cycle - coupon-based" +)}`, () => { + const mainCustomerId = "main-referral-13"; + const redeemer = "referral13-r1"; + const redeemerPM = "success"; + const autumn: AutumnInt = new AutumnInt(); + let stripeCli: Stripe; + const testClockIds: string[] = []; + let referralCode: ReferralCode; + + let redemption: RewardRedemption; + let db: DrizzleCli; + let org: Organization; + let env: AppEnv; + + before(async function () { + await setupBefore(this); + stripeCli = this.stripeCli; + db = this.db; + org = this.org; + env = this.env; + + try { + await Promise.all([ + autumn.customers.delete(mainCustomerId), + autumn.customers.delete(redeemer), + RewardRedemptionService._resetCustomerRedemptions({ + db, + internalCustomerId: [mainCustomerId, redeemer], + }), + ]); + } catch {} + + // Initialize main customer with Pro product already attached + const res = await initCustomer({ + autumn: this.autumnJs, + customerId: mainCustomerId, + db, + org, + env, + attachPm: "success", + }); + + testClockIds.push(res.testClockId); + + // Attach Pro product to main customer first + await autumn.attach({ + customer_id: mainCustomerId, + product_id: products.pro.id, + }); + + const redeemerRes = await initCustomer({ + autumn: this.autumnJs, + customerId: redeemer, + db: this.db, + org: this.org, + env: this.env, + attachPm: redeemerPM, + withTestClock: true, + }); + + testClockIds.push(redeemerRes.testClockId); + }); + + it("should advance clock 10 days before redeeming", async () => { + // Advance 10 days after Pro is attached + await Promise.all( + testClockIds.map((x) => + advanceTestClock({ + testClockId: x, + numberOfDays: 10, + waitForSeconds: 10, + stripeCli, + }) + ) + ); + }); + + it("should create code once", async () => { + referralCode = await autumn.referrals.createCode({ + customerId: mainCustomerId, + referralId: referralPrograms.paidProductImmediateReferrer.id, + }); + + assert.exists(referralCode.code); + }); + + it("should create redemption for redeemer and fail if redeemed again", async () => { + redemption = await autumn.referrals.redeem({ + customerId: redeemer, + code: referralCode.code, + }); + + // Try redeem for redeemer again + try { + await autumn.referrals.redeem({ + customerId: redeemer, + code: referralCode.code, + }); + assert.fail("Should not be able to redeem again"); + } catch (error) { + assert.instanceOf(error, AutumnError); + assert.equal(error.code, ErrCode.CustomerAlreadyRedeemedReferralCode); + } + }); + + it("should have referrer already on Pro, and redeemer gets free product", async () => { + const redemptionResult = await autumn.redemptions.get(redemption.id); + assert.equal(redemptionResult.redeemer_applied, true); + + const mainProds = (await autumn.customers.get(mainCustomerId)).products; + const redeemerProds = (await autumn.customers.get(redeemer)).products; + + // Main customer (referrer) should have the pro product (already attached) + assert.equal(mainProds.length, 1); + assert.equal(mainProds[0].id, products.pro.id); + + // Redeemer should only have the free product (no pro product given in referrer-only program) + assert.equal(redeemerProds.length, 1); + assert.equal(redeemerProds[0].id, products.free.id); + + expectProductV1Attached({ + customer: await autumn.customers.get(mainCustomerId), + product: products.pro, + status: CusProductStatus.Active, + }); + + // Verify redeemer only has free product + expectProductV1Attached({ + customer: await autumn.customers.get(redeemer), + product: products.free, + status: CusProductStatus.Active, + }); + }); + + it("should advance test clock and verify referrer gets discount on next Pro cycle", async () => { + // Advance 31 days from current time to trigger next billing cycle + // Coupon was applied on day 10, lasts 30 days, so should still be active on day 31 + await Promise.all( + testClockIds.map((x) => + advanceTestClock({ + testClockId: x, + numberOfDays: 31, + waitForSeconds: 25, + stripeCli, + }) + ) + ); + + // Test that main customer's Pro invoice has discount applied + const mainCustomerWithInvoices = await autumn.customers.get( + mainCustomerId, + { + expand: [CusExpand.Invoices, CusExpand.Rewards], + } + ); + + const proInvoice = mainCustomerWithInvoices.invoices.find((x) => + x.product_ids.includes(products.pro.id) + ); + + const expectedTotal = products.pro.prices[0].config.amount; + + const actualTotal = proInvoice?.total; + + if (proInvoice) { + // Should have a discount applied - invoice total should be less than full Pro price ($10) + assert.isBelow( + actualTotal!, + expectedTotal, // $10 in cents + "Pro invoice should have discount applied, making it less than full price" + ); + + // For referrer-only reward, the discount should make it significantly cheaper or free + assert.isAtMost( + actualTotal!, + expectedTotal / 2, // $5 or less in cents - assuming at least 50% discount + "Referrer should get substantial discount on Pro product" + ); + } + + const dbCustomers = await Promise.all( + [mainCustomerId, redeemer].map((x) => + CusService.getFull({ + db, + idOrInternalId: x, + orgId: org.id, + env, + inStatuses: [ + CusProductStatus.Active, + CusProductStatus.PastDue, + CusProductStatus.Expired, + ], + }) + ) + ); + + const expectedProducts = [ + [ + // Main referrer - keeps Pro with discount applied + { name: "Free", status: CusProductStatus.Expired }, + { name: "Pro", status: CusProductStatus.Active }, + ], + [ + // Redeemer - only has free product (no reward in referrer-only program) + { name: "Free", status: CusProductStatus.Active }, + ], + ]; + + dbCustomers.forEach((customer, index) => { + const expectedProductsForCustomer = expectedProducts[index]; + expectedProductsForCustomer.forEach((expectedProduct) => { + const matchingProduct = customer.customer_products.find( + (cp) => + cp.product.name === expectedProduct.name && + cp.status === expectedProduct.status + ); + const unMatchedProduct = customer.customer_products.find( + (cp) => cp.product.name === expectedProduct.name + ); + + assert.exists( + matchingProduct, + `Customer ${customer.name} should have ${expectedProduct.name} product with status ${expectedProduct.status}. ${unMatchedProduct ? `However ${unMatchedProduct.product.name} with status ${unMatchedProduct.status} was found instead` : ""}` + ); + }); + }); + }); +}); diff --git a/server/tests/advanced/referrals/paid/referrals14.ts b/server/tests/advanced/referrals/paid/referrals14.ts new file mode 100644 index 000000000..12aa8246b --- /dev/null +++ b/server/tests/advanced/referrals/paid/referrals14.ts @@ -0,0 +1,264 @@ +import { + type AppEnv, + CusExpand, + CusProductStatus, + ErrCode, + type Organization, + type ReferralCode, + type RewardRedemption, +} from "@autumn/shared"; +import { assert } from "chai"; +import chalk from "chalk"; +import type { Stripe } from "stripe"; +import { setupBefore } from "tests/before.js"; +import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js"; +import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { products, referralPrograms } from "../../../global.js"; + +export const group = "referrals14"; + +describe(`${chalk.yellowBright( + "referrals14: Testing referrals - referrer on Premium (higher tier), gets pro_amount discount - coupon-based" +)}`, () => { + const mainCustomerId = "main-referral-14"; + const redeemer = "referral14-r1"; + const redeemerPM = "success"; + const autumn: AutumnInt = new AutumnInt(); + let stripeCli: Stripe; + const testClockIds: string[] = []; + let referralCode: ReferralCode; + + let redemption: RewardRedemption; + let db: DrizzleCli; + let org: Organization; + let env: AppEnv; + + before(async function () { + await setupBefore(this); + stripeCli = this.stripeCli; + db = this.db; + org = this.org; + env = this.env; + + try { + await Promise.all([ + autumn.customers.delete(mainCustomerId, { deleteInStripe: true }), + autumn.customers.delete(redeemer, { deleteInStripe: true }), + RewardRedemptionService._resetCustomerRedemptions({ + db, + internalCustomerId: [mainCustomerId, redeemer], + }), + ]); + } catch {} + + // Initialize main customer with Premium product already attached + const res = await initCustomer({ + autumn: this.autumnJs, + customerId: mainCustomerId, + db, + org, + env, + attachPm: "success", + }); + + testClockIds.push(res.testClockId); + + // Attach Premium product to main customer first (higher tier than Pro) + await autumn.attach({ + customer_id: mainCustomerId, + product_id: products.premium.id, + }); + + const redeemerRes = await initCustomer({ + autumn: this.autumnJs, + customerId: redeemer, + db: this.db, + org: this.org, + env: this.env, + attachPm: redeemerPM, + withTestClock: true, + }); + + testClockIds.push(redeemerRes.testClockId); + + // Advance 10 days after Premium is attached, then redeem the code + await Promise.all( + testClockIds.map((x) => + advanceTestClock({ + testClockId: x, + numberOfDays: 10, + waitForSeconds: 5, + stripeCli, + }) + ) + ); + }); + + it("should create code once", async () => { + referralCode = await autumn.referrals.createCode({ + customerId: mainCustomerId, + referralId: referralPrograms.paidProductImmediateReferrer.id, + }); + + assert.exists(referralCode.code); + + // Get referral code again + const referralCode2 = await autumn.referrals.createCode({ + customerId: mainCustomerId, + referralId: referralPrograms.paidProductImmediateReferrer.id, + }); + + assert.equal(referralCode2.code, referralCode.code); + }); + + it("should create redemption for redeemer and fail if redeemed again", async () => { + redemption = await autumn.referrals.redeem({ + customerId: redeemer, + code: referralCode.code, + }); + + // Try redeem for redeemer again + try { + await autumn.referrals.redeem({ + customerId: redeemer, + code: referralCode.code, + }); + assert.fail("Should not be able to redeem again"); + } catch (error) { + assert.instanceOf(error, AutumnError); + assert.equal(error.code, ErrCode.CustomerAlreadyRedeemedReferralCode); + } + }); + + it("should have referrer already on Premium, and redeemer gets free product", async () => { + const redemptionResult = await autumn.redemptions.get(redemption.id); + assert.equal(redemptionResult.redeemer_applied, true); + + const mainCus = await autumn.customers.get(mainCustomerId); + const redeemerCus = await autumn.customers.get(redeemer); + const mainProds = mainCus.products; + const redeemerProds = redeemerCus.products; + + // Main customer (referrer) should have the premium product (already attached) + assert.equal(mainProds.length, 1); + assert.equal(mainProds[0].id, products.premium.id); + + // Redeemer should only have the free product (no pro product given in referrer-only program) + assert.equal(redeemerProds.length, 1); + assert.equal(redeemerProds[0].id, products.free.id); + + expectProductV1Attached({ + customer: mainCus, + product: products.premium, + status: CusProductStatus.Active, + }); + + // Verify redeemer only has free product + expectProductV1Attached({ + customer: redeemerCus, + product: products.free, + status: CusProductStatus.Active, + }); + }); + + it("should advance test clock and verify referrer gets pro_amount discount on Premium cycle", async () => { + // Advance 21 more days (total 31 days from start) to trigger next billing cycle + // Coupon was applied on day 10, lasts 30 days, so should still be active on day 31 + await Promise.all( + testClockIds.map((x) => + advanceTestClock({ + testClockId: x, + numberOfDays: 31, + waitForSeconds: 25, + stripeCli, + }) + ) + ); + + // Test that main customer's Premium invoice has pro_amount discount applied + const mainCustomerWithInvoices = await autumn.customers.get( + mainCustomerId, + { + expand: [CusExpand.Invoices], + } + ); + + const premiumInvoice = mainCustomerWithInvoices.invoices.find((x) => + x.product_ids.includes(products.premium.id) + ); + if (premiumInvoice) { + // Premium costs $50, Pro costs $10 - so referrer should get $10 discount on Premium + // Expected: Premium ($50) - Pro amount ($10) = $40 + console.log(products.premium.prices); + const premiumPrice = products.premium.prices[0].config.amount; // $50 + const proAmount = products.pro.prices[0].config.amount; // $10 (pro_amount discount) + const expectedTotal = premiumPrice - proAmount; // $40 + + // The invoice total should be exactly Premium price minus pro_amount + assert.equal( + premiumInvoice.total, + expectedTotal, + `Premium invoice should be $40 (Premium $50 - Pro amount $10 discount). Got $${premiumInvoice.total}` + ); + + // Verify that the discount was applied (total is less than full Premium price) + assert.isBelow( + premiumInvoice.total, + premiumPrice, + "Referrer on Premium should get pro_amount discount, making it less than full Premium price" + ); + } + + const dbCustomers = await Promise.all( + [mainCustomerId, redeemer].map((x) => + CusService.getFull({ + db, + idOrInternalId: x, + orgId: org.id, + env, + inStatuses: [ + CusProductStatus.Active, + CusProductStatus.PastDue, + CusProductStatus.Expired, + ], + }) + ) + ); + + const expectedProducts = [ + [ + // Main referrer - keeps Premium with pro_amount discount applied + { name: "Free", status: CusProductStatus.Expired }, + { name: "Premium", status: CusProductStatus.Active }, + ], + [ + // Redeemer - only has free product (no reward in referrer-only program) + { name: "Free", status: CusProductStatus.Active }, + ], + ]; + + dbCustomers.forEach((customer, index) => { + const expectedProductsForCustomer = expectedProducts[index]; + expectedProductsForCustomer.forEach((expectedProduct) => { + const matchingProduct = customer.customer_products.find( + (cp) => + cp.product.name === expectedProduct.name && + cp.status === expectedProduct.status + ); + const unMatchedProduct = customer.customer_products.find( + (cp) => cp.product.name === expectedProduct.name + ); + + assert.exists( + matchingProduct, + `Customer ${customer.name} should have ${expectedProduct.name} product with status ${expectedProduct.status}. ${unMatchedProduct ? `However ${unMatchedProduct.product.name} with status ${unMatchedProduct.status} was found instead` : ""}` + ); + }); + }); + }); +}); diff --git a/server/tests/advanced/referrals/paid/referrals15.ts b/server/tests/advanced/referrals/paid/referrals15.ts new file mode 100644 index 000000000..59b884853 --- /dev/null +++ b/server/tests/advanced/referrals/paid/referrals15.ts @@ -0,0 +1,296 @@ +import { + type AppEnv, + CusExpand, + CusProductStatus, + ErrCode, + type Organization, + type ReferralCode, + type RewardRedemption, +} from "@autumn/shared"; +import { assert } from "chai"; +import chalk from "chalk"; +import type { Stripe } from "stripe"; +import { setupBefore } from "tests/before.js"; +import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js"; +import { advanceTestClock } from "tests/utils/stripeUtils.js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; +import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { CusService } from "@/internal/customers/CusService.js"; +import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { products, referralPrograms } from "../../../global.js"; + +export const group = "referrals15"; + +describe(`${chalk.yellowBright( + "referrals15: Testing referrals - referrer starts with no product, gets pro_amount discount - immediate, both - coupon-based" +)}`, () => { + const mainCustomerId = "main-referral-15"; + const redeemer = "referral15-r1"; + const redeemerPM = "success"; + const autumn: AutumnInt = new AutumnInt(); + let stripeCli: Stripe; + const testClockIds: string[] = []; + let referralCode: ReferralCode; + + let redemption: RewardRedemption; + let db: DrizzleCli; + let org: Organization; + let env: AppEnv; + + before(async function () { + await setupBefore(this); + stripeCli = this.stripeCli; + db = this.db; + org = this.org; + env = this.env; + + try { + await Promise.all([ + autumn.customers.delete(mainCustomerId, { deleteInStripe: true }), + autumn.customers.delete(redeemer, { deleteInStripe: true }), + RewardRedemptionService._resetCustomerRedemptions({ + db, + internalCustomerId: [mainCustomerId, redeemer], + }), + ]); + } catch {} + + // Initialize main customer with NO paid product (just free tier) + const res = await initCustomer({ + autumn: this.autumnJs, + customerId: mainCustomerId, + db, + org, + env, + attachPm: "success", + }); + + testClockIds.push(res.testClockId); + + const redeemerRes = await initCustomer({ + autumn: this.autumnJs, + customerId: redeemer, + db: this.db, + org: this.org, + env: this.env, + attachPm: redeemerPM, + withTestClock: true, + }); + + testClockIds.push(redeemerRes.testClockId); + }); + + it("should advance clock 10 days before redeeming", async () => { + // Advance 10 days after setup + await Promise.all( + testClockIds.map((x) => + advanceTestClock({ + testClockId: x, + numberOfDays: 10, + waitForSeconds: 10, + stripeCli, + }) + ) + ); + }); + + it("should create code once", async () => { + referralCode = await autumn.referrals.createCode({ + customerId: mainCustomerId, + referralId: referralPrograms.paidProductImmediateAll.id, + }); + + assert.exists(referralCode.code); + + // Get referral code again + const referralCode2 = await autumn.referrals.createCode({ + customerId: mainCustomerId, + referralId: referralPrograms.paidProductImmediateAll.id, + }); + + assert.equal(referralCode2.code, referralCode.code); + }); + + it("should create redemption for redeemer and fail if redeemed again", async () => { + redemption = await autumn.referrals.redeem({ + customerId: redeemer, + code: referralCode.code, + }); + + // Try redeem for redeemer again + try { + await autumn.referrals.redeem({ + customerId: redeemer, + code: referralCode.code, + }); + assert.fail("Should not be able to redeem again"); + } catch (error) { + assert.instanceOf(error, AutumnError); + assert.equal(error.code, ErrCode.CustomerAlreadyRedeemedReferralCode); + } + }); + + it("should have both referrer and redeemer get pro product", async () => { + const redemptionResult = await autumn.redemptions.get(redemption.id); + assert.equal(redemptionResult.redeemer_applied, true); + + const mainCus = await autumn.customers.get(mainCustomerId); + const redeemerCus = await autumn.customers.get(redeemer); + const mainProds = mainCus.products; + const redeemerProds = redeemerCus.products; + + // Main customer (referrer) should now have the pro product + assert.equal(mainProds.length, 1); + assert.equal(mainProds[0].id, products.pro.id); + + // Redeemer should also have the pro product (both get reward) + assert.equal(redeemerProds.length, 1); + assert.equal(redeemerProds[0].id, products.pro.id); + + expectProductV1Attached({ + customer: mainCus, + product: products.pro, + status: CusProductStatus.Active, + }); + + expectProductV1Attached({ + customer: redeemerCus, + product: products.pro, + status: CusProductStatus.Active, + }); + }); + + it("should advance test clock and verify both customers get pro_amount discount on Pro cycle", async () => { + // Advance 31 days from current time to trigger next billing cycle + // Coupon was applied on day 10, lasts 30 days, so should still be active on day 31 + await Promise.all( + testClockIds.map((x) => + advanceTestClock({ + testClockId: x, + numberOfDays: 31, + waitForSeconds: 25, + stripeCli, + }) + ) + ); + + // Test that both customers' Pro invoices have pro_amount discount applied + const [mainCustomerWithInvoices, redeemerWithInvoices] = await Promise.all([ + autumn.customers.get(mainCustomerId, { + expand: [CusExpand.Invoices], + }), + autumn.customers.get(redeemer, { + expand: [CusExpand.Invoices], + }), + ]); + + // console.log( + // "Main Customer Invoices:\n", + // mainCustomerWithInvoices.invoices + // .map( + // (x) => + // `${x.product_ids.join(", ")}: ${x.total} | ${new Date(x.created_at).toLocaleDateString()}`, + // ) + // .join("\n"), + // ); + + // console.log( + // "Redeemer Invoices:\n", + // redeemerWithInvoices.invoices + // .map( + // (x) => + // `${x.product_ids.join(", ")}: ${x.total} | ${new Date(x.created_at).toLocaleDateString()}`, + // ) + // .join("\n"), + // ); + + // Check main customer (referrer) invoice + const mainProInvoice = mainCustomerWithInvoices.invoices.find((x) => + x.product_ids.includes(products.pro.id) + ); + if (mainProInvoice) { + // Pro costs $10, so with pro_amount discount it should be $0 (Pro - Pro amount = $0) + const proPrice = products.pro.prices[0].config.amount; // $10 + const proAmount = products.pro.prices[0].config.amount; // $10 (pro_amount discount) + const expectedTotal = proPrice - proAmount; // $0 + + // console.log("Main customer expected total:", expectedTotal); + // console.log("Main customer Pro invoice total:", mainProInvoice.total); + + assert.equal( + mainProInvoice.total, + expectedTotal, + `Main customer Pro invoice should be $0 (Pro $10 - Pro amount $10 discount). Got $${mainProInvoice.total}` + ); + } + + // Check redeemer invoice + const redeemerProInvoice = redeemerWithInvoices.invoices.find((x) => + x.product_ids.includes(products.pro.id) + ); + if (redeemerProInvoice) { + // Pro costs $10, so with pro_amount discount it should be $0 (Pro - Pro amount = $0) + const proPrice = products.pro.prices[0].config.amount; // $10 + const proAmount = products.pro.prices[0].config.amount; // $10 (pro_amount discount) + const expectedTotal = proPrice - proAmount; // $0 + + // console.log("Redeemer expected total:", expectedTotal); + // console.log("Redeemer Pro invoice total:", redeemerProInvoice.total); + + assert.equal( + redeemerProInvoice.total, + expectedTotal, + `Redeemer Pro invoice should be $0 (Pro $10 - Pro amount $10 discount). Got $${redeemerProInvoice.total}` + ); + } + + const dbCustomers = await Promise.all( + [mainCustomerId, redeemer].map((x) => + CusService.getFull({ + db, + idOrInternalId: x, + orgId: org.id, + env, + inStatuses: [ + CusProductStatus.Active, + CusProductStatus.PastDue, + CusProductStatus.Expired, + ], + }) + ) + ); + + const expectedProducts = [ + [ + // Main referrer - has Pro with pro_amount discount applied + { name: "Free", status: CusProductStatus.Expired }, + { name: "Pro", status: CusProductStatus.Active }, + ], + [ + // Redeemer - also has Pro with pro_amount discount applied + { name: "Free", status: CusProductStatus.Expired }, + { name: "Pro", status: CusProductStatus.Active }, + ], + ]; + + dbCustomers.forEach((customer, index) => { + const expectedProductsForCustomer = expectedProducts[index]; + expectedProductsForCustomer.forEach((expectedProduct) => { + const matchingProduct = customer.customer_products.find( + (cp) => + cp.product.name === expectedProduct.name && + cp.status === expectedProduct.status + ); + const unMatchedProduct = customer.customer_products.find( + (cp) => cp.product.name === expectedProduct.name + ); + + assert.exists( + matchingProduct, + `Customer ${customer.name} should have ${expectedProduct.name} product with status ${expectedProduct.status}. ${unMatchedProduct ? `However ${unMatchedProduct.product.name} with status ${unMatchedProduct.status} was found instead` : ""}` + ); + }); + }); + }); +}); diff --git a/server/tests/advanced/referrals/paid/referrals16.ts b/server/tests/advanced/referrals/paid/referrals16.ts new file mode 100644 index 000000000..0b56bcab2 --- /dev/null +++ b/server/tests/advanced/referrals/paid/referrals16.ts @@ -0,0 +1,351 @@ +// import { +// type AppEnv, +// CusExpand, +// CusProductStatus, +// ErrCode, +// type Organization, +// type ReferralCode, +// type RewardRedemption, +// } from "@autumn/shared"; +// import { assert } from "chai"; +// import chalk from "chalk"; +// import type { Stripe } from "stripe"; +// import { setupBefore } from "tests/before.js"; +// import { expectProductV1Attached } from "tests/utils/expectUtils/expectProductAttached.js"; +// import { +// advanceTestClock, +// completeCheckoutForm, +// } from "tests/utils/stripeUtils.js"; +// import type { DrizzleCli } from "@/db/initDrizzle.js"; +// import AutumnError, { AutumnInt } from "@/external/autumn/autumnCli.js"; +// import { CusService } from "@/internal/customers/CusService.js"; +// import { RewardRedemptionService } from "@/internal/rewards/RewardRedemptionService.js"; +// import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +// import { products, referralPrograms, rewards } from "../../../global.js"; + +// export const group = "referrals16"; + +// describe(`${chalk.yellowBright( +// "referrals16: Testing referrals - referrer starts with no product, gets pro_amount discount - checkout, both - coupon-based" +// )}`, () => { +// const mainCustomerId = "main-referral-16"; +// const redeemer = "referral16-r1"; +// const redeemerPM = "success"; +// const autumn: AutumnInt = new AutumnInt(); +// let stripeCli: Stripe; +// const testClockIds: string[] = []; +// let referralCode: ReferralCode; + +// let redemption: RewardRedemption; +// let db: DrizzleCli; +// let org: Organization; +// let env: AppEnv; + +// before(async function () { +// await setupBefore(this); +// stripeCli = this.stripeCli; +// db = this.db; +// org = this.org; +// env = this.env; + +// try { +// await Promise.all([ +// autumn.customers.delete(mainCustomerId, { deleteInStripe: true }), +// autumn.customers.delete(redeemer, { deleteInStripe: true }), +// RewardRedemptionService._resetCustomerRedemptions({ +// db, +// internalCustomerId: [mainCustomerId, redeemer], +// }), +// ]); +// } catch {} + +// // Initialize main customer with NO paid product (just free tier) +// const res = await initCustomer({ +// autumn: this.autumnJs, +// customerId: mainCustomerId, +// db, +// org, +// env, +// attachPm: "success", +// }); + +// testClockIds.push(res.testClockId); + +// const redeemerRes = await initCustomer({ +// autumn: this.autumnJs, +// customerId: redeemer, +// db: this.db, +// org: this.org, +// env: this.env, +// attachPm: redeemerPM, +// withTestClock: true, +// }); + +// testClockIds.push(redeemerRes.testClockId); +// }); + +// it("should advance clock 10 days before redeeming", async () => { +// // Advance 10 days after setup +// await Promise.all( +// testClockIds.map((x) => +// advanceTestClock({ +// testClockId: x, +// numberOfDays: 10, +// waitForSeconds: 10, +// stripeCli, +// }) +// ) +// ); +// }); + +// it("should create code once", async () => { +// referralCode = await autumn.referrals.createCode({ +// customerId: mainCustomerId, +// referralId: referralPrograms.paidProductCheckoutAll.id, +// }); + +// assert.exists(referralCode.code); + +// // Get referral code again +// const referralCode2 = await autumn.referrals.createCode({ +// customerId: mainCustomerId, +// referralId: referralPrograms.paidProductCheckoutAll.id, +// }); + +// assert.equal(referralCode2.code, referralCode.code); +// }); + +// it("should create redemption for redeemer and fail if redeemed again", async () => { +// redemption = await autumn.referrals.redeem({ +// customerId: redeemer, +// code: referralCode.code, +// }); + +// // Try redeem for redeemer again +// try { +// await autumn.referrals.redeem({ +// customerId: redeemer, +// code: referralCode.code, +// }); +// assert.fail("Should not be able to redeem again"); +// } catch (error) { +// assert.instanceOf(error, AutumnError); +// assert.equal(error.code, ErrCode.CustomerAlreadyRedeemedReferralCode); +// } +// }); + +// it("should have referrer and redeemer still on free tier (reward not triggered yet)", async () => { +// const redemptionResult = await autumn.redemptions.get(redemption.id); +// assert.equal(redemptionResult.triggered, false); // Checkout trigger not fired yet + +// const mainCus = await autumn.customers.get(mainCustomerId); +// const redeemerCus = await autumn.customers.get(redeemer); +// const mainProds = mainCus.products; +// const redeemerProds = redeemerCus.products; + +// // Both customers should still only have free product +// assert.equal(mainProds.length, 1); +// assert.equal(mainProds[0].id, products.free.id); + +// assert.equal(redeemerProds.length, 1); +// assert.equal(redeemerProds[0].id, products.free.id); + +// expectProductV1Attached({ +// customer: mainCus, +// product: products.free, +// status: CusProductStatus.Active, +// }); + +// expectProductV1Attached({ +// customer: redeemerCus, +// product: products.free, +// status: CusProductStatus.Active, +// }); +// }); + +// it("should trigger reward when redeemer checks out with Premium", async () => { +// // Redeemer purchases Premium product (triggers checkout reward) +// const checkoutRes = await autumn.attach({ +// customer_id: redeemer, +// product_id: products.premium.id, +// force_checkout: true, +// }); + +// await completeCheckoutForm(checkoutRes.checkout_url); + +// // Wait a bit for webhook processing +// await new Promise((resolve) => setTimeout(resolve, 10000)); + +// // Now both customers should have the reward applied +// const redemptionResult = await autumn.redemptions.get(redemption.id); + +// assert.equal(redemptionResult.applied, true); + +// const mainCus = await autumn.customers.get(mainCustomerId); +// const redeemerCus = await autumn.customers.get(redeemer); +// const mainProds = mainCus.products; +// const redeemerProds = redeemerCus.products; + +// // Main customer (referrer) should now have the pro product +// assert.equal(mainProds.length, 1); +// assert.equal(mainProds[0].id, products.pro.id); + +// // Redeemer should have both Premium (purchased) and the Pro price discount +// assert.equal(redeemerProds.length, 1); +// const redeemerStripeDiscounts = await stripeCli.subscriptions.retrieve( +// redeemerProds.find((x) => x.id === products.premium.id) +// ?.subscription_ids?.[0]!, +// { +// expand: ["discounts"], +// } +// ); + +// const parsedDiscountID = redeemerStripeDiscounts.discounts.find((x) => { +// if (typeof x === "string") { +// return x; +// } else if (typeof x === "object") { +// return x.coupon.id; +// } else return null; +// })!; + +// assert.equal( +// redeemerStripeDiscounts.discounts.length, +// 1, +// `Redeemer Stripe Discounts: ${JSON.stringify(redeemerStripeDiscounts.discounts, null, 4)}` +// ); +// assert.equal( +// typeof parsedDiscountID === "object" +// ? parsedDiscountID.coupon.id +// : parsedDiscountID, +// rewards.paidProductWithConfig.id, +// `Parsed Discount ID: ${parsedDiscountID}` +// ); + +// assert.exists( +// redeemerProds.find((x) => x.id === products.premium.id), +// `Redeemer must have Premium product` +// ); + +// expectProductV1Attached({ +// customer: mainCus, +// product: products.pro, +// status: CusProductStatus.Active, +// }); + +// expectProductV1Attached({ +// customer: redeemerCus, +// product: products.premium, +// status: CusProductStatus.Active, +// }); +// }); + +// it("should advance test clock and verify both customers get pro_amount discount on their cycles", async () => { +// // Advance 31 days from current time to trigger next billing cycle +// await Promise.all( +// testClockIds.map((x) => +// advanceTestClock({ +// testClockId: x, +// numberOfDays: 31, +// waitForSeconds: 25, +// stripeCli, +// }) +// ) +// ); + +// // Test that both customers' invoices have pro_amount discount applied +// const [mainCustomerWithInvoices, redeemerWithInvoices] = await Promise.all([ +// autumn.customers.get(mainCustomerId, { +// expand: [CusExpand.Invoices], +// }), +// autumn.customers.get(redeemer, { +// expand: [CusExpand.Invoices], +// }), +// ]); + +// // Check main customer (referrer) Pro invoice +// const mainProInvoice = mainCustomerWithInvoices.invoices.find((x) => +// x.product_ids.includes(products.pro.id) +// ); +// if (mainProInvoice) { +// // Pro costs $10, so with pro_amount discount it should be $0 (Pro - Pro amount = $0) +// const proPrice = products.pro.prices[0].config.amount; // $10 +// const proAmount = products.pro.prices[0].config.amount; // $10 (pro_amount discount) +// const expectedTotal = proPrice - proAmount; // $0 + +// // console.log("Main customer expected total:", expectedTotal); +// // console.log("Main customer Pro invoice total:", mainProInvoice.total); + +// assert.equal( +// mainProInvoice.total, +// expectedTotal, +// `Main customer Pro invoice should be $0 (Pro $10 - Pro amount $10 discount). Got $${mainProInvoice.total}` +// ); +// } + +// // Check redeemer Premium invoice (should have $10 off) +// const redeemerPremiumInvoice = redeemerWithInvoices.invoices.find((x) => +// x.product_ids.includes(products.premium.id) +// ); +// if (redeemerPremiumInvoice) { +// // Premium costs $50, so with pro_amount discount it should be $40 (Premium - Pro amount = $40) +// const premiumPrice = products.premium.prices[0].config.amount; // $50 +// const proAmount = products.pro.prices[0].config.amount; // $10 (pro_amount discount) +// const expectedTotal = premiumPrice - proAmount; // $40 + +// assert.equal( +// redeemerPremiumInvoice.total, +// expectedTotal, +// `Redeemer Premium invoice should be $40 (Premium $50 - Pro amount $10 discount). Got $${redeemerPremiumInvoice.total}` +// ); +// } + +// const dbCustomers = await Promise.all( +// [mainCustomerId, redeemer].map((x) => +// CusService.getFull({ +// db, +// idOrInternalId: x, +// orgId: org.id, +// env, +// inStatuses: [ +// CusProductStatus.Active, +// CusProductStatus.PastDue, +// CusProductStatus.Expired, +// ], +// }) +// ) +// ); + +// const expectedProducts = [ +// [ +// // Main referrer - has Pro with pro_amount discount applied +// { name: "Free", status: CusProductStatus.Expired }, +// { name: "Pro", status: CusProductStatus.Active }, +// ], +// [ +// // Redeemer - has both Premium (purchased) and Pro (reward) with discounts +// { name: "Free", status: CusProductStatus.Expired }, +// { name: "Pro", status: CusProductStatus.Active }, +// { name: "Premium", status: CusProductStatus.Active }, +// ], +// ]; + +// dbCustomers.forEach((customer, index) => { +// const expectedProductsForCustomer = expectedProducts[index]; +// expectedProductsForCustomer.forEach((expectedProduct) => { +// const matchingProduct = customer.customer_products.find( +// (cp) => +// cp.product.name === expectedProduct.name && +// cp.status === expectedProduct.status +// ); +// const unMatchedProduct = customer.customer_products.find( +// (cp) => cp.product.name === expectedProduct.name +// ); + +// assert.exists( +// matchingProduct, +// `Customer ${customer.name} should have ${expectedProduct.name} product with status ${expectedProduct.status}. ${unMatchedProduct ? `However ${unMatchedProduct.product.name} with status ${unMatchedProduct.status} was found instead` : ""}` +// ); +// }); +// }); +// }); +// }); diff --git a/server/tests/attach/basic/basic3.ts b/server/tests/attach/basic/basic3.ts index 69d032bce..eba70ead9 100644 --- a/server/tests/attach/basic/basic3.ts +++ b/server/tests/attach/basic/basic3.ts @@ -126,6 +126,7 @@ describe(`${chalk.yellowBright("basic3: Testing attach one time / monthly add on expectedAmt, "add on balance should be correct" ); + expect(cusRes.add_ons).to.have.lengthOf( 1, "should only have one add on product after two purchases (since they combine)" diff --git a/server/tests/global.ts b/server/tests/global.ts index 7f4465065..3e8ea98ff 100644 --- a/server/tests/global.ts +++ b/server/tests/global.ts @@ -1,5 +1,7 @@ import dotenv from "dotenv"; + dotenv.config(); + import { AggregateType, AllowanceType, @@ -7,26 +9,25 @@ import { BillingInterval, CouponDurationType, EntInterval, - Feature, + type Feature, + FeatureType, FeatureUsageType, RewardReceivedBy, RewardTriggerEvent, RewardType, } from "@autumn/shared"; -import { FeatureType } from "@autumn/shared"; +import { initDrizzle } from "@/db/initDrizzle.js"; +import { FeatureService } from "@/internal/features/FeatureService.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; import { - initReward, initEntitlement, initFeature, initFreeTrial, initPrice, initProduct, + initReward, initRewardProgram, } from "./utils/init.js"; -import { createSupabaseClient } from "@/external/supabaseUtils.js"; -import { OrgService } from "@/internal/orgs/OrgService.js"; -import { initDrizzle } from "@/db/initDrizzle.js"; -import { FeatureService } from "@/internal/features/FeatureService.js"; export const features: Record = { boolean1: initFeature({ @@ -325,6 +326,26 @@ export const products = { freeTrial: null, isAddOn: true, }), + + proAddOn: initProduct({ + id: "proAddOn", + entitlements: { + metered1: initEntitlement({ + feature: features.metered1, + allowance: 100, + interval: EntInterval.Lifetime, + }), + }, + prices: [ + initPrice({ + type: "fixed_cycle", + billingInterval: BillingInterval.OneOff, + amount: 100, + }), + ], + freeTrial: null, + isAddOn: true, + }), }; export const oneTimeProducts = { @@ -821,6 +842,20 @@ export const rewards = { durationType: CouponDurationType.Months, durationValue: 1, }), + paidProductWithConfig: initReward({ + id: "paidProductWithConfig", + type: RewardType.FreeProduct, + freeProductId: products.pro.id, + freeProductConfig: { + durationType: CouponDurationType.Months, + durationValue: 1, + }, + }), + paidProductAddOn: initReward({ + id: "paidProductAddOn", + type: RewardType.FreeProduct, + freeProductId: products.proAddOn.id, + }), freeProduct: initReward({ id: "freeProduct", type: RewardType.FreeProduct, @@ -829,6 +864,13 @@ export const rewards = { }; export const referralPrograms = { + freeProduct: initRewardProgram({ + id: "freeProduct", + internalRewardId: rewards.freeProduct.id, + when: RewardTriggerEvent.Checkout, + receivedBy: RewardReceivedBy.All, + productIds: [products.pro.id, products.proWithTrial.id], + }), onCheckout: initRewardProgram({ id: "onCheckout", internalRewardId: rewards.monthOff.id, @@ -840,12 +882,66 @@ export const referralPrograms = { internalRewardId: rewards.monthOff.id, when: RewardTriggerEvent.CustomerCreation, }), - freeProduct: initRewardProgram({ - id: "freeProduct", - internalRewardId: rewards.freeProduct.id, + paidProductImmediateAll: initRewardProgram({ + id: "paidProduct-immediate-all", + internalRewardId: rewards.paidProductWithConfig.id, + when: RewardTriggerEvent.CustomerCreation, + receivedBy: RewardReceivedBy.All, + productIds: [products.pro.id], + maxRedemptions: 100, + }), + paidProductImmediateReferrer: initRewardProgram({ + id: "paidProduct-immediate-referrer", + internalRewardId: rewards.paidProductWithConfig.id, + when: RewardTriggerEvent.CustomerCreation, + receivedBy: RewardReceivedBy.Referrer, + productIds: [products.pro.id], + maxRedemptions: 100, + }), + + paidProductCheckoutAll: initRewardProgram({ + id: "paidProduct-checkout-all", + internalRewardId: rewards.paidProductWithConfig.id, when: RewardTriggerEvent.Checkout, receivedBy: RewardReceivedBy.All, - productIds: [products.pro.id, products.proWithTrial.id], + productIds: [products.premium.id], + }), + paidProductCheckoutReferrer: initRewardProgram({ + id: "paidProduct-checkout-referrer", + internalRewardId: rewards.paidProductWithConfig.id, + when: RewardTriggerEvent.Checkout, + receivedBy: RewardReceivedBy.Referrer, + productIds: [products.premium.id], + }), + + paidAddOnAll: initRewardProgram({ + id: "paidAddOn-all", + internalRewardId: rewards.paidProductAddOn.id, + when: RewardTriggerEvent.CustomerCreation, + receivedBy: RewardReceivedBy.All, + productIds: [products.proAddOn.id], + }), + paidAddOnReferrer: initRewardProgram({ + id: "paidAddOn-referrer", + internalRewardId: rewards.paidProductAddOn.id, + when: RewardTriggerEvent.CustomerCreation, + receivedBy: RewardReceivedBy.Referrer, + productIds: [products.proAddOn.id], + }), + + paidAddOnCheckoutAll: initRewardProgram({ + id: "paidAddOn-checkout-all", + internalRewardId: rewards.paidProductAddOn.id, + when: RewardTriggerEvent.Checkout, + receivedBy: RewardReceivedBy.All, + productIds: [products.premium.id], + }), + paidAddOnCheckoutReferrer: initRewardProgram({ + id: "paidAddOn-checkout-referrer", + internalRewardId: rewards.paidProductAddOn.id, + when: RewardTriggerEvent.Checkout, + receivedBy: RewardReceivedBy.Referrer, + productIds: [products.premium.id], }), }; @@ -854,7 +950,7 @@ const ORG_SLUG = process.env.TESTS_ORG!; before(async function () { try { this.env = AppEnv.Sandbox; - let { db, client } = initDrizzle(); + const { db, client } = initDrizzle(); this.db = db; this.client = client; @@ -871,8 +967,8 @@ before(async function () { const cleanFeatures = (features: Record) => { for (const featureId in features) { - let feature = features[featureId as keyof typeof features]; - let dbFeature = dbFeatures.find((f: any) => f.id === feature.id); + const feature = features[featureId as keyof typeof features]; + const dbFeature = dbFeatures.find((f: any) => f.id === feature.id); if (!dbFeature) { // throw new Error(`Feature ${feature.id} not found`); continue; @@ -881,7 +977,7 @@ before(async function () { dbFeature.internal_id; if (feature.type === FeatureType.Metered) { // Ignore this for now - // @ts-ignore eventName is manually set + // @ts-expect-error eventName is manually set features[featureId as keyof typeof features].eventName = dbFeature.config?.filters[0].value[0]; } diff --git a/server/tests/merged/add/mergedAdd2.test.ts b/server/tests/merged/add/mergedAdd2.test.ts index 6b7373748..b3f21d47f 100644 --- a/server/tests/merged/add/mergedAdd2.test.ts +++ b/server/tests/merged/add/mergedAdd2.test.ts @@ -9,17 +9,7 @@ import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; import { APIVersion, AppEnv, entities, Organization } from "@autumn/shared"; import { constructArrearItem } from "@/utils/scriptUtils/constructItem.js"; import { DrizzleCli } from "@/db/initDrizzle.js"; -import { - addPrefixToProducts, - getBasePrice, -} from "tests/utils/testProductUtils/testProductUtils.js"; -import { expect } from "chai"; -import { expectSubToBeCorrect } from "../mergeUtils.test.js"; - -import { getAttachPreviewTotal } from "tests/utils/testAttachUtils/getAttachPreviewTotal.js"; -import { advanceToNextInvoice } from "tests/utils/testAttachUtils/testAttachUtils.js"; -import { getExpectedInvoiceTotal } from "tests/utils/expectUtils/expectInvoiceUtils.js"; -import { timeout } from "@/utils/genUtils.js"; +import { addPrefixToProducts } from "tests/utils/testProductUtils/testProductUtils.js"; // UNCOMMENT FROM HERE let premium = constructProduct({ diff --git a/server/tests/merged/add/mergedAdd3.test.ts b/server/tests/merged/add/mergedAdd3.test.ts new file mode 100644 index 000000000..8626ab2aa --- /dev/null +++ b/server/tests/merged/add/mergedAdd3.test.ts @@ -0,0 +1,155 @@ +import chalk from "chalk"; +import { setupBefore } from "tests/before.js"; +import { Stripe } from "stripe"; +import { createProducts } from "tests/utils/productUtils.js"; +import { constructProduct } from "@/utils/scriptUtils/createTestProducts.js"; +import { TestFeature } from "tests/setup/v2Features.js"; +import { AutumnInt } from "@/external/autumn/autumnCli.js"; +import { initCustomer } from "@/utils/scriptUtils/initCustomer.js"; +import { + APIVersion, + AppEnv, + CusProductStatus, + entities, + Organization, +} from "@autumn/shared"; +import { + constructArrearItem, + constructFeatureItem, +} from "@/utils/scriptUtils/constructItem.js"; +import { DrizzleCli } from "@/db/initDrizzle.js"; +import { addPrefixToProducts } from "tests/utils/testProductUtils/testProductUtils.js"; +import { attachAndExpectCorrect } from "tests/utils/expectUtils/expectAttach.js"; +import { expectProductAttached } from "tests/utils/expectUtils/expectProductAttached.js"; + +// UNCOMMENT FROM HERE +let premium = constructProduct({ + id: "premium", + items: [constructFeatureItem({ featureId: TestFeature.Words })], + type: "premium", +}); +let pro = constructProduct({ + id: "pro", + items: [constructFeatureItem({ featureId: TestFeature.Words })], + type: "pro", +}); + +const ops = [ + { + entityId: "1", + product: premium, + results: [{ product: premium, status: CusProductStatus.Active }], + }, + { + entityId: "2", + product: premium, + results: [{ product: premium, status: CusProductStatus.Active }], + }, + { + entityId: "1", + product: pro, + results: [ + { product: premium, status: CusProductStatus.Active }, + { product: pro, status: CusProductStatus.Scheduled }, + ], + }, + { + entityId: "3", + product: premium, + results: [{ product: premium, status: CusProductStatus.Active }], + }, +]; + +const testCase = "mergedAdd3"; +describe(`${chalk.yellowBright(`${testCase}: Testing scheduled, and merged add to subscription`)}`, () => { + let customerId = testCase; + let autumn: AutumnInt = new AutumnInt({ version: APIVersion.v1_4 }); + + let stripeCli: Stripe; + let testClockId: string; + let curUnix: number; + let db: DrizzleCli; + let org: Organization; + let env: AppEnv; + + before(async function () { + await setupBefore(this); + const { autumnJs } = this; + db = this.db; + org = this.org; + env = this.env; + + stripeCli = this.stripeCli; + + addPrefixToProducts({ + products: [premium, pro], + prefix: testCase, + }); + + await createProducts({ + autumn: autumnJs, + products: [premium, pro], + db, + orgId: org.id, + env, + customerId, + }); + + const { testClockId: testClockId1 } = await initCustomer({ + autumn: autumnJs, + customerId, + db, + org, + env, + attachPm: "success", + }); + + testClockId = testClockId1!; + }); + + const entities = [ + { + id: "1", + name: "Entity 1", + feature_id: TestFeature.Users, + }, + { + id: "2", + name: "Entity 2", + feature_id: TestFeature.Users, + }, + { + id: "3", + name: "Entity 3", + feature_id: TestFeature.Users, + }, + ]; + + it("should run operations", async function () { + await autumn.entities.create(customerId, entities); + + for (let index = 0; index < ops.length; index++) { + const op = ops[index]; + await attachAndExpectCorrect({ + autumn, + customerId, + product: op.product, + stripeCli, + db, + org, + env, + entities, + entityId: op.entityId, + }); + + for (const result of op.results) { + const entity = await autumn.entities.get(customerId, op.entityId); + expectProductAttached({ + customer: entity, + product: result.product, + status: result.status, + }); + } + } + }); +}); diff --git a/server/tests/utils/expectUtils/expectProductAttached.ts b/server/tests/utils/expectUtils/expectProductAttached.ts index ed02da03f..d64da0d09 100644 --- a/server/tests/utils/expectUtils/expectProductAttached.ts +++ b/server/tests/utils/expectUtils/expectProductAttached.ts @@ -1,137 +1,197 @@ -import { notNullish, nullish } from "@/utils/genUtils.js"; -import { CusProductStatus, ProductV2 } from "@autumn/shared"; -import { Customer } from "autumn-js"; +import { + type CreateFreeTrial, + CusProductStatus, + type Entitlement, + type ProductV2, +} from "@autumn/shared"; +import type { Customer, ProductItem } from "autumn-js"; import { expect } from "chai"; -import { Decimal } from "decimal.js"; export const expectProductAttached = ({ - customer, - product, - productId, - status, - entityId, - isCanceled = false, - quantity, + customer, + product, + productId, + status, + entityId, + isCanceled = false, + quantity, }: { - customer: Customer; - product?: ProductV2; - productId?: string; - status?: CusProductStatus; - entityId?: string; - isCanceled?: boolean; - quantity?: number; + customer: Customer; + product?: ProductV2; + productId?: string; + status?: CusProductStatus; + entityId?: string; + isCanceled?: boolean; + quantity?: number; }) => { - const cusProducts = customer.products; - const finalProductId = productId || product?.id; - const productAttached = cusProducts.find( - (p) => - p.id === finalProductId && (entityId ? p.entity_id === entityId : true) - ); + const cusProducts = customer.products; + const finalProductId = productId || product?.id; + const productAttached = cusProducts.find( + (p) => + p.id === finalProductId && (entityId ? p.entity_id === entityId : true), + ); - if (!productAttached) { - console.log(`product ${finalProductId} not attached`); - console.log(cusProducts); - } + if (!productAttached) { + console.log(`product ${finalProductId} not attached`); + console.log(cusProducts); + } - expect(productAttached, `product ${finalProductId} is attached`).to.exist; + expect(productAttached, `product ${finalProductId} is attached`).to.exist; - if (status) { - expect(productAttached?.status).to.equal( - status, - `product ${finalProductId} should have status ${status}` - ); - } else { - expect( - productAttached?.status, - `product ${finalProductId} is not expired` - ).to.not.equal(CusProductStatus.Expired); - } + if (status) { + expect(productAttached?.status).to.equal( + status, + `product ${finalProductId} should have status ${status}`, + ); + } else { + expect( + productAttached?.status, + `product ${finalProductId} is not expired`, + ).to.not.equal(CusProductStatus.Expired); + } - if (quantity) { - // @ts-ignore - expect(productAttached?.quantity).to.equal(quantity); - } + if (quantity) { + // @ts-expect-error + expect(productAttached?.quantity).to.equal(quantity); + } - if (entityId) { - // @ts-ignore - expect(productAttached?.entity_id).to.equal(entityId); - } + if (entityId) { + expect(productAttached?.entity_id).to.equal(entityId); + } - if (isCanceled) { - expect(productAttached?.canceled_at).to.exist; - // @ts-ignore - // expect(productAttached?.canceled).to.be.true; - } + if (isCanceled) { + expect(productAttached?.canceled_at).to.exist; + // expect(productAttached?.canceled).to.be.true; + } }; +export const expectProductV1Attached = ({ + customer, + product, + productId, + status, + entityId, +}: { + customer: Customer; + product: { + id: string; + isDefault?: boolean; + isAddOn?: boolean; + items?: Record; + entitlements: Record; + prices: any[]; + freeTrial?: CreateFreeTrial; + group?: string; + }; + productId?: string; + status?: CusProductStatus; + entityId?: string; +}) => { + const cusProducts = customer.products; + const finalProductId = productId || product?.id; + const productAttached = cusProducts.find( + (p) => + p.id === finalProductId && (entityId ? p.entity_id === entityId : true), + ); + + expect(productAttached, `product ${finalProductId} is attached`).to.exist; + + if (status) { + expect(productAttached?.status).to.equal( + status, + `product ${finalProductId} should have status ${status}`, + ); + } +}; + +export const expectAddOnAttached = ({ + customer, + productId, + status, +}: { + customer: Customer & { add_ons: { + id: string; + status: CusProductStatus; + }[] }; + productId: string; + status?: CusProductStatus; +}) => { + const addOn = customer.add_ons.find((a) => a.id === productId); + expect(addOn, `add on ${productId} is attached`).to.exist; + + if (status) { + expect(addOn?.status).to.equal(status, `add on ${productId} should have status ${status}`); + } +} + export const expectInvoicesCorrect = ({ - customer, - first, - // second, + customer, + first, + // second, }: { - customer: Customer; - first: { - productId: string; - total: number; - }; - // second?: { - // productId: string; - // total: number; - // }; + customer: Customer; + first: { + productId: string; + total: number; + }; + // second?: { + // productId: string; + // total: number; + // }; }) => { - const invoices = customer.invoices; - if (!invoices) { - console.log(`invoices is nullish`); - } + const invoices = customer.invoices; + if (!invoices) { + console.log(`invoices is nullish`); + } - try { - expect(invoices![0].total).to.approximately( - first.total, - 0.01, - `invoice total is correct: ${first.total}` - ); + try { + expect(invoices![0].total).to.approximately( + first.total, + 0.01, + `invoice total is correct: ${first.total}`, + ); - expect(invoices![0].product_ids).to.include( - first.productId, - `invoice includes product ${first.productId}` - ); - } catch (error) { - console.log(`invoice for ${first.productId}, ${first.total} not found`); - throw error; - } + expect(invoices![0].product_ids).to.include( + first.productId, + `invoice includes product ${first.productId}`, + ); + } catch (error) { + console.log(`invoice for ${first.productId}, ${first.total} not found`); + throw error; + } - // if (first) { + // if (first) { - // } + // } - // if (second) { - // const totalAmount = new Decimal(invoices![0].total) - // .plus(invoices![1].total) - // .toDecimalPlaces(2) - // .toNumber(); - // // console.log("First invoice:", invoices![0].total, invoices![0].product_ids); - // // console.log( - // // "Second invoice:", - // // invoices![1].total, - // // invoices![1].product_ids, - // // ); - // try { - // expect(totalAmount).to.approximately( - // second.total, - // 0.01, - // `first & second invoice total should sum to ${second.total}`, - // ); - // expect( - // invoices![0].product_ids.includes(second.productId), - // `invoice 1 includes product ${second.productId}`, - // ).to.be.true; - // expect( - // invoices![1].product_ids.includes(second.productId), - // `invoice 2 includes product ${second.productId}`, - // ).to.be.true; - // } catch (error) { - // console.log(`invoice for ${second.productId}, ${second.total} not found`); - // throw error; - // } - // } + // if (second) { + // const totalAmount = new Decimal(invoices![0].total) + // .plus(invoices![1].total) + // .toDecimalPlaces(2) + // .toNumber(); + // // console.log("First invoice:", invoices![0].total, invoices![0].product_ids); + // // console.log( + // // "Second invoice:", + // // invoices![1].total, + // // invoices![1].product_ids, + // // ); + // try { + // expect(totalAmount).to.approximately( + // second.total, + // 0.01, + // `first & second invoice total should sum to ${second.total}`, + // ); + // expect( + // invoices![0].product_ids.includes(second.productId), + // `invoice 1 includes product ${second.productId}`, + // ).to.be.true; + // expect( + // invoices![1].product_ids.includes(second.productId), + // `invoice 2 includes product ${second.productId}`, + // ).to.be.true; + // } catch (error) { + // console.log(`invoice for ${second.productId}, ${second.total} not found`); + // throw error; + // } + // } }; diff --git a/server/tests/utils/init.ts b/server/tests/utils/init.ts index 917b63358..0d15bf37e 100644 --- a/server/tests/utils/init.ts +++ b/server/tests/utils/init.ts @@ -1,435 +1,441 @@ import { - AggregateType, - AllowanceType, - AppEnv, - BillingInterval, - BillWhen, - CouponDurationType, - CreateFreeTrial, - EntInterval, - Entitlement, - Feature, - FeatureType, - FeatureUsageType, - FreeTrialDuration, - Organization, - PriceType, - ProductItem, - RewardReceivedBy, - RewardTriggerEvent, - RewardType, + AggregateType, + AllowanceType, + type AppEnv, + BillingInterval, + BillWhen, + CouponDurationType, + type CreateFreeTrial, + EntInterval, + type Entitlement, + type Feature, + FeatureType, + FeatureUsageType, FreeTrialDuration, + type Organization, + PriceType, + type ProductItem, + RewardReceivedBy, + RewardTriggerEvent, + RewardType } from "@autumn/shared"; - -import { getAxiosInstance } from "./setup.js"; -import { SupabaseClient } from "@supabase/supabase-js"; +import type { DrizzleCli } from "@/db/initDrizzle.js"; import { attachPmToCus } from "@/external/stripe/stripeCusUtils.js"; import { generateId } from "@/utils/genUtils.js"; -import { DrizzleCli } from "@/db/initDrizzle.js"; +import { getAxiosInstance } from "./setup.js"; export const keyToTitle = (key: string) => { - return key - .replace(/[_-]/g, " ") - .replace(/\b\w/g, (char) => char.toUpperCase()); + return key + .replace(/[_-]/g, " ") + .replace(/\b\w/g, (char) => char.toUpperCase()); }; export const initFeature = ({ - id, - type, - creditSchema = [], - aggregateType = AggregateType.Sum, - groupBy = "", - eventName, - usageType = FeatureUsageType.Single, + id, + type, + creditSchema = [], + aggregateType = AggregateType.Sum, + groupBy = "", + eventName, + usageType = FeatureUsageType.Single, }: { - id: string; - type: FeatureType; - creditSchema?: { - metered_feature_id: string; - feature_amount: number; - credit_amount: number; - }[]; - aggregateType?: AggregateType; - groupBy?: string; - eventName?: string; - usageType?: FeatureUsageType; + id: string; + type: FeatureType; + creditSchema?: { + metered_feature_id: string; + feature_amount: number; + credit_amount: number; + }[]; + aggregateType?: AggregateType; + groupBy?: string; + eventName?: string; + usageType?: FeatureUsageType; }): (Feature & { eventName: string }) | any => { - const name = keyToTitle(id); + const name = keyToTitle(id); - if (type == FeatureType.Boolean) { - return { - id, - name, - type, - } as Feature; - } + if (type == FeatureType.Boolean) { + return { + id, + name, + type, + } as Feature; + } - if (type == FeatureType.Metered) { - return { - id, - name, - type, - config: { - filters: [ - { - value: eventName ? [eventName] : [id], - property: "", - operator: "", - }, - ], - aggregate: { - type: aggregateType, - property: "value", - }, - usage_type: usageType, - // group_by: groupBy - // ? { - // property: groupBy, - // } - // : undefined, - }, - } as Feature; - } + if (type == FeatureType.Metered) { + return { + id, + name, + type, + config: { + filters: [ + { + value: eventName ? [eventName] : [id], + property: "", + operator: "", + }, + ], + aggregate: { + type: aggregateType, + property: "value", + }, + usage_type: usageType, + // group_by: groupBy + // ? { + // property: groupBy, + // } + // : undefined, + }, + } as Feature; + } - if (type == FeatureType.CreditSystem) { - return { - id, - name, - type, - config: { - schema: creditSchema, - usage_type: FeatureUsageType.Single, - }, - } as Feature; - } + if (type == FeatureType.CreditSystem) { + return { + id, + name, + type, + config: { + schema: creditSchema, + usage_type: FeatureUsageType.Single, + }, + } as Feature; + } - throw new Error(`Invalid feature type: ${type}`); + throw new Error(`Invalid feature type: ${type}`); }; export const initEntitlement = ({ - feature, - allowance, - interval = EntInterval.Month, - allowanceType = AllowanceType.Fixed, - entityFeatureId, - carryFromPrevious = false, + feature, + allowance, + interval = EntInterval.Month, + allowanceType = AllowanceType.Fixed, + entityFeatureId, + carryFromPrevious = false, }: { - feature: Feature; - allowance?: number; - interval?: EntInterval; - allowanceType?: AllowanceType; - entityFeatureId?: string; - carryFromPrevious?: boolean; + feature: Feature; + allowance?: number; + interval?: EntInterval; + allowanceType?: AllowanceType; + entityFeatureId?: string; + carryFromPrevious?: boolean; }) => { - if (feature.type == FeatureType.Boolean) { - return { - feature_id: feature.id, - internal_feature_id: feature.internal_id, - } as Entitlement; - } + if (feature.type == FeatureType.Boolean) { + return { + feature_id: feature.id, + internal_feature_id: feature.internal_id, + } as Entitlement; + } - const isUnlimitedOrNone = - allowanceType == AllowanceType.Unlimited || allowance == null; + const isUnlimitedOrNone = + allowanceType == AllowanceType.Unlimited || allowance == null; - return { - feature_id: feature.id, - internal_feature_id: feature.internal_id, - allowance_type: allowanceType, - allowance: isUnlimitedOrNone ? null : allowance, - interval: isUnlimitedOrNone ? null : interval, - entity_feature_id: entityFeatureId, - carry_from_previous: carryFromPrevious, - created_at: Date.now(), - id: generateId("ent"), - } as Entitlement; + return { + feature_id: feature.id, + internal_feature_id: feature.internal_id, + allowance_type: allowanceType, + allowance: isUnlimitedOrNone ? null : allowance, + interval: isUnlimitedOrNone ? null : interval, + entity_feature_id: entityFeatureId, + carry_from_previous: carryFromPrevious, + created_at: Date.now(), + id: generateId("ent"), + } as Entitlement; }; export const initPrice = ({ - type, - feature, - billingInterval = BillingInterval.Month, - amount = 10.0, - oneTier = false, - billingUnits = 10, + type, + feature, + billingInterval = BillingInterval.Month, + amount = 10.0, + oneTier = false, + billingUnits = 10, }: { - type: - | "monthly" - | "in_advance" - | "in_arrears" - | "fixed_cycle" - | "in_arrear_prorated"; - feature?: Feature; - billingInterval?: BillingInterval; - amount?: number; - oneTier?: boolean; - billingUnits?: number; + type: + | "monthly" + | "in_advance" + | "in_arrears" + | "fixed_cycle" + | "in_arrear_prorated"; + feature?: Feature; + billingInterval?: BillingInterval; + amount?: number; + oneTier?: boolean; + billingUnits?: number; }) => { - if (type == "monthly" || type == "fixed_cycle") { - return { - name: type == "monthly" ? "Monthly" : "Fixed Cycle", - config: { - type: PriceType.Fixed, - amount: amount, - interval: billingInterval, - }, - }; - } + if (type == "monthly" || type == "fixed_cycle") { + return { + name: type == "monthly" ? "Monthly" : "Fixed Cycle", + config: { + type: PriceType.Fixed, + amount: amount, + interval: billingInterval, + }, + }; + } - if (!feature) { - throw new Error("Feature is required for in_advance and in_arrears"); - } + if (!feature) { + throw new Error("Feature is required for in_advance and in_arrears"); + } - if (type == "in_advance") { - return { - name: "In Advance", - config: { - type: PriceType.Usage, - bill_when: BillWhen.StartOfPeriod, - feature_id: feature!.id, - interval: billingInterval, - billing_units: billingUnits, - usage_tiers: [ - { - from: 0, - to: -1, - amount: amount || 10.0, - }, - ], - }, - }; - } + if (type == "in_advance") { + return { + name: "In Advance", + config: { + type: PriceType.Usage, + bill_when: BillWhen.StartOfPeriod, + feature_id: feature!.id, + interval: billingInterval, + billing_units: billingUnits, + usage_tiers: [ + { + from: 0, + to: -1, + amount: amount || 10.0, + }, + ], + }, + }; + } - if (type == "in_arrears" || type == "in_arrear_prorated") { - return { - name: "In Arrears", - config: { - type: PriceType.Usage, - bill_when: BillWhen.EndOfPeriod, - feature_id: feature!.id, - interval: billingInterval, - billing_units: billingUnits, - should_prorate: type == "in_arrear_prorated", - usage_tiers: oneTier - ? [ - { - from: 0, - to: -1, - amount: amount || 0.01, - }, - ] - : [ - { - from: 0, - to: 10, - amount: 0.5, - }, - { - from: 11, - to: -1, - amount: 0.25, - }, - ], - }, - }; - } + if (type == "in_arrears" || type == "in_arrear_prorated") { + return { + name: "In Arrears", + config: { + type: PriceType.Usage, + bill_when: BillWhen.EndOfPeriod, + feature_id: feature!.id, + interval: billingInterval, + billing_units: billingUnits, + should_prorate: type == "in_arrear_prorated", + usage_tiers: oneTier + ? [ + { + from: 0, + to: -1, + amount: amount || 0.01, + }, + ] + : [ + { + from: 0, + to: 10, + amount: 0.5, + }, + { + from: 11, + to: -1, + amount: 0.25, + }, + ], + }, + }; + } }; export const initFreeTrial = ({ - length, - uniqueFingerprint = false, - cardRequired = true, + length, + uniqueFingerprint = false, + cardRequired = true, }: { - length: number; - uniqueFingerprint?: boolean; - cardRequired?: boolean; + length: number; + uniqueFingerprint?: boolean; + cardRequired?: boolean; }): CreateFreeTrial => { - return { - length, - unique_fingerprint: uniqueFingerprint, - duration: FreeTrialDuration.Day, - card_required: cardRequired, - }; + return { + length, + unique_fingerprint: uniqueFingerprint, + duration: FreeTrialDuration.Day, + card_required: cardRequired, + }; }; export const initProduct = ({ - id, - isDefault = false, - isAddOn = false, - items, - entitlements, - prices, - freeTrial, - group = "", + id, + isDefault = false, + isAddOn = false, + items, + entitlements, + prices, + freeTrial, + group = "", }: { - id: string; - isDefault?: boolean; - isAddOn?: boolean; - items?: Record; - entitlements: Record; - prices: any[]; - freeTrial: CreateFreeTrial | null; - group?: string; + id: string; + isDefault?: boolean; + isAddOn?: boolean; + items?: Record; + entitlements: Record; + prices: any[]; + freeTrial: CreateFreeTrial | null; + group?: string; }) => { - // if (notNullish(items)) { - // return { - // id, - // name: keyToTitle(id), - // is_default: isDefault, - // is_add_on: isAddOn, - // group: group, - // items: items, - // free_trial: freeTrial, - // }; - // } + // if (notNullish(items)) { + // return { + // id, + // name: keyToTitle(id), + // is_default: isDefault, + // is_add_on: isAddOn, + // group: group, + // items: items, + // free_trial: freeTrial, + // }; + // } - return { - id, - name: keyToTitle(id), - is_default: isDefault, - is_add_on: isAddOn, - entitlements: entitlements, - prices, - free_trial: freeTrial, - group: group, - }; + return { + id, + name: keyToTitle(id), + is_default: isDefault, + is_add_on: isAddOn, + entitlements: entitlements, + prices, + free_trial: freeTrial, + group: group, + }; }; export const initCustomer = async ({ - customer_data, - customerId, - attachPm = false, - db, - org, - env, - testClockId, + customer_data, + customerId, + attachPm = false, + db, + org, + env, + testClockId, }: { - customer_data?: { - id: string; - name?: string; - email?: string; - fingerprint?: string; - }; - customerId?: string; - attachPm?: boolean; - db: DrizzleCli; - org: Organization; - env: AppEnv; - testClockId?: string; + customer_data?: { + id: string; + name?: string; + email?: string; + fingerprint?: string; + }; + customerId?: string; + attachPm?: boolean; + db: DrizzleCli; + org: Organization; + env: AppEnv; + testClockId?: string; }) => { - const axiosInstance = getAxiosInstance(); + const axiosInstance = getAxiosInstance(); - if (!customerId && !customer_data) { - throw new Error("customerId or customer_data is required"); - } + if (!customerId && !customer_data) { + throw new Error("customerId or customer_data is required"); + } - let customerData = customerId - ? { - id: customerId, - name: customerId, - email: `${customerId}@example.com`, - } - : customer_data; + const customerData = customerId + ? { + id: customerId, + name: customerId, + email: `${customerId}@example.com`, + } + : customer_data; - // Delete customer if exists - try { - await axiosInstance.delete(`/v1/customers/${customerData!.id}`); - // console.log(" - Successfully deleted customer"); - } catch (error) { - // console.log("Failed to delete customer"); - } + // Delete customer if exists + try { + await axiosInstance.delete(`/v1/customers/${customerData!.id}`); + // console.log(" - Successfully deleted customer"); + } catch (error) { + // console.log("Failed to delete customer"); + } - try { - const { data } = await axiosInstance.post(`/v1/customers`, customerData); - // Attach stripe card + try { + const { data } = await axiosInstance.post(`/v1/customers`, customerData); + // Attach stripe card - if (attachPm) { - await attachPmToCus({ - customer: data.customer, - org: org, - env: env, - db: db, - testClockId: testClockId, - }); - } + if (attachPm) { + await attachPmToCus({ + customer: data.customer, + org: org, + env: env, + db: db, + testClockId: testClockId, + }); + } - return data.customer; - } catch (error) { - console.log("Failed to create customer", error); - } + return data.customer; + } catch (error) { + console.log("Failed to create customer", error); + } }; // Init Reward export const initReward = ({ - id, - type = RewardType.PercentageDiscount, - discountValue, - durationType = CouponDurationType.OneOff, - durationValue = 0, - onlyUsagePrices = false, - productIds, - applyToAll = false, - freeProductId, + id, + type = RewardType.PercentageDiscount, + discountValue, + durationType = CouponDurationType.OneOff, + durationValue = 0, + onlyUsagePrices = false, + productIds, + applyToAll = false, + freeProductId, + freeProductConfig, }: { - id: string; - type?: RewardType; - discountValue?: number; - durationType?: CouponDurationType; - durationValue?: number; - onlyUsagePrices?: boolean; - productIds?: string[]; - applyToAll?: boolean; - freeProductId?: string; + id: string; + type?: RewardType; + discountValue?: number; + durationType?: CouponDurationType; + durationValue?: number; + onlyUsagePrices?: boolean; + productIds?: string[]; + applyToAll?: boolean; + freeProductId?: string; + freeProductConfig?: { + durationType: CouponDurationType; + durationValue: number; + }; }): any => { - if ( - type == RewardType.PercentageDiscount || - type == RewardType.FixedDiscount || - type == RewardType.InvoiceCredits - ) { - return { - id, - name: keyToTitle(id), - type, + if ( + type === RewardType.PercentageDiscount || + type === RewardType.FixedDiscount || + type === RewardType.InvoiceCredits + ) { + return { + id, + name: keyToTitle(id), + type, - only_usage_prices: onlyUsagePrices, - product_ids: productIds, + only_usage_prices: onlyUsagePrices, + product_ids: productIds, - discount_config: { - discount_value: discountValue, - duration_type: durationType, - duration_value: durationValue, - apply_to_all: applyToAll, - }, - }; - } else if (type == RewardType.FreeProduct) { - return { - id, - name: keyToTitle(id), - type, - free_product_id: freeProductId, - }; - } + discount_config: { + discount_value: discountValue, + duration_type: durationType, + duration_value: durationValue, + apply_to_all: applyToAll, + }, + }; + } else if (type === RewardType.FreeProduct) { + return { + id, + name: keyToTitle(id), + type, + free_product_id: freeProductId, + free_product_config: { + duration_type: freeProductConfig?.durationType, + duration_value: freeProductConfig?.durationValue, + }, + }; + } }; export const initRewardProgram = ({ - id, - when = RewardTriggerEvent.CustomerCreation, - productIds = [], - internalRewardId, - maxRedemptions = 2, - receivedBy = RewardReceivedBy.Referrer, + id, + when = RewardTriggerEvent.CustomerCreation, + productIds = [], + internalRewardId, + maxRedemptions = 2, + receivedBy = RewardReceivedBy.Referrer, }: { - id: string; - productIds?: string[]; - internalRewardId: string; - when?: RewardTriggerEvent; - maxRedemptions?: number; - receivedBy?: RewardReceivedBy; + id: string; + productIds?: string[]; + internalRewardId: string; + when?: RewardTriggerEvent; + maxRedemptions?: number; + receivedBy?: RewardReceivedBy; }): any => { - return { - id, - when, - product_ids: productIds, - internal_reward_id: internalRewardId, - max_redemptions: maxRedemptions, - received_by: receivedBy, - }; + return { + id, + when, + product_ids: productIds, + internal_reward_id: internalRewardId, + max_redemptions: maxRedemptions, + received_by: receivedBy, + }; }; diff --git a/server/tests/utils/setup.ts b/server/tests/utils/setup.ts index fdcca2442..c4586d9f3 100644 --- a/server/tests/utils/setup.ts +++ b/server/tests/utils/setup.ts @@ -1,486 +1,486 @@ -import { createSupabaseClient } from "@/external/supabaseUtils.js"; import { AppEnv, - CreateReward, - Feature, + type CreateReward, + type Feature, FeatureType, - FullProduct, - Organization, - Price, + type FullProduct, + type Organization, + type Price, PriceType, - RewardProgram, + type RewardProgram, RewardType, } from "@autumn/shared"; - import axios from "axios"; - -import { OrgService } from "@/internal/orgs/OrgService.js"; -import { createStripeCli } from "@/external/stripe/utils.js"; -import { deleteAllStripeTestClocks } from "./stripeUtils.js"; +import type Stripe from "stripe"; import { AutumnCli } from "tests/cli/AutumnCli.js"; -import Stripe from "stripe"; +import { features as v2Features } from "tests/setup/v2Features.js"; +import { initDrizzle } from "@/db/initDrizzle.js"; import { AutumnInt } from "@/external/autumn/autumnCli.js"; -import { deactivateStripeMeters } from "@/external/stripe/stripeProductUtils.js"; -import { mapToProductItems } from "@/internal/products/productV2Utils.js"; import { CacheManager } from "@/external/caching/CacheManager.js"; import { CacheType } from "@/external/caching/cacheActions.js"; -import { hashApiKey } from "@/internal/dev/api-keys/apiKeyUtils.js"; -import { initDrizzle } from "@/db/initDrizzle.js"; +import { deactivateStripeMeters } from "@/external/stripe/stripeProductUtils.js"; +import { createStripeCli } from "@/external/stripe/utils.js"; import { CusService } from "@/internal/customers/CusService.js"; -import { ProductService } from "@/internal/products/ProductService.js"; -import { RewardService } from "@/internal/rewards/RewardService.js"; +import { hashApiKey } from "@/internal/dev/api-keys/apiKeyUtils.js"; import { FeatureService } from "@/internal/features/FeatureService.js"; -import { features as v2Features } from "tests/setup/v2Features.js"; -import { timeout } from "./genUtils.js"; +import { OrgService } from "@/internal/orgs/OrgService.js"; +import { ProductService } from "@/internal/products/ProductService.js"; +import { mapToProductItems } from "@/internal/products/productV2Utils.js"; +import { RewardService } from "@/internal/rewards/RewardService.js"; +import { deleteAllStripeTestClocks } from "./stripeUtils.js"; export const getAxiosInstance = ( - apiKey: string = process.env.UNIT_TEST_AUTUMN_SECRET_KEY! + apiKey: string = process.env.UNIT_TEST_AUTUMN_SECRET_KEY!, ) => { - return axios.create({ - baseURL: "http://localhost:8080", - headers: { - Authorization: `Bearer ${apiKey}`, - }, - }); + return axios.create({ + baseURL: "http://localhost:8080", + headers: { + Authorization: `Bearer ${apiKey}`, + }, + }); }; export const getPublicAxiosInstance = ({ - withBearer, - pkey = process.env.UNIT_TEST_AUTUMN_PUBLIC_KEY!, + withBearer, + pkey = process.env.UNIT_TEST_AUTUMN_PUBLIC_KEY!, }: { - withBearer: boolean; - pkey?: string; + withBearer: boolean; + pkey?: string; }) => { - let headers = withBearer - ? { - Authorization: `Bearer ${pkey}`, - } - : { - "x-publishable-key": pkey, - }; - return axios.create({ - baseURL: "http://localhost:8080", - headers: headers, - }); + const headers = withBearer + ? { + Authorization: `Bearer ${pkey}`, + } + : { + "x-publishable-key": pkey, + }; + return axios.create({ + baseURL: "http://localhost:8080", + headers: headers, + }); }; export const clearOrg = async ({ - orgSlug, - env, + orgSlug, + env, }: { - orgSlug: string; - env?: AppEnv; + orgSlug: string; + env?: AppEnv; }) => { - if (env !== AppEnv.Sandbox) { - console.error("Cannot clear non-sandbox orgs"); - process.exit(1); - } + if (env !== AppEnv.Sandbox) { + console.error("Cannot clear non-sandbox orgs"); + process.exit(1); + } - const autumn = new AutumnInt(); + const autumn = new AutumnInt(); - if (process.env.STRIPE_TEST_KEY) { - console.log(`Reconnecting stripe...`); + if (process.env.STRIPE_TEST_KEY) { + console.log(`Reconnecting stripe...`); - try { - await autumn.stripe.delete(); - } catch (error) {} - try { - await autumn.stripe.connect({ - secret_key: process.env.STRIPE_TEST_KEY!, - success_url: "https://useautumn.com", - default_currency: "usd", - }); - } catch (error: any) { - console.error("Error reconnecting stripe", error.message); - process.exit(1); - } - } + try { + await autumn.stripe.delete(); + } catch (error) {} + try { + await autumn.stripe.connect({ + secret_key: process.env.STRIPE_TEST_KEY!, + success_url: "https://useautumn.com", + default_currency: "usd", + }); + } catch (error: any) { + console.error("Error reconnecting stripe", error.message); + process.exit(1); + } + } - const { db, client } = initDrizzle(); - const org = await OrgService.getBySlug({ db, slug: orgSlug }); + const { db, client } = initDrizzle(); + const org = await OrgService.getBySlug({ db, slug: orgSlug }); - await Promise.all([ - CacheManager.invalidate({ - action: CacheType.SecretKey, - value: hashApiKey(process.env.UNIT_TEST_AUTUMN_SECRET_KEY!), - }), - CacheManager.invalidate({ - action: CacheType.PublicKey, - value: process.env.UNIT_TEST_AUTUMN_PUBLIC_KEY!, - }), - ]); - await CacheManager.disconnect(); + await Promise.all([ + CacheManager.invalidate({ + action: CacheType.SecretKey, + value: hashApiKey(process.env.UNIT_TEST_AUTUMN_SECRET_KEY!), + }), + CacheManager.invalidate({ + action: CacheType.PublicKey, + value: process.env.UNIT_TEST_AUTUMN_PUBLIC_KEY!, + }), + ]); + await CacheManager.disconnect(); - if (!org) { - throw new Error(`Org ${orgSlug} not found`); - } + if (!org) { + throw new Error(`Org ${orgSlug} not found`); + } - if (!(org.slug == "unit-test-org" || org.slug == "ci_cd")) { - console.error("Cannot clear non-unit-test-orgs"); - process.exit(1); - } + if (!(org.slug === "unit-test-org" || org.slug === "ci_cd")) { + console.error("Cannot clear non-unit-test-orgs"); + process.exit(1); + } - const orgId = org.id; + const orgId = org.id; - // 1. Delete all customers - await CusService.deleteByOrgId({ db, orgId, env }); - console.log(" ✅ Deleted customers"); + // 1. Delete all customers + await CusService.deleteByOrgId({ db, orgId, env }); + console.log(" ✅ Deleted customers"); - const stripeCli = createStripeCli({ org, env: env! }); - const stripeCustomers = await stripeCli.customers.list({ - limit: 100, - }); - const deleteCustomer = async (customer: Stripe.Customer) => { - try { - await stripeCli.customers.del(customer.id); - } catch (error) { - console.error("Error deleting stripe customer", customer.id); - } - }; + const stripeCli = createStripeCli({ org, env: env! }); + const stripeCustomers = await stripeCli.customers.list({ + limit: 100, + }); + const deleteCustomer = async (customer: Stripe.Customer) => { + try { + await stripeCli.customers.del(customer.id); + } catch (error) { + console.error("Error deleting stripe customer", customer.id); + } + }; - const cusBatchSize = 5; - for (let i = 0; i < stripeCustomers.data.length; i += cusBatchSize) { - const batch = stripeCustomers.data.slice(i, i + cusBatchSize); - const batchDeleteCustomers = []; - for (const customer of batch) { - batchDeleteCustomers.push(deleteCustomer(customer)); - } - await Promise.all(batchDeleteCustomers); - console.log( - ` ✅ Deleted ${i + batch.length}/${ - stripeCustomers.data.length - } Stripe customers` - ); - } + const cusBatchSize = 5; + for (let i = 0; i < stripeCustomers.data.length; i += cusBatchSize) { + const batch = stripeCustomers.data.slice(i, i + cusBatchSize); + const batchDeleteCustomers = []; + for (const customer of batch) { + batchDeleteCustomers.push(deleteCustomer(customer)); + } + await Promise.all(batchDeleteCustomers); + console.log( + ` ✅ Deleted ${i + batch.length}/${ + stripeCustomers.data.length + } Stripe customers`, + ); + } - console.log(" ✅ Deleted Stripe customers"); + console.log(" ✅ Deleted Stripe customers"); - // 2. Delete all products - await ProductService.deleteByOrgId({ db, orgId, env }); + // 2. Delete all products + await ProductService.deleteByOrgId({ db, orgId, env }); - console.log(" ✅ Deleted products"); + console.log(" ✅ Deleted products"); - const stripeProducts = await stripeCli.products.list({ - limit: 100, - active: true, - }); + const stripeProducts = await stripeCli.products.list({ + limit: 100, + active: true, + }); - const batchSize = 5; - const removeStripeProduct = async (product: Stripe.Product) => { - try { - await stripeCli.products.del(product.id); - } catch (error) { - await stripeCli.products.update(product.id, { - active: false, - }); - } - }; + const batchSize = 5; + const removeStripeProduct = async (product: Stripe.Product) => { + try { + await stripeCli.products.del(product.id); + } catch (error) { + await stripeCli.products.update(product.id, { + active: false, + }); + } + }; - for (let i = 0; i < stripeProducts.data.length; i += batchSize) { - const batch = stripeProducts.data.slice(i, i + batchSize); - const batchDeleteProducts = []; - for (const product of batch) { - batchDeleteProducts.push(removeStripeProduct(product)); - } - await Promise.all(batchDeleteProducts); - // await timeout(800); - console.log( - ` ✅ Deleted ${i + batch.length}/${ - stripeProducts.data.length - } Stripe products` - ); - } + for (let i = 0; i < stripeProducts.data.length; i += batchSize) { + const batch = stripeProducts.data.slice(i, i + batchSize); + const batchDeleteProducts = []; + for (const product of batch) { + batchDeleteProducts.push(removeStripeProduct(product)); + } + await Promise.all(batchDeleteProducts); + // await timeout(800); + console.log( + ` ✅ Deleted ${i + batch.length}/${ + stripeProducts.data.length + } Stripe products`, + ); + } - console.log(" ✅ Deleted Stripe products"); + console.log(" ✅ Deleted Stripe products"); - await deleteAllStripeTestClocks({ stripeCli }); - console.log(" ✅ Deleted Stripe test clocks"); + await deleteAllStripeTestClocks({ stripeCli }); + console.log(" ✅ Deleted Stripe test clocks"); - // Delete all stripe meters - await deactivateStripeMeters({ org, env }); - console.log(" ✅ Deactivated Stripe meters"); + // Delete all stripe meters + await deactivateStripeMeters({ org, env }); + console.log(" ✅ Deactivated Stripe meters"); - // Batch delete coupons + // Batch delete coupons - const batchDeleteCoupons = []; - await RewardService.deleteByOrgId({ db, orgId, env }); + const batchDeleteCoupons = []; + await RewardService.deleteByOrgId({ db, orgId, env }); - const stripeCoupons = await stripeCli.coupons.list({ - limit: 100, - }); - for (const coupon of stripeCoupons.data) { - batchDeleteCoupons.push(stripeCli.coupons.del(coupon.id)); - } + const stripeCoupons = await stripeCli.coupons.list({ + limit: 100, + }); + for (const coupon of stripeCoupons.data) { + batchDeleteCoupons.push(stripeCli.coupons.del(coupon.id)); + } - await Promise.all(batchDeleteCoupons); - console.log(" ✅ Deleted Stripe coupons"); + await Promise.all(batchDeleteCoupons); + console.log(" ✅ Deleted Stripe coupons"); - await FeatureService.deleteByOrgId({ db, orgId, env }); + await FeatureService.deleteByOrgId({ db, orgId, env }); - console.log(`✅ Cleared org ${orgSlug} (${env})`); + console.log(`✅ Cleared org ${orgSlug} (${env})`); - await client.end(); - return org; + await client.end(); + return org; }; export const setupOrg = async ({ - orgId, - env, - features, - products, - rewards, - rewardTriggers, + orgId, + env, + features, + products, + rewards, + rewardTriggers, }: { - orgId: string; - env: AppEnv; - features: Record; - products: Record; - rewards: Record; - rewardTriggers: Record; + orgId: string; + env: AppEnv; + features: Record; + products: Record; + rewards: Record; + rewardTriggers: Record; }) => { - const axiosInstance = getAxiosInstance(); - const { client, db } = initDrizzle(); + const axiosInstance = getAxiosInstance(); + const { client, db } = initDrizzle(); - const autumn = new AutumnInt(); + const autumn = new AutumnInt(); - let insertFeatures = []; - for (const feature of Object.values(features)) { - insertFeatures.push(axiosInstance.post("/v1/internal_features", feature)); - } + const insertFeatures = []; + for (const feature of Object.values(features)) { + insertFeatures.push(axiosInstance.post("/v1/internal_features", feature)); + } - await Promise.all(insertFeatures); + await Promise.all(insertFeatures); - await FeatureService.insert({ - db, - data: Object.values(v2Features), - logger: console, - }); + await FeatureService.insert({ + db, + data: Object.values(v2Features), + logger: console, + }); - // const allFeatures = await FeatureService.list({ db, orgId, env }); + // const allFeatures = await FeatureService.list({ db, orgId, env }); - let org: Organization | null = null; - let newFeatures: Feature[] = []; - try { - org = await OrgService.get({ db, orgId }); - await OrgService.update({ - db, - orgId, - updates: { - config: { - ...org.config, - bill_upgrade_immediately: true, - }, - }, - }); + let org: Organization | null = null; + let newFeatures: Feature[] = []; + try { + org = await OrgService.get({ db, orgId }); + await OrgService.update({ + db, + orgId, + updates: { + config: { + ...org.config, + bill_upgrade_immediately: true, + }, + }, + }); - newFeatures = (await FeatureService.list({ db, orgId, env })).filter((f) => - Object.keys(features).includes(f.id) - ); - } catch (error) { - console.error("Error updating org", error); - } + newFeatures = (await FeatureService.list({ db, orgId, env })).filter((f) => + Object.keys(features).includes(f.id), + ); + } catch (error) { + console.error("Error updating org", error); + } - for (const feature of newFeatures!) { - features[feature.id].internal_id = feature.internal_id; + for (const feature of newFeatures!) { + features[feature.id].internal_id = feature.internal_id; - if (feature.type === FeatureType.Metered) { - features[feature.id].eventName = feature.config?.filters[0].value[0]; - } - } + if (feature.type === FeatureType.Metered) { + features[feature.id].eventName = feature.config?.filters[0].value[0]; + } + } - console.log("✅ Inserted features"); + console.log("✅ Inserted features"); - // 2. Create products - let insertProducts = []; + // 2. Create products + const insertProducts = []; - const productValues = Object.values(products); - const batchSize = 5; + const productValues = Object.values(products); + const batchSize = 5; - for ( - let batchStart = 0; - batchStart < productValues.length; - batchStart += batchSize - ) { - const batch = productValues.slice(batchStart, batchStart + batchSize); - const batchPromises = []; + for ( + let batchStart = 0; + batchStart < productValues.length; + batchStart += batchSize + ) { + const batch = productValues.slice(batchStart, batchStart + batchSize); + const batchPromises = []; - for (const product of batch) { - const insertProduct = async () => { - await autumn.products.create({ - id: product.id, - name: product.name, - group: product.group, - is_add_on: product.is_add_on, - is_default: product.is_default, - }); + for (const product of batch) { + const insertProduct = async () => { + await autumn.products.create({ + id: product.id, + name: product.name, + group: product.group, + is_add_on: product.is_add_on, + is_default: product.is_default, + }); - const prices = product.prices.map((p: any) => ({ - ...p, - config: { - ...p.config, - internal_feature_id: newFeatures!.find( - (f) => f.id === (p.config as any)?.feature_id - )?.internal_id, - }, - })); + const prices = product.prices.map((p: any) => ({ + ...p, + config: { + ...p.config, + internal_feature_id: newFeatures!.find( + (f) => f.id === (p.config as any)?.feature_id, + )?.internal_id, + }, + })); - const entitlements = Object.values(product.entitlements).map( - (ent: any) => ({ - ...ent, - internal_feature_id: newFeatures!.find( - (f) => f.id === ent.feature_id - )?.internal_id, - }) - ); + const entitlements = Object.values(product.entitlements).map( + (ent: any) => ({ + ...ent, + internal_feature_id: newFeatures!.find( + (f) => f.id === ent.feature_id, + )?.internal_id, + }), + ); - const entWithFeatures = entitlements.map((ent) => ({ - ...ent, - feature: newFeatures!.find((f) => f.id === ent.feature_id), - })); + const entWithFeatures = entitlements.map((ent) => ({ + ...ent, + feature: newFeatures!.find((f) => f.id === ent.feature_id), + })); - let items = mapToProductItems({ - prices, - entitlements: entWithFeatures, - allowFeatureMatch: true, - features: newFeatures!, - }); + const items = mapToProductItems({ + prices, + entitlements: entWithFeatures, + allowFeatureMatch: true, + features: newFeatures!, + }); - try { - await axiosInstance.post(`/v1/products/${product.id}`, { - // prices: prices, - // entitlements: entitlements, - items, - free_trial: product.free_trial, - }); - } catch (error) { - console.log("Product:", product.name); - console.error("Error creating product prices / ents"); - console.log("Items", items); - } - return; - }; + try { + await axiosInstance.post(`/v1/products/${product.id}`, { + // prices: prices, + // entitlements: entitlements, + items, + free_trial: product.free_trial, + }); + } catch (error) { + console.log("Product:", product.name); + console.error("Error creating product prices / ents"); + console.log("Items", items); + } + return; + }; - batchPromises.push(insertProduct()); - } + batchPromises.push(insertProduct()); + } - // Wait for the current batch to complete before proceeding to the next - await Promise.all(batchPromises); - insertProducts.push(...batchPromises); - } + // Wait for the current batch to complete before proceeding to the next + await Promise.all(batchPromises); + insertProducts.push(...batchPromises); + } - await Promise.all(insertProducts); - console.log("✅ Inserted products"); + await Promise.all(insertProducts); + console.log("✅ Inserted products"); - if (process.env.MOCHA_PARALLEL === "true") { - console.log("MOCHA RUNNING IN PARALLEL"); - await AutumnCli.initStripeProducts(); - console.log("✅ Initialized stripe products / prices"); - } else { - console.log("MOCHA RUNNING IN SERIAL"); - } + if (process.env.MOCHA_PARALLEL === "true") { + console.log("MOCHA RUNNING IN PARALLEL"); + await AutumnCli.initStripeProducts(); + console.log("✅ Initialized stripe products / prices"); + } else { + console.log("MOCHA RUNNING IN SERIAL"); + } - // return; + // return; - // Fetch all products - const { list: allProducts } = await AutumnCli.getProducts(); - const productIds = allProducts.map((p: any) => p.id); + // Fetch all products + const { list: allProducts } = await AutumnCli.getProducts(); + const productIds = allProducts.map((p: any) => p.id); - // Insert coupons - let insertCoupons = []; - for (const reward of Object.values(rewards)) { - const createReward = async () => { - let priceIds = []; + // Insert coupons + const insertCoupons = []; + for (const reward of Object.values(rewards)) { + const createReward = async () => { + let priceIds = []; - let rewardData: any = { - id: reward.id, - name: reward.name, - promo_codes: [ - { - code: reward.id, - }, - ], - type: reward.type, - }; + const rewardData: any = { + id: reward.id, + name: reward.name, + promo_codes: [ + { + code: reward.id, + }, + ], + type: reward.type, + }; - if (reward.type === RewardType.FreeProduct) { - rewardData.free_product_id = reward.free_product_id; - } else { - if (reward.only_usage_prices) { - let filteredProducts = allProducts.filter((product: FullProduct) => { - if (reward.product_ids) { - return reward.product_ids.includes(product.id); - } else return true; - }); + if (reward.type === RewardType.FreeProduct) { + rewardData.free_product_id = reward.free_product_id; + rewardData.free_product_config = reward.free_product_config; + } else { + if (reward.only_usage_prices) { + const filteredProducts = allProducts.filter( + (product: FullProduct) => { + if (reward.product_ids) { + return reward.product_ids.includes(product.id); + } else return true; + }, + ); - priceIds = filteredProducts.flatMap((product: FullProduct) => - product.prices - .filter((price: Price) => price.config!.type === PriceType.Usage) - .map((price) => { - return price.id; - }) - ); - } else if (reward.product_ids) { - priceIds = allProducts - .filter((product: FullProduct) => - reward.product_ids.includes(product.id) - ) - .flatMap((product: FullProduct) => - product.prices.map((price) => price.id) - ); - } + priceIds = filteredProducts.flatMap((product: FullProduct) => + product.prices + .filter((price: Price) => price.config!.type === PriceType.Usage) + .map((price) => { + return price.id; + }), + ); + } else if (reward.product_ids) { + priceIds = allProducts + .filter((product: FullProduct) => + reward.product_ids.includes(product.id), + ) + .flatMap((product: FullProduct) => + product.prices.map((price) => price.id), + ); + } - rewardData.discount_config = { - discount_value: reward.discount_config.discount_value, - duration_type: reward.discount_config.duration_type, - duration_value: reward.discount_config.duration_value, - apply_to_all: reward.discount_config.apply_to_all, - price_ids: priceIds, - }; - } + rewardData.discount_config = { + discount_value: reward.discount_config.discount_value, + duration_type: reward.discount_config.duration_type, + duration_value: reward.discount_config.duration_value, + apply_to_all: reward.discount_config.apply_to_all, + price_ids: priceIds, + }; + } - const newReward: CreateReward & { internal_id: string } = { - internal_id: reward.id, - id: reward.id, - name: reward.name, - promo_codes: [ - { - code: reward.id, - }, - ], - type: reward.type, - discount_config: rewardData.discount_config, - free_product_id: rewardData.free_product_id, - }; + const newReward: CreateReward & { internal_id: string } = { + internal_id: reward.id, + id: reward.id, + name: reward.name, + promo_codes: [ + { + code: reward.id, + }, + ], + type: reward.type, + discount_config: rewardData.discount_config, + free_product_id: rewardData.free_product_id, + free_product_config: rewardData.free_product_config?.duration_type && rewardData.free_product_config?.duration_value ? rewardData.free_product_config : undefined, + }; - let rewardRes = await autumn.rewards.create(newReward); + const rewardRes = await autumn.rewards.create(newReward); - return { - id: reward.id, - rewardRes, - }; - }; + return { + id: reward.id, + rewardRes, + }; + }; - console.log("Creating reward", reward.id); - insertCoupons.push(createReward()); - } + console.log("Creating reward", reward.id); + insertCoupons.push(createReward()); + } - await Promise.all(insertCoupons); - console.log("✅ Inserted coupons"); + await Promise.all(insertCoupons); + console.log("✅ Inserted coupons"); - // CREATE REWARD TRIGGERS - let insertRewardTriggers = []; - let insertedRewards = await RewardService.list({ db, orgId, env }); - for (const rewardTrigger of Object.values(rewardTriggers)) { - let rt = { - ...rewardTrigger, - internal_reward_id: insertedRewards.find( - (r) => r.id === rewardTrigger.internal_reward_id - )?.internal_id!, - }; - insertRewardTriggers.push(autumn.rewardPrograms.create(rt)); - } - await Promise.all(insertRewardTriggers); - console.log("✅ Inserted reward triggers"); + // CREATE REWARD TRIGGERS + const insertRewardTriggers = []; + const insertedRewards = await RewardService.list({ db, orgId, env }); + for (const rewardTrigger of Object.values(rewardTriggers)) { + const rt = { + ...rewardTrigger, + internal_reward_id: insertedRewards.find( + (r) => r.id === rewardTrigger.internal_reward_id, + )?.internal_id!, + }; + insertRewardTriggers.push(autumn.rewardPrograms.create(rt)); + } + await Promise.all(insertRewardTriggers); + console.log("✅ Inserted reward triggers"); - await client.end(); + await client.end(); }; diff --git a/server/tests/utils/stripeUtils.ts b/server/tests/utils/stripeUtils.ts index 85567aabd..4f15ce4fd 100644 --- a/server/tests/utils/stripeUtils.ts +++ b/server/tests/utils/stripeUtils.ts @@ -66,7 +66,6 @@ export const completeCheckoutForm = async ( await page.type("#billingPostalCode", "123456"); if (overrideQuantity) { - // console.log(" - Overriding quantity"); const quantityBtn = await page.$(".AdjustableQuantitySelector"); await quantityBtn?.evaluate((b: any) => (b as HTMLElement).click()); @@ -77,6 +76,7 @@ export const completeCheckoutForm = async ( const updateBtn = await page.$(".AdjustQuantityFooter-btn"); await updateBtn?.evaluate((b: any) => (b as HTMLElement).click()); + await timeout(1000); } @@ -88,7 +88,6 @@ export const completeCheckoutForm = async ( await timeout(5000); } - // const submitButton = await page.$(".SubmitButton-TextContainer"); const submitButton = await page.$(".SubmitButton-TextContainer"); await submitButton?.evaluate((b: any) => (b as HTMLElement).click()); await timeout(7000); diff --git a/setup.js b/setup.js index 9fc85fa85..aaf949e91 100644 --- a/setup.js +++ b/setup.js @@ -1,86 +1,97 @@ #!/usr/bin/env node -import { randomBytes } from 'crypto'; -import { writeFileSync, copyFileSync } from 'fs'; -import inquirer from 'inquirer'; -import { spawnSync } from 'child_process'; -import chalk from 'chalk'; +import { randomBytes } from "crypto"; +import { writeFileSync, copyFileSync, readFileSync } from "fs"; +import inquirer from "inquirer"; +import { spawnSync } from "child_process"; +import chalk from "chalk"; const genUrlSafeBase64 = (bytes) => { return randomBytes(bytes) - .toString('base64') - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=+$/g, ''); -} - + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/g, ""); +}; const genRandomSubdomain = (length = 10) => { - const chars = 'abcdefghijklmnopqrstuvwxyz'; - let result = ''; + const chars = "abcdefghijklmnopqrstuvwxyz"; + let result = ""; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; -} +}; const genAlphanumericPassword = (length = 24) => { - const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - let result = ''; + const chars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + let result = ""; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; -} +}; // Helper to get or create a Supabase org const getOrCreateSupabaseOrg = async () => { - // List orgs (table output) - const orgListResult = spawnSync( - 'npx', - ['supabase', 'orgs', 'list'], - { encoding: 'utf-8', shell: true } - ); + const orgListResult = spawnSync("npx", ["supabase", "orgs", "list"], { + encoding: "utf-8", + shell: true, + }); if (orgListResult.status !== 0) { - console.error(chalk.red('❌ Failed to list Supabase orgs.')); + console.error(chalk.red("❌ Failed to list Supabase orgs.")); process.exit(1); } - const lines = orgListResult.stdout.split('\n').map(l => l.trim()).filter(Boolean); + const lines = orgListResult.stdout + .split("\n") + .map((l) => l.trim()) + .filter(Boolean); // Find separator line (e.g., "------|------") - const sepIdx = lines.findIndex(line => line.includes('|') && line.includes('-')); + const sepIdx = lines.findIndex( + (line) => line.includes("|") && line.includes("-") + ); // If only header and separator, no orgs exist if (lines.length <= sepIdx + 1) { - console.log(chalk.yellowBright('No Supabase organizations found.')); + console.log(chalk.yellowBright("No Supabase organizations found.")); const { createOrg } = await inquirer.prompt([ { - type: 'confirm', - name: 'createOrg', - message: chalk.cyan('Would you like to create a new Supabase organization now?'), + type: "confirm", + name: "createOrg", + message: chalk.cyan( + "Would you like to create a new Supabase organization now?" + ), default: true, }, ]); if (!createOrg) { - console.log(chalk.red('❌ Cannot continue without a Supabase organization. Exiting.')); + console.log( + chalk.red( + "❌ Cannot continue without a Supabase organization. Exiting." + ) + ); process.exit(1); } const { orgName } = await inquirer.prompt([ { - type: 'input', - name: 'orgName', - message: chalk.cyan('Enter a name for your new Supabase organization:'), + type: "input", + name: "orgName", + message: chalk.cyan("Enter a name for your new Supabase organization:"), validate: (input) => input && input.length > 2, }, ]); - console.log(chalk.blueBright(`\nCreating Supabase organization '${orgName}'...`)); + console.log( + chalk.blueBright(`\nCreating Supabase organization '${orgName}'...`) + ); const createOrgResult = spawnSync( - 'npx', - ['supabase', 'orgs', 'create', orgName], - { stdio: 'inherit', encoding: 'utf-8', shell: true } + "npx", + ["supabase", "orgs", "create", orgName], + { stdio: "inherit", encoding: "utf-8", shell: true } ); if (createOrgResult.status !== 0) { - console.error(chalk.red('❌ Failed to create Supabase organization.')); + console.error(chalk.red("❌ Failed to create Supabase organization.")); process.exit(1); } // Done! Org created, return @@ -92,11 +103,27 @@ const getOrCreateSupabaseOrg = async () => { }; const generateSupabaseDatabaseUrl = async () => { - // Step 3: Run supabase login - console.log(chalk.magentaBright('\n================ Supabase Setup ================\n')); + // Step 3: Check if already logged in, otherwise run supabase login + console.log( + chalk.magentaBright("\n================ Supabase Setup ================\n") + ); - console.log(chalk.blueBright('\nLaunching Supabase login...')); - spawnSync('npx', ['supabase', 'login'], { stdio: 'inherit', shell: true }); + // Check if user is already logged in by trying to list orgs + console.log(chalk.blueBright("\nChecking Supabase authentication...")); + const authCheckResult = spawnSync("npx", ["supabase", "orgs", "list"], { + encoding: "utf-8", + shell: true, + stdio: "pipe", // Capture output instead of showing it + }); + + if (authCheckResult.status !== 0) { + // User is not logged in + console.log(chalk.yellowBright("Not logged in to Supabase.")); + console.log(chalk.blueBright("Launching Supabase login...")); + spawnSync("npx", ["supabase", "login"], { stdio: "inherit", shell: true }); + } else { + console.log(chalk.greenBright("✅ Already logged in to Supabase.")); + } // Step 3.5: Ensure org exists and select one await getOrCreateSupabaseOrg(); @@ -108,149 +135,120 @@ const generateSupabaseDatabaseUrl = async () => { const dbPassword = genAlphanumericPassword(24); // 24 chars, alphanumeric // Step 6: Create Supabase project - console.log(chalk.blueBright(`\nCreating Supabase project '${projectName}'...`)); - const createResult = spawnSync( - 'npx', - ['supabase', 'projects', 'create', projectName, '--db-password', dbPassword], - { stdio: 'inherit', encoding: 'utf-8', shell: true } + console.log( + chalk.blueBright(`\nCreating Supabase project '${projectName}'...`) ); - + const createResult = spawnSync( + "npx", + [ + "supabase", + "projects", + "create", + projectName, + "--db-password", + dbPassword, + ], + { stdio: "inherit", encoding: "utf-8", shell: true } + ); + if (createResult.status !== 0) { - console.error('❌ Failed to create Supabase project.'); + console.error("❌ Failed to create Supabase project."); process.exit(1); } // List supabase projects const listResult = spawnSync( - 'npx', - ['supabase', 'projects', 'list', '--output', 'json'], - { encoding: 'utf-8', shell: true } + "npx", + ["supabase", "projects", "list", "--output", "json"], + { encoding: "utf-8", shell: true } ); if (listResult.status !== 0) { - console.error('❌ Failed to list Supabase projects.'); + console.error("❌ Failed to list Supabase projects."); process.exit(1); } const projects = JSON.parse(listResult.stdout); - const found = projects.find(p => p.name === projectName); + const found = projects.find((p) => p.name === projectName); if (!found) { - console.error(chalk.red('❌ Failed to find new Supabase project.')); + console.error(chalk.red("❌ Failed to find new Supabase project.")); process.exit(1); } const projectId = found.id; - const region = found.region; + const region = found.region; // Step 8: Construct DATABASE_URL - const databaseUrl = `postgresql://postgres.${projectId}:${dbPassword}@aws-0-${region}.pooler.supabase.com:5432/postgres`; + const databaseUrl = `postgresql://postgres.${projectId}:${dbPassword}@aws-1-${region}.pooler.supabase.com:5432/postgres`; console.log(chalk.greenBright(`\nGenerated DB password: ${dbPassword}\n`)); console.log(chalk.greenBright(`\nYour DATABASE_URL is:\n${databaseUrl}\n`)); - console.log(chalk.yellow('--------------------------------')); + console.log(chalk.yellow("--------------------------------")); // Step 9: Prompt to run docker compose up - console.log(chalk.magentaBright('\nNext steps:')); - console.log(chalk.yellow('Run "docker compose -f docker-compose.dev.yml up" to start Autumn')); - + console.log(chalk.magentaBright("\nNext steps:")); + console.log( + chalk.yellow( + 'Run "docker compose -f docker-compose.dev.yml up" to start Autumn' + ) + ); return databaseUrl; -} +}; const handleDatabaseSetup = async () => { - // Step 2: Ask user what to do for DB - console.log(chalk.magentaBright('\n================ Autumn Database Setup ================\n')); - const { dbOption } = await inquirer.prompt([ - { - type: 'list', - name: 'dbOption', - message: chalk.cyan('How do you want to set up your database?'), - choices: [ - { name: 'Set up Supabase (Cloud) for Autumn', value: 'supabase' }, - { name: 'Paste in your own DATABASE_URL', value: 'paste' }, - { name: 'Paste in your own DATABASE_URL later', value: 'later' }, - ], - default: 'supabase', - }, -]); - -let databaseUrl = ''; - -if (dbOption === 'supabase') { - databaseUrl = await generateSupabaseDatabaseUrl(); -} else if (dbOption === 'paste') { - const res = await inquirer.prompt([ + // Step 2: Ask user what to do for DB + console.log( + chalk.magentaBright( + "\n================ Autumn Database Setup ================\n" + ) + ); + const { dbOption } = await inquirer.prompt([ { - type: 'input', - name: 'databaseUrl', - message: 'Paste in your DATABASE_URL:', - validate: (input) => input && input.length > 5, - }, - ]); - databaseUrl = res.databaseUrl; -} else if (dbOption === 'later') { - databaseUrl = ''; -} - -return databaseUrl; -} - -const handleLocalRunSetup = async () => { - // Step 10: Stripe webhook URL setup - console.log(chalk.magentaBright('\n================ Stripe Webhook Setup ================\n')); - const { webhookOption } = await inquirer.prompt([ - { - type: 'list', - name: 'webhookOption', - message: - chalk.cyan('Stripe requires a public URL to send webhook events to your app.\nThis is read from the STRIPE_WEBHOOK_URL environment variable.\n\nWould you like to:'), + type: "list", + name: "dbOption", + message: chalk.cyan("How do you want to set up your database?"), choices: [ - { name: '1. Generate a localtunnel URL (recommended for local development)', value: 'localtunnel' }, - { name: '2. Skip for now and paste in your own URL later', value: 'skip' }, + { name: "Set up Supabase (Cloud) for Autumn", value: "supabase" }, + { name: "Paste in your own DATABASE_URL", value: "paste" }, + { name: "Paste in your own DATABASE_URL later", value: "later" }, ], - default: 'localtunnel', + default: "supabase", }, ]); - let stripeWebhookVars = []; + let databaseUrl = ""; - if (webhookOption === 'localtunnel') { - const subdomain = genRandomSubdomain(32); - const webhookUrl = `https://${subdomain}.loca.lt`; - stripeWebhookVars.push(`STRIPE_WEBHOOK_URL=${webhookUrl}`); - stripeWebhookVars.push(`LOCALHOST_RUN_SUBDOMAIN=${subdomain}`); - console.log(chalk.greenBright(`\nTo start your webhook tunnel, run this in another terminal:\n`)); - console.log(chalk.yellowBright(` npx localtunnel --port 8080 --subdomain ${subdomain}\n`)); - console.log(chalk.greenBright(`\nTest your webhook URL with:\n`)); - console.log(chalk.yellowBright(` curl ${webhookUrl}\n`)); - console.log(chalk.cyan('If you need to restart the tunnel in the future, use the same command.')); - } else if (webhookOption === 'paste') { - const { stripeWebhookUrl } = await inquirer.prompt([ + if (dbOption === "supabase") { + databaseUrl = await generateSupabaseDatabaseUrl(); + } else if (dbOption === "paste") { + const res = await inquirer.prompt([ { - type: 'input', - name: 'stripeWebhookUrl', - message: chalk.cyan('Paste in your STRIPE_WEBHOOK_URL:'), + type: "input", + name: "databaseUrl", + message: "Paste in your DATABASE_URL:", validate: (input) => input && input.length > 5, }, ]); - stripeWebhookVars.push(`STRIPE_WEBHOOK_URL=${stripeWebhookUrl}`); - } else { - // skip + databaseUrl = res.databaseUrl; + } else if (dbOption === "later") { + databaseUrl = ""; } - console.log(chalk.yellow('--------------------------------')); + return databaseUrl; +}; - return stripeWebhookVars; -} async function main() { // Step 1: Generate secrets - console.log(chalk.magentaBright('\n================ Autumn Setup ================\n')); + console.log( + chalk.magentaBright("\n================ Autumn Setup ================\n") + ); const localtunnelReservedKey = genRandomSubdomain(32); const secrets = { BETTER_AUTH_SECRET: genUrlSafeBase64(64), ENCRYPTION_IV: genUrlSafeBase64(16), ENCRYPTION_PASSWORD: genUrlSafeBase64(64), - BETTER_AUTH_URL: 'http://localhost:8080', - CLIENT_URL: 'http://localhost:3000', + BETTER_AUTH_URL: "http://localhost:8080", + CLIENT_URL: "http://localhost:3000", LOCALTUNNEL_RESERVED_KEY: localtunnelReservedKey, STRIPE_WEBHOOK_URL: `https://${localtunnelReservedKey}.loca.lt`, }; @@ -262,86 +260,60 @@ async function main() { // stripeWebhookVars = await handleLocalRunSetup(); // Step 11: Write to server/.env - console.log(chalk.magentaBright('\n================ Writing .env ================\n')); + console.log( + chalk.magentaBright("\n================ Writing .env ================\n") + ); const envSections = []; // Autumn Auth section envSections.push( - '# Auth', + "# Auth", `BETTER_AUTH_SECRET=${secrets.BETTER_AUTH_SECRET}`, `BETTER_AUTH_URL=${secrets.BETTER_AUTH_URL}`, `CLIENT_URL=${secrets.CLIENT_URL}`, - '' + "" ); // Stripe required section envSections.push( - '# Stripe', + "# Stripe", `LOCALTUNNEL_RESERVED_KEY=${secrets.LOCALTUNNEL_RESERVED_KEY}`, `ENCRYPTION_IV=${secrets.ENCRYPTION_IV}`, `ENCRYPTION_PASSWORD=${secrets.ENCRYPTION_PASSWORD}`, `STRIPE_WEBHOOK_URL=${secrets.STRIPE_WEBHOOK_URL}`, - '' + "" ); // Database section if (databaseUrl) { - envSections.push( - '# Database', - `DATABASE_URL=${databaseUrl}`, - '' - ); + envSections.push("# Database", `DATABASE_URL=${databaseUrl}`, ""); } // Stripe Webhooks section if (stripeWebhookVars.length > 0) { - envSections.push('# Stripe Webhooks'); + envSections.push("# Stripe Webhooks"); envSections.push(...stripeWebhookVars); - envSections.push(''); + envSections.push(""); } - const envVars = envSections.join('\n'); + const envVars = envSections.join("\n"); - - writeFileSync('server/.env', envVars); + writeFileSync("server/.env", envVars); try { - copyFileSync('vite/.env.example', 'vite/.env'); + copyFileSync("vite/.env.example", "vite/.env"); } catch (error) { - console.log(chalk.red('❌ Failed to copy vite/.env.example to vite/.env')); - console.log(chalk.red('❌ Please copy the file manually')); + console.log(chalk.red("❌ Failed to copy vite/.env.example to vite/.env")); + console.log(chalk.red("❌ Please copy the file manually")); } - console.log(chalk.greenBright('🎉 Setup complete! 🎉')); - console.log(chalk.cyan('You can find your env variables in server/.env')); + console.log(chalk.greenBright("🎉 Setup complete! 🎉")); + console.log(chalk.cyan("You can find your env variables in server/.env")); - - // Prompt to run pnpm run db:push at the end - const { runDbPush } = await inquirer.prompt([ - { - type: 'confirm', - name: 'runDbPush', - message: chalk.cyan('Would you like to run "bun run db:push" to set up your tables now?'), - default: true, - }, - ]); - - if (runDbPush) { - console.log(chalk.blueBright('\nRunning "bun run db:push" in shared directory...')); - const dbPushResult = spawnSync('bun', ['run', 'db:push'], { stdio: 'inherit', encoding: 'utf-8', shell: true, cwd: 'shared', env: { - ...process.env, - NODE_OPTIONS: '--import tsx', - DATABASE_URL: databaseUrl, - } }); - if (dbPushResult.status !== 0) { - console.error(chalk.red('❌ Failed to run "bun run db:push". Please check the error above.')); - process.exit(1); - } - console.log(chalk.greenBright('✅ Successfully ran "bun run db:push".')); - } - - console.log(chalk.cyan('\nNext steps:')); - console.log(chalk.cyan('Run the following command to start Autumn:')); - console.log(chalk.cyan(' docker compose -f docker-compose.dev.yml up')); + console.log(chalk.cyan("\nNext steps:\n")); + console.log(chalk.cyan("1. Set up your database tables:\n")); + console.log(chalk.whiteBright(" bun db:generate && bun db:migrate\n")); + console.log(chalk.cyan("2. Start Autumn:\n")); + console.log(chalk.whiteBright(" docker compose -f docker-compose.dev.yml up\n")); } -main(); \ No newline at end of file +main(); diff --git a/shared/models/attachModels/attachEnums/AttachConfig.ts b/shared/models/attachModels/attachEnums/AttachConfig.ts index f37bcd2db..d45e837f4 100644 --- a/shared/models/attachModels/attachEnums/AttachConfig.ts +++ b/shared/models/attachModels/attachEnums/AttachConfig.ts @@ -18,4 +18,5 @@ export interface AttachConfig { sameIntervals: boolean; carryTrial: boolean; finalizeInvoice: boolean; + requirePaymentMethod: boolean; } diff --git a/shared/models/rewardModels/referralModels/referralModels.ts b/shared/models/rewardModels/referralModels/referralModels.ts index ede3c25ff..bcf143639 100644 --- a/shared/models/rewardModels/referralModels/referralModels.ts +++ b/shared/models/rewardModels/referralModels/referralModels.ts @@ -1,37 +1,38 @@ import { z } from "zod"; -let ReferralCodeSchema = z.object({ - // Code, org id, env should be unique - code: z.string(), - org_id: z.string(), - env: z.string(), +const ReferralCodeSchema = z.object({ + // Code, org id, env should be unique + code: z.string(), + org_id: z.string(), + env: z.string(), - created_at: z.number(), - internal_customer_id: z.string(), - internal_reward_program_id: z.string(), + created_at: z.number(), + internal_customer_id: z.string(), + internal_reward_program_id: z.string(), - // ID of the referral code - id: z.string(), + // ID of the referral code + id: z.string(), }); -let RewardRedemptionSchema = z.object({ - id: z.string(), - created_at: z.number(), - updated_at: z.number(), +const RewardRedemptionSchema = z.object({ + id: z.string(), + created_at: z.number(), + updated_at: z.number(), - // Customer who signed up / paid - internal_customer_id: z.string(), // customer who redeemed the code... - internal_reward_program_id: z.string(), // reward scheme that was redeemed + // Customer who signed up / paid + internal_customer_id: z.string(), // customer who redeemed the code... + internal_reward_program_id: z.string(), // reward scheme that was redeemed - // Referral code used - // code: z.string(), - referral_code_id: z.string(), + // Referral code used + // code: z.string(), + referral_code_id: z.string(), - // Whether the reward was triggered - triggered: z.boolean(), + // Whether the reward was triggered + triggered: z.boolean(), - // Whether the (coupon) was applied - applied: z.boolean(), + // Whether the (coupon) was applied + applied: z.boolean(), + redeemer_applied: z.boolean(), }); export type ReferralCode = z.infer; diff --git a/shared/models/rewardModels/referralModels/rewardRedemptionTable.ts b/shared/models/rewardModels/referralModels/rewardRedemptionTable.ts index 54eb0ab75..7f833783e 100644 --- a/shared/models/rewardModels/referralModels/rewardRedemptionTable.ts +++ b/shared/models/rewardModels/referralModels/rewardRedemptionTable.ts @@ -19,6 +19,7 @@ export const rewardRedemptions = pgTable( triggered: boolean(), internal_reward_program_id: text("internal_reward_program_id"), applied: boolean().default(false), + redeemer_applied: boolean().default(false), referral_code_id: text("referral_code_id"), }, (table) => [ @@ -37,5 +38,5 @@ export const rewardRedemptions = pgTable( foreignColumns: [referralCodes.id], name: "reward_redemptions_referral_code_id_fkey", }).onDelete("cascade"), - ], + ] ); diff --git a/shared/models/rewardModels/rewardModels/rewardModels.ts b/shared/models/rewardModels/rewardModels/rewardModels.ts index 1781fc753..12adfb5d4 100644 --- a/shared/models/rewardModels/rewardModels/rewardModels.ts +++ b/shared/models/rewardModels/rewardModels/rewardModels.ts @@ -14,6 +14,11 @@ export const DiscountConfigSchema = z.object({ price_ids: z.array(z.string()).optional(), }); +export const FreeProductConfigSchema = z.object({ + duration_type: z.nativeEnum(CouponDurationType), + duration_value: z.number(), +}); + const RewardSchema = z.object({ name: z.string().nullish(), @@ -23,6 +28,7 @@ const RewardSchema = z.object({ free_product_id: z.string().nullish(), discount_config: DiscountConfigSchema.nullish(), + free_product_config: FreeProductConfigSchema.nullish(), internal_id: z.string(), org_id: z.string(), @@ -36,6 +42,7 @@ export const CreateRewardSchema = z.object({ id: z.string(), type: z.nativeEnum(RewardType).nullish(), discount_config: DiscountConfigSchema.nullish(), + free_product_config: FreeProductConfigSchema.nullish(), free_product_id: z.string().nullish(), }); @@ -43,3 +50,4 @@ export type PromoCode = z.infer; export type CreateReward = z.infer; export type Reward = z.infer; export type DiscountConfig = z.infer; +export type FreeProductConfig = z.infer; diff --git a/shared/models/rewardModels/rewardModels/rewardTable.ts b/shared/models/rewardModels/rewardModels/rewardTable.ts index 097c0aa46..b8248ef6e 100644 --- a/shared/models/rewardModels/rewardModels/rewardTable.ts +++ b/shared/models/rewardModels/rewardModels/rewardTable.ts @@ -1,32 +1,37 @@ -import { foreignKey, jsonb, text } from "drizzle-orm/pg-core"; -import { numeric } from "drizzle-orm/pg-core"; -import { pgTable } from "drizzle-orm/pg-core"; +import type { InferInsertModel, InferSelectModel } from "drizzle-orm"; +import { foreignKey, jsonb, numeric, pgTable, text } from "drizzle-orm/pg-core"; import { organizations } from "../../orgModels/orgTable.js"; -import { DiscountConfig, PromoCode } from "./rewardModels.js"; -import { InferSelectModel, InferInsertModel } from "drizzle-orm"; +import type { + DiscountConfig, + FreeProductConfig, + PromoCode, +} from "./rewardModels.js"; export const rewards = pgTable( - "rewards", - { - internal_id: text("internal_id").primaryKey().notNull(), - id: text(), - org_id: text("org_id"), - env: text(), - created_at: numeric({ mode: "number" }), - name: text(), - discount_config: jsonb("discount_config").$type(), - free_product_id: text("free_product_id"), - promo_codes: jsonb("promo_codes").$type().array(), - type: text(), - }, - (table) => [ - foreignKey({ - columns: [table.org_id], - foreignColumns: [organizations.id], - name: "coupons_org_id_fkey", - }).onDelete("cascade"), - ], + "rewards", + { + internal_id: text("internal_id").primaryKey().notNull(), + id: text(), + org_id: text("org_id"), + env: text(), + created_at: numeric({ mode: "number" }), + name: text(), + discount_config: jsonb("discount_config").$type(), + free_product_config: jsonb( + "free_product_config", + ).$type(), + free_product_id: text("free_product_id"), + promo_codes: jsonb("promo_codes").$type().array(), + type: text(), + }, + (table) => [ + foreignKey({ + columns: [table.org_id], + foreignColumns: [organizations.id], + name: "coupons_org_id_fkey", + }).onDelete("cascade"), + ], ); export type RewardRow = InferSelectModel; -export type InsertRewardRow = InferInsertModel; \ No newline at end of file +export type InsertRewardRow = InferInsertModel; diff --git a/shared/package.json b/shared/package.json index 1dc7842fd..624b58bae 100644 --- a/shared/package.json +++ b/shared/package.json @@ -14,9 +14,9 @@ "license": "Apache-2.0", "scripts": { "build:tsc": "tsc", - "build": "bun build ./index.ts --outdir dist --target bun --external zod", + "build": "bun build ./index.ts --outdir dist --format esm --target bun --external zod", "dev": "bunx nodemon --ext ts --ignore dist --exec \"bun run build && bun run dev:dts\"", - "dev:dts": "tsc --emitDeclarationOnly --outDir dist --project tsconfig.json --declaration --declarationMap false --emitDeclarationOnly true --outFile dist/index.d.ts", + "dev:dts": "tsc --emitDeclarationOnly --outDir dist --project tsconfig.json", "dev:bun": "bun ./index.ts --outdir dist --target bun --external zod --watch", "db:push": "bun db:generate && bun db:migrate", "db:generate": "cross-env NODE_OPTIONS=\"--import tsx\" bunx drizzle-kit generate --config drizzle.config.ts", diff --git a/shared/utils/cusEntUtils/balanceUtils.ts b/shared/utils/cusEntUtils/balanceUtils.ts new file mode 100644 index 000000000..6b600c801 --- /dev/null +++ b/shared/utils/cusEntUtils/balanceUtils.ts @@ -0,0 +1,71 @@ +import { FullCustomerEntitlement } from "../../models/cusProductModels/cusEntModels/cusEntModels.js"; +import { notNullish, nullish } from "../utils.js"; + +export const getSummedEntityBalances = ({ + cusEnt, +}: { + cusEnt: FullCustomerEntitlement; +}) => { + if (nullish(cusEnt.entities)) { + return { + balance: 0, + adjustment: 0, + unused: 0, + count: 0, + }; + } + + return { + balance: Object.values(cusEnt.entities!).reduce( + (acc, curr) => acc + curr.balance, + 0 + ), + adjustment: Object.values(cusEnt.entities!).reduce( + (acc, curr) => acc + curr.adjustment, + 0 + ), + unused: 0, + count: Object.values(cusEnt.entities!).length, + }; +}; + +export const getCusEntBalance = ({ + cusEnt, + entityId, +}: { + cusEnt: FullCustomerEntitlement; + entityId?: string | null; +}) => { + let entitlement = cusEnt.entitlement; + let ent = cusEnt.entitlement; + let feature = ent.feature; + + if (notNullish(entitlement.entity_feature_id)) { + if (nullish(entityId)) { + return getSummedEntityBalances({ + cusEnt, + }); + } else { + let entityBalance = cusEnt.entities?.[entityId!]?.balance; + let adjustment = cusEnt.entities?.[entityId!]?.adjustment || 0; + + if (nullish(entityBalance)) { + return { balance: 0, adjustment: 0, unused: 0, count: 1 }; + } + + return { + balance: entityBalance || 0, + adjustment, + unused: 0, + count: 1, + }; + } + } + + return { + balance: cusEnt.balance, + adjustment: cusEnt.adjustment, + unused: cusEnt.replaceables?.length || 0, + count: 1, + }; +}; diff --git a/shared/utils/cusProductUtils/formatCusProductUtils.ts b/shared/utils/cusProductUtils/formatCusProductUtils.ts new file mode 100644 index 000000000..0d0f68975 --- /dev/null +++ b/shared/utils/cusProductUtils/formatCusProductUtils.ts @@ -0,0 +1,12 @@ +import { FullCusProduct } from "../../models/cusProductModels/cusProductModels.js"; + +export const logCusProducts = ({ + cusProducts, +}: { + cusProducts: FullCusProduct[]; +}) => { + console.log(`CUS PRODUCTS:`); + for (const cusProduct of cusProducts) { + console.log(`${cusProduct.product.id} - ${cusProduct.status}`); + } +}; diff --git a/shared/utils/index.ts b/shared/utils/index.ts index 7fff843ae..5153d093e 100644 --- a/shared/utils/index.ts +++ b/shared/utils/index.ts @@ -7,8 +7,12 @@ export * from "./cusProductUtils/convertCusProduct.js"; export * from "./cusProductUtils/productIdToCusProduct.js"; export * from "./cusProductUtils/cusProductConstants.js"; export * from "./cusProductUtils/cusProductUtils.js"; +export * from "./cusProductUtils/formatCusProductUtils.js"; export * from "./utils.js"; +// Cus ent utils +export * from "./cusEntUtils/balanceUtils.js"; + // Item utils export * from "./productV2Utils/productItemUtils/mapToItem.js"; export * from "./productV2Utils/productItemUtils/productItemUtils.js"; @@ -16,3 +20,4 @@ export * from "./productV2Utils/mapToProductV2.js"; // Product utils export * from "./productUtils/convertUtils.js"; +export * from "./productUtils/priceUtils.js"; diff --git a/vite/biome.json b/vite/biome.json index fcbfad054..53487eb49 100644 --- a/vite/biome.json +++ b/vite/biome.json @@ -17,7 +17,10 @@ "linter": { "enabled": true, "rules": { - "recommended": true + "recommended": true, + "complexity": { + "noStaticOnlyClass": "off" + } } }, "javascript": { diff --git a/vite/src/app/layout.tsx b/vite/src/app/layout.tsx index c25af9d8e..55c7e460f 100644 --- a/vite/src/app/layout.tsx +++ b/vite/src/app/layout.tsx @@ -162,10 +162,10 @@ const MainContent = () => { "w-full h-full overflow-auto flex justify-center bg-stone-50" )} > -
+
-
+ {/*

Autumn is coming to mobile soon @@ -175,7 +175,7 @@ const MainContent = () => { your desktop?

-
+
*/}
diff --git a/vite/src/components/general/modal-components/FieldLabel.tsx b/vite/src/components/general/modal-components/FieldLabel.tsx index 86bc0413c..99b20e662 100644 --- a/vite/src/components/general/modal-components/FieldLabel.tsx +++ b/vite/src/components/general/modal-components/FieldLabel.tsx @@ -1,21 +1,42 @@ +import type React from "react"; import { cn } from "@/lib/utils"; -import React from "react"; +import { InfoTooltip } from "./InfoTooltip"; -function FieldLabel({ children, className, description, empty = false }: any) { - if (empty) { - children = "\u00A0"; - } - if (!description) { - return ( -
{children}
- ); - } - return ( -
- {children} - {description &&

{description}

} -
- ); +function FieldLabel({ + children, + className, + description, + empty = false, + tooltip, +}: { + children: React.ReactNode; + className?: string; + description?: string; + empty?: boolean; + tooltip?: string; +}) { + if (empty) { + children = "\u00A0"; + } + if (!description) { + return ( +
{children}
+ ); + } + return ( +
+ {children} + {description && !tooltip && ( +

{description}

+ )} + {tooltip && description && ( +
+

{description}

+ {tooltip} +
+ )} +
+ ); } export default FieldLabel; diff --git a/vite/src/services/products/RewardService.tsx b/vite/src/services/products/RewardService.tsx index 4ffd34c5b..8fa29c49e 100644 --- a/vite/src/services/products/RewardService.tsx +++ b/vite/src/services/products/RewardService.tsx @@ -1,37 +1,37 @@ -import { Reward, CreateReward } from "@autumn/shared"; +import type { CreateReward, Reward } from "@autumn/shared"; -import { AxiosInstance } from "axios"; +import type { AxiosInstance } from "axios"; export class RewardService { - static async createReward({ - axiosInstance, - data, - }: { - axiosInstance: AxiosInstance; - data: CreateReward; - }) { - await axiosInstance.post("/v1/rewards", data); - } + static async createReward({ + axiosInstance, + data, + }: { + axiosInstance: AxiosInstance; + data: CreateReward; + }) { + await axiosInstance.post("/v1/rewards", data); + } - static async deleteReward({ - axiosInstance, - internalId, - }: { - axiosInstance: AxiosInstance; - internalId: string; - }) { - await axiosInstance.delete(`/v1/rewards/${internalId}`); - } + static async deleteReward({ + axiosInstance, + internalId, + }: { + axiosInstance: AxiosInstance; + internalId: string; + }) { + await axiosInstance.delete(`/v1/rewards/${internalId}`); + } - static async updateReward({ - axiosInstance, - internalId, - data, - }: { - axiosInstance: AxiosInstance; - internalId: string; - data: Reward; - }) { - await axiosInstance.post(`/v1/rewards/${internalId}`, data); - } + static async updateReward({ + axiosInstance, + internalId, + data, + }: { + axiosInstance: AxiosInstance; + internalId: string; + data: Reward; + }) { + await axiosInstance.post(`/v1/rewards/${internalId}`, data); + } } diff --git a/vite/src/utils/formatUtils/formatTextUtils.ts b/vite/src/utils/formatUtils/formatTextUtils.ts index 99d121179..87ba41c9b 100644 --- a/vite/src/utils/formatUtils/formatTextUtils.ts +++ b/vite/src/utils/formatUtils/formatTextUtils.ts @@ -1,10 +1,12 @@ import { BillingInterval, - EntInterval, - ProductItemInterval, + EntInterval } from "@autumn/shared"; -export const keyToTitle = (key: string) => { +export const keyToTitle = (key: string, options?: { exclusionMap?: Record }) => { + if(options?.exclusionMap?.[key]) { + return options.exclusionMap[key]; + } return key .replace(/[_-]/g, " ") .replace(/\b\w/g, (char) => char.toUpperCase()); @@ -23,22 +25,24 @@ export const slugify = ( ) => { return text .toLowerCase() - .replace(/ /g, type == "underscore" ? "_" : "-") + .replace(/ /g, type === "underscore" ? "_" : "-") .replace(/[^\w\s-]/g, ""); }; export const formatAmount = ({ amount, currency, + maxFractionDigits = 10, }: { amount: number; currency: string; + maxFractionDigits?: number; }) => { return new Intl.NumberFormat("en-US", { style: "currency", currency: currency, minimumFractionDigits: 0, - maximumFractionDigits: 10, + maximumFractionDigits: maxFractionDigits, }).format(amount); }; diff --git a/vite/src/utils/product/productItemUtils.ts b/vite/src/utils/product/productItemUtils.ts index d912e6295..033ae7bc9 100644 --- a/vite/src/utils/product/productItemUtils.ts +++ b/vite/src/utils/product/productItemUtils.ts @@ -1,9 +1,9 @@ import { BillingInterval, EntInterval, - Feature, + type Feature, Infinite, - ProductItem, + type ProductItem, ProductItemFeatureType, ProductItemType, UsageModel, @@ -13,103 +13,105 @@ import { isFeatureItem, isFeaturePriceItem, isPriceItem } from "./getItemType"; import { itemToUsageType } from "./productItemUtils/convertItem"; export const itemIsUnlimited = (item: ProductItem) => { - return item.included_usage == Infinite; + return item.included_usage === Infinite; }; export const formatAmount = ({ - defaultCurrency, - amount, - maxFractionDigits = 6, + defaultCurrency, + amount, + maxFractionDigits = 6, }: { - defaultCurrency: string; - amount: number; - maxFractionDigits?: number; + defaultCurrency: string; + amount: number; + maxFractionDigits?: number; }) => { - return new Intl.NumberFormat("en-US", { - style: "currency", - currency: defaultCurrency, - minimumFractionDigits: 0, - maximumFractionDigits: maxFractionDigits || 6, - }).format(amount); + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: defaultCurrency, + minimumFractionDigits: 0, + maximumFractionDigits: maxFractionDigits || 6, + }).format(amount); }; export const getItemType = (item: ProductItem) => { - if (isPriceItem(item)) { - return ProductItemType.Price; - } else if (isFeatureItem(item)) { - return ProductItemType.Feature; - } + if (isPriceItem(item)) { + return ProductItemType.Price; + } else if (isFeatureItem(item)) { + return ProductItemType.Feature; + } - return ProductItemType.FeaturePrice; + return ProductItemType.FeaturePrice; }; -export const intervalIsNone = (interval: any) => { - return ( - nullish(interval) || - interval == EntInterval.Lifetime || - interval == BillingInterval.OneOff - ); +export const intervalIsNone = ( + interval: EntInterval | BillingInterval | null | undefined, +) => { + return ( + nullish(interval) || + interval === EntInterval.Lifetime || + interval === BillingInterval.OneOff + ); }; export const getShowParams = (item: ProductItem | null) => { - if (!item) { - return { - price: false, - feature: false, - allowance: false, - perEntity: false, - cycle: false, - }; - } + if (!item) { + return { + price: false, + feature: false, + allowance: false, + perEntity: false, + cycle: false, + }; + } - return { - price: notNullish(item.price) || notNullish(item.tiers), - feature: !isPriceItem(item), - allowance: true, - perEntity: notNullish(item.entity_feature_id), - cycle: true, - }; + return { + price: notNullish(item.price) || notNullish(item.tiers), + feature: !isPriceItem(item), + allowance: true, + perEntity: notNullish(item.entity_feature_id), + cycle: true, + }; }; export const shouldShowProrationConfig = ({ - item, - features, + item, + features, }: { - item: ProductItem; - features: Feature[]; + item: ProductItem; + features: Feature[]; }) => { - if (!isFeaturePriceItem(item)) return false; + if (!isFeaturePriceItem(item)) return false; - // If pay per use single use - const usageType = itemToUsageType({ item, features }); + // If pay per use single use + const usageType = itemToUsageType({ item, features }); - if (item.usage_model == UsageModel.Prepaid) return true; + if (item.usage_model === UsageModel.Prepaid) return true; - // if ( - // usageType == ProductItemFeatureType.SingleUse && - // item.usage_model == UsageModel.Prepaid - // ) { - // return true; - // } else + // if ( + // usageType == ProductItemFeatureType.SingleUse && + // item.usage_model == UsageModel.Prepaid + // ) { + // return true; + // } else - if ( - usageType == ProductItemFeatureType.ContinuousUse - // &&item.usage_model !== UsageModel.Prepaid - ) { - return true; - } - return false; + if ( + usageType === ProductItemFeatureType.ContinuousUse + // &&item.usage_model !== UsageModel.Prepaid + ) { + return true; + } + return false; }; export const itemsHaveSameInterval = ({ - item1, - item2, + item1, + item2, }: { - item1: ProductItem; - item2: ProductItem; + item1: ProductItem; + item2: ProductItem; }) => { - return ( - item1.interval == item2.interval && - (item1.interval_count || 1) == (item2.interval_count || 1) - ); + return ( + item1.interval === item2.interval && + (item1.interval_count || 1) === (item2.interval_count || 1) + ); }; diff --git a/vite/src/views/customers/customer/customer-product-list/CusProductStatus.tsx b/vite/src/views/customers/customer/customer-product-list/CusProductStatus.tsx index e6aa8821e..a2d84e866 100644 --- a/vite/src/views/customers/customer/customer-product-list/CusProductStatus.tsx +++ b/vite/src/views/customers/customer/customer-product-list/CusProductStatus.tsx @@ -1,68 +1,66 @@ +import { CusProductStatus, type FullCusProduct } from "@autumn/shared"; +import { differenceInDays } from "date-fns"; import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; -import { CusProductStatus, FullCusProduct } from "@autumn/shared"; -import { CusProductStripeLink } from "./CusProductStripeLink"; import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; -import { formatUnixToDateTime } from "@/utils/formatUtils/formatDateUtils"; -import { notNullish } from "@/utils/genUtils"; -import { differenceInDays, subDays } from "date-fns"; +import { CusProductStripeLink } from "./CusProductStripeLink"; export const CusProductStatusItem = ({ - cusProduct, + cusProduct, }: { - cusProduct: FullCusProduct; + cusProduct: FullCusProduct; }) => { - const getStatus = () => { - if (cusProduct.status == CusProductStatus.Expired) { - return CusProductStatus.Expired; - } + const getStatus = () => { + if (cusProduct.status == CusProductStatus.Expired) { + return CusProductStatus.Expired; + } - const trialing = - cusProduct.trial_ends_at && cusProduct.trial_ends_at > Date.now(); + const trialing = + cusProduct.trial_ends_at && cusProduct.trial_ends_at > Date.now(); - const canceled = cusProduct.canceled; + const canceled = cusProduct.canceled; - if (canceled) return "canceled"; + if (canceled) return "canceled"; - if (trialing) { - return CusProductStatus.Trialing; - } + if (trialing) { + return CusProductStatus.Trialing; + } - return cusProduct.status; - }; + return cusProduct.status; + }; - const getTitle = () => { - const status = getStatus(); - if (status == CusProductStatus.Trialing) { - const daysTillEnd = differenceInDays( - new Date(cusProduct.trial_ends_at!), - new Date() - ); - return `trial (${daysTillEnd}d)`; - } - return keyToTitle(getStatus()).toLowerCase(); - }; + const getTitle = () => { + const status = getStatus(); + if (status === CusProductStatus.Trialing) { + const daysTillEnd = differenceInDays( + new Date(cusProduct.trial_ends_at!), + new Date(), + ); + return `trial (${daysTillEnd}d)`; + } + return keyToTitle(getStatus()).toLowerCase(); + }; - const statusToColor: Record = { - [CusProductStatus.Active]: "bg-lime-500", - [CusProductStatus.Expired]: "bg-stone-800", - [CusProductStatus.PastDue]: "bg-red-500", - [CusProductStatus.Scheduled]: "bg-blue-500", - [CusProductStatus.Trialing]: "bg-blue-400", - canceled: "bg-gray-500", - [CusProductStatus.Unknown]: "bg-gray-500", - }; + const statusToColor: Record = { + [CusProductStatus.Active]: "bg-lime-500", + [CusProductStatus.Expired]: "bg-stone-800", + [CusProductStatus.PastDue]: "bg-red-500", + [CusProductStatus.Scheduled]: "bg-blue-500", + [CusProductStatus.Trialing]: "bg-blue-400", + canceled: "bg-gray-500", + [CusProductStatus.Unknown]: "bg-gray-500", + }; - return ( -
- - {getTitle()} - + return ( +
+ + {getTitle()} + - -
- ); + +
+ ); }; diff --git a/vite/src/views/customers/customer/entitlements/UpdateCusEntitlement.tsx b/vite/src/views/customers/customer/entitlements/UpdateCusEntitlement.tsx index 4877a547d..2e7c7ccd3 100644 --- a/vite/src/views/customers/customer/entitlements/UpdateCusEntitlement.tsx +++ b/vite/src/views/customers/customer/entitlements/UpdateCusEntitlement.tsx @@ -22,6 +22,7 @@ import { getBackendErr, notNullish } from "@/utils/genUtils"; import CopyButton from "@/components/general/CopyButton"; import { AlertCircle, Info, InfoIcon } from "lucide-react"; import { useCusQuery } from "../hooks/useCusQuery"; +import { getCusEntBalance } from "@autumn/shared"; function UpdateCusEntitlement({ selectedCusEntitlement, @@ -32,17 +33,40 @@ function UpdateCusEntitlement({ }) { const { customer, refetch } = useCusQuery(); const { entityId } = useCustomerContext(); - // const { customer, env, cusMutate, entityId } = useCustomerContext(); + const cusEnt = selectedCusEntitlement; + console.log( + "Balance: ", + cusEnt + ? getCusEntBalance({ + cusEnt: cusEnt!, + entityId, + }).balance + : null + ); + const [updateLoading, setUpdateLoading] = useState(false); const axiosInstance = useAxiosInstance(); + console.log( + `Cus ent: ${cusEnt?.entitlement.feature_id}, Balances: `, + cusEnt + ? getCusEntBalance({ + cusEnt: cusEnt!, + entityId, + }) + : null + ); + const [updateFields, setUpdateFields] = useState({ - balance: - entityId && notNullish(cusEnt?.entities?.[entityId]?.balance) - ? cusEnt?.entities?.[entityId]?.balance - : cusEnt?.balance, + balance: cusEnt + ? getCusEntBalance({ + cusEnt, + entityId, + }).balance + : null, + next_reset_at: cusEnt?.next_reset_at, }); @@ -55,10 +79,12 @@ function UpdateCusEntitlement({ useEffect(() => { setUpdateFields({ - balance: - entityId && notNullish(cusEnt?.entities?.[entityId]?.balance) - ? cusEnt?.entities?.[entityId]?.balance - : cusEnt?.balance, + balance: cusEnt + ? getCusEntBalance({ + cusEnt: cusEnt!, + entityId, + }).balance + : null, next_reset_at: cusEnt?.next_reset_at, }); }, [selectedCusEntitlement]); diff --git a/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx b/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx index bcbe3d546..6c12dd2d3 100644 --- a/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx +++ b/vite/src/views/customers/customer/product/components/attach-preview/DueNextCycle.tsx @@ -1,4 +1,10 @@ -import { AttachBranch, Feature, features } from "@autumn/shared"; +import { + AttachBranch, + Feature, + features, + getAmountForQuantity, + Price, +} from "@autumn/shared"; import { PriceItem } from "@/components/pricing/attach-pricing-dialog"; import { formatUnixToDate } from "@/utils/formatUtils/formatDateUtils"; @@ -20,9 +26,30 @@ export const DueNextCycle = () => { const getPrepaidPrice = ({ option }: { option: any }) => { const quantity = (option.quantity || 0) / option.billing_units; + // Handle tiered pricing + if (option.tiers) { + const amount = getAmountForQuantity({ + price: { + config: { + usage_tiers: option.full_tiers, + billing_units: option.billing_units, + }, + } as Price, + quantity: option.quantity || 0, + }); + + return formatAmount({ + amount, + currency, + maxFractionDigits: 2, + }); + } + + // Handle fixed pricing return formatAmount({ amount: option.full_price * quantity, currency, + maxFractionDigits: 2, }); }; diff --git a/vite/src/views/customers/customer/product/components/attach-preview/UpdateQuantity.tsx b/vite/src/views/customers/customer/product/components/attach-preview/UpdateQuantity.tsx index 0c111bd15..e95f8af81 100644 --- a/vite/src/views/customers/customer/product/components/attach-preview/UpdateQuantity.tsx +++ b/vite/src/views/customers/customer/product/components/attach-preview/UpdateQuantity.tsx @@ -4,7 +4,8 @@ import { useOrg } from "@/hooks/common/useOrg"; import { notNullish } from "@/utils/genUtils"; import { formatAmount } from "@/utils/product/productItemUtils"; import { useProductContext } from "@/views/products/product/ProductContext"; -import { AttachBranch } from "@autumn/shared"; +import { AttachBranch, getAmountForQuantity, Price } from "@autumn/shared"; +import { Decimal } from "decimal.js"; export const UpdateQuantity = () => { const { org } = useOrg(); @@ -17,12 +18,38 @@ export const UpdateQuantity = () => { const currency = org?.default_currency || "USD"; + const getPrepaidPrice = ({ option }: { option: any }) => { + if (notNullish(option.price)) { + return `x ${formatAmount({ + amount: option.price, + defaultCurrency: currency, + maxFractionDigits: 5, + })} per `; + } + + if (option.tiers) { + const start = option.tiers[0].amount; + const end = option.tiers[option.tiers.length - 1].amount; + return "x "; + // return `${formatAmount({ + // amount: start, + // defaultCurrency: currency, + // maxFractionDigits: 5, + // })} - ${formatAmount({ + // amount: end, + // defaultCurrency: currency, + // maxFractionDigits: 5, + // })} `; + } + + return ""; + }; + const getTotalPrice = () => { return options.reduce((acc: number, option: any) => { const currentQuantity = option.current_quantity || 0; const newQuantity = option.quantity || 0; - let difference = newQuantity - currentQuantity; - difference = difference / option.billing_units; + const difference = newQuantity - currentQuantity; const isDecrease = newQuantity < currentQuantity; @@ -30,7 +57,42 @@ export const UpdateQuantity = () => { return acc; } - return acc + option.price * difference; + let priceForDifference = 0; + + // Handle tiered pricing + if (option.tiers) { + // Calculate amount for new quantity + const newAmount = getAmountForQuantity({ + price: { + config: { + usage_tiers: option.tiers, + billing_units: option.billing_units, + }, + } as Price, + quantity: newQuantity, + }); + + // Calculate amount for current quantity + const currentAmount = getAmountForQuantity({ + price: { + config: { + usage_tiers: option.tiers, + billing_units: option.billing_units, + }, + } as Price, + quantity: currentQuantity, + }); + + priceForDifference = new Decimal(newAmount) + .minus(currentAmount) + .toNumber(); + } else { + // Handle fixed pricing + const differenceInUnits = difference / option.billing_units; + priceForDifference = option.price * differenceInUnits; + } + + return acc + priceForDifference; }, 0); }; @@ -69,13 +131,14 @@ export const UpdateQuantity = () => { /> - ×{" "} - {formatAmount({ + {/* ×{" "} */} + {/* {formatAmount({ defaultCurrency: currency, amount: price, maxFractionDigits: 2, - })}{" "} - per {billing_units === 1 ? " " : billing_units} {feature_name} + })}{" "} */} + {getPrepaidPrice({ option })} + {billing_units === 1 ? " " : billing_units} {feature_name} diff --git a/vite/src/views/customers/customer/product/components/attachProductUtils.ts b/vite/src/views/customers/customer/product/components/attachProductUtils.ts index d97f5ad2a..f48d6ee55 100644 --- a/vite/src/views/customers/customer/product/components/attachProductUtils.ts +++ b/vite/src/views/customers/customer/product/components/attachProductUtils.ts @@ -27,8 +27,10 @@ export const getAttachBody = ({ successUrl?: string; version?: number; }) => { - const isCustom = attachState.itemsChanged; - const customData = attachState.itemsChanged + const isCustom = + attachState.itemsChanged || attachState.cusProduct?.is_custom; + + const customData = isCustom ? { items: product.items, free_trial: product.free_trial, diff --git a/vite/src/views/customers/customer/product/hooks/useAttachState.tsx b/vite/src/views/customers/customer/product/hooks/useAttachState.tsx index 5bf2fcea7..a1a05f81b 100644 --- a/vite/src/views/customers/customer/product/hooks/useAttachState.tsx +++ b/vite/src/views/customers/customer/product/hooks/useAttachState.tsx @@ -194,5 +194,7 @@ export const useAttachState = ({ flags, setFlags, + + cusProduct, }; }; diff --git a/vite/src/views/developer/configure-stripe/ConfigureStripe.tsx b/vite/src/views/developer/configure-stripe/ConfigureStripe.tsx index 9bc497d00..3f536a95c 100644 --- a/vite/src/views/developer/configure-stripe/ConfigureStripe.tsx +++ b/vite/src/views/developer/configure-stripe/ConfigureStripe.tsx @@ -153,7 +153,7 @@ export const ConfigureStripe = () => { /> ) : ( setNewStripeConfig({ diff --git a/vite/src/views/products/rewards/reward-config/CreateReward.tsx b/vite/src/views/products/rewards/reward-config/CreateReward.tsx index 8be756b25..0b31f7aa7 100644 --- a/vite/src/views/products/rewards/reward-config/CreateReward.tsx +++ b/vite/src/views/products/rewards/reward-config/CreateReward.tsx @@ -1,83 +1,83 @@ +import { useEffect, useState } from "react"; +import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { - Dialog, - DialogContent, - DialogTrigger, - DialogHeader, - DialogTitle, - DialogFooter, + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; - -import React, { useEffect, useState } from "react"; - -import { useAxiosInstance } from "@/services/useAxiosInstance"; -import { toast } from "sonner"; - -import { getBackendErr } from "@/utils/genUtils"; -import { useProductsContext } from "../../ProductsContext"; -import { RewardService } from "@/services/products/RewardService"; -import { RewardConfig } from "./RewardConfig"; -import { defaultReward } from "../utils/defaultRewardModels"; import { useRewardsQuery } from "@/hooks/queries/useRewardsQuery"; +import { RewardService } from "@/services/products/RewardService"; +import { useAxiosInstance } from "@/services/useAxiosInstance"; +import { getBackendErr } from "@/utils/genUtils"; +import { defaultReward } from "../utils/defaultRewardModels"; +import { RewardConfig } from "./RewardConfig"; function CreateReward() { - const axiosInstance = useAxiosInstance(); - const { refetch } = useRewardsQuery(); + const axiosInstance = useAxiosInstance(); + const { refetch } = useRewardsQuery(); - const [isLoading, setIsLoading] = useState(false); - const [open, setOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [open, setOpen] = useState(false); - const [reward, setReward] = useState(defaultReward); + const [reward, setReward] = useState(defaultReward); - useEffect(() => { - if (open) { - setReward(defaultReward); - } - }, [open]); + useEffect(() => { + if (open) { + setReward(defaultReward); + } + }, [open]); - const handleCreate = async () => { - if (!reward?.id && !reward?.name) { - toast.error("ID and name are required"); - return; - } - setIsLoading(true); - try { - await RewardService.createReward({ - axiosInstance, - data: reward, - }); + const handleCreate = () => { + setIsLoading(true); + (async () => { + if (!reward?.id && !reward?.name) { + toast.error("ID and name are required"); + return; + } - await refetch(); - setOpen(false); - } catch (error) { - console.log("Error:", error); - toast.error(getBackendErr(error, "Failed to create coupon")); - } - setIsLoading(false); - }; + try { + await RewardService.createReward({ + axiosInstance, + data: reward, + }); - return ( - - - - - - - Create Reward - - - - - - - - ); + await refetch(); + setOpen(false); + } catch (error) { + console.log("Error:", error); + toast.error(getBackendErr(error, "Failed to create coupon")); + } finally { + setIsLoading(false); + } + })(); + }; + + return ( + + + + + + + Create Reward + + + + + + + + ); } export default CreateReward; diff --git a/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx b/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx index 277a27e60..bada9593c 100644 --- a/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx +++ b/vite/src/views/products/rewards/reward-config/DiscountConfig.tsx @@ -38,93 +38,93 @@ import { isFeatureItem } from "@/utils/product/getItemType"; import { formatProductItemText } from "@/utils/product/product-item/formatProductItem"; export const DiscountConfig = ({ - reward, - setReward, + reward, + setReward, }: { - reward: Reward; - setReward: (reward: Reward) => void; + reward: Reward; + setReward: (reward: Reward) => void; }) => { - const { org } = useOrg(); + const { org } = useOrg(); - const config = reward.discount_config!; - const setConfig = (key: any, value: any) => { - setReward({ - ...reward, - discount_config: { ...config, [key]: value }, - }); - }; + const config = reward.discount_config!; + const setConfig = (key: any, value: any) => { + setReward({ + ...reward, + discount_config: { ...config, [key]: value }, + }); + }; - return ( -
-
-
- Amount - - setConfig("discount_value", Number(e.target.value)) - } - endContent={ -

- {reward.type === RewardType.PercentageDiscount - ? "%" - : org?.default_currency || "USD"} -

- } - /> -
-
- Duration -
- {config.duration_type === CouponDurationType.Months && ( - { - setConfig("duration_value", Number(e.target.value)); - }} - type="number" - /> - )} - -
-
-
+ return ( +
+
+
+ Amount + + setConfig("discount_value", Number(e.target.value)) + } + endContent={ +

+ {reward.type === RewardType.PercentageDiscount + ? "%" + : org?.default_currency || "USD"} +

+ } + /> +
+
+ Duration +
+ {config.duration_type === CouponDurationType.Months && ( + { + setConfig("duration_value", Number(e.target.value)); + }} + type="number" + /> + )} + +
+
+
- {/* {config.duration_type !== CouponDurationType.OneOff && + {/* {config.duration_type !== CouponDurationType.OneOff && reward.type === RewardType.FixedDiscount && (
)} */} -
- {/*

Products

*/} - Products +
+ {/*

Products

*/} + Products - -
-
- ); + +
+
+ ); }; const ProductPriceSelector = ({ - reward, - setReward, + reward, + setReward, }: { - reward: Reward; - setReward: (reward: Reward) => void; + reward: Reward; + setReward: (reward: Reward) => void; }) => { - const { org } = useOrg(); - const { products } = useProductsQuery(); - const { features } = useFeaturesQuery(); + const { org } = useOrg(); + const { products } = useProductsQuery(); + const { features } = useFeaturesQuery(); - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false); - const config = reward.discount_config!; - const setConfig = (key: any, value: any) => { - setReward({ - ...reward, - discount_config: { ...config, [key]: value }, - }); - }; + const config = reward.discount_config!; + const setConfig = (key: any, value: any) => { + setReward({ + ...reward, + discount_config: { ...config, [key]: value }, + }); + }; - // Handle selection/deselection of a price - const handlePriceToggle = (priceId: string) => { - let newPriceIds = [...(config.price_ids || [])]; - if (config.price_ids?.includes(priceId)) { - newPriceIds = config.price_ids?.filter((id) => id !== priceId) || []; - } else { - newPriceIds = [...(config.price_ids || []), priceId]; - } - setConfig("price_ids", newPriceIds); - }; + // Handle selection/deselection of a price + const handlePriceToggle = (priceId: string) => { + let newPriceIds = [...(config.price_ids || [])]; + if (config.price_ids?.includes(priceId)) { + newPriceIds = config.price_ids?.filter((id) => id !== priceId) || []; + } else { + newPriceIds = [...(config.price_ids || []), priceId]; + } + setConfig("price_ids", newPriceIds); + }; - if (!products || products.length === 0) { - return

No products available

; - } + if (!products || products.length === 0) { + return

No products available

; + } - return ( - - - -
- ); - })} - - - - - - - - - No prices found. - - { - setConfig("apply_to_all", !config.apply_to_all); - }} - className="cursor-pointer" - > -

Apply to all products

- {config.apply_to_all && ( - - )} -
-
- {!config.apply_to_all && - products.map((product: any) => ( - - {product.items.length > 0 ? ( - product.items - ?.filter((item: ProductItem) => { - return !isFeatureItem(item); - }) - .map((item: any) => ( - handlePriceToggle(item.price_id)} - className="cursor-pointer overflow-x-hidden max-w-[380px]" - > - - {formatProductItemText({ - item, - org, - features, - })} - + const text = item + ? formatProductItemText({ + item, + org, + features, + }) + : "Unknown Price"; + return ( +
+

{text}

+ +
+ ); + })} + + + + + + + + + No prices found. + + { + setConfig("apply_to_all", !config.apply_to_all); + }} + className="cursor-pointer" + > +

Apply to all products

+ {config.apply_to_all && ( + + )} +
+
+ {!config.apply_to_all && + products.map((product: any) => ( + + {product.items.length > 0 ? ( + product.items + ?.filter((item: ProductItem) => { + return !isFeatureItem(item); + }) + .map((item: any) => ( + handlePriceToggle(item.price_id)} + className="cursor-pointer overflow-x-hidden max-w-[380px]" + > + + {formatProductItemText({ + item, + org, + features, + })} + - {config.price_ids?.includes(item.price_id) && ( - - )} - - )) - ) : ( - -

No prices available

-
- )} -
- ))} -
-
-
-
- - ); + {config.price_ids?.includes(item.price_id) && ( + + )} +
+ )) + ) : ( + +

No prices available

+
+ )} +
+ ))} +
+
+
+
+ + ); }; diff --git a/vite/src/views/products/rewards/reward-config/FreeDurationSelect.tsx b/vite/src/views/products/rewards/reward-config/FreeDurationSelect.tsx new file mode 100644 index 000000000..1d4831a13 --- /dev/null +++ b/vite/src/views/products/rewards/reward-config/FreeDurationSelect.tsx @@ -0,0 +1,73 @@ +import { CouponDurationType, type Reward } from "@autumn/shared"; +import FieldLabel from "@/components/general/modal-components/FieldLabel"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; + +export const FreeDurationSelect = ({ + reward, + setReward, +}: { + reward: Reward; + setReward: (reward: Reward) => void; +}) => { + return ( +
+ Duration +
+ { + { + setReward({ + ...reward, + // @ts-expect-error + free_product_config: { + ...(reward.free_product_config ?? {}), + duration_value: Number(e.target.value), + }, + }); + }} + type="number" + min={1} + max={12} + /> + } + +
+
+ ); +}; diff --git a/vite/src/views/products/rewards/reward-config/RewardConfig.tsx b/vite/src/views/products/rewards/reward-config/RewardConfig.tsx index 32caff09f..6da6a9ae4 100644 --- a/vite/src/views/products/rewards/reward-config/RewardConfig.tsx +++ b/vite/src/views/products/rewards/reward-config/RewardConfig.tsx @@ -1,10 +1,12 @@ import { type ProductV2, type Reward, - RewardType + RewardType, + UsageModel, } from "@autumn/shared"; import { useEffect, useState } from "react"; import FieldLabel from "@/components/general/modal-components/FieldLabel"; +import { WarningBox } from "@/components/general/modal-components/WarningBox"; import { Input } from "@/components/ui/input"; import { Select, @@ -13,141 +15,148 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { useOrg } from "@/hooks/common/useOrg"; import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; -import { keyToTitle, slugify } from "@/utils/formatUtils/formatTextUtils"; +import { + formatAmount as formatCurrency, + keyToTitle, + slugify, +} from "@/utils/formatUtils/formatTextUtils"; import { notNullish } from "@/utils/genUtils"; -import { isFreeProduct } from "@/utils/product/priceUtils"; +import { isFeaturePriceItem, isPriceItem } from "@/utils/product/getItemType"; +import { isFreeProduct, isOneOffProduct } from "@/utils/product/priceUtils"; import { defaultDiscountConfig } from "../utils/defaultRewardModels"; import { DiscountConfig } from "./DiscountConfig"; +import { FreeDurationSelect } from "./FreeDurationSelect"; export const RewardConfig = ({ - reward, - setReward, + reward, + setReward, }: { - reward: Reward; - setReward: (reward: Reward) => void; + reward: Reward; + setReward: (reward: Reward) => void; }) => { - const [idChanged, setIdChanged] = useState(false); - const { products } = useProductsQuery(); + const [idChanged, setIdChanged] = useState(false); + const { products } = useProductsQuery(); - useEffect(() => { - if (!idChanged) { - setReward({ - ...reward, - id: slugify(reward.name || ""), - }); - } - }, [idChanged, reward, setReward]); + useEffect(() => { + if (!idChanged) { + setReward({ + ...reward, + id: slugify(reward.name || ""), + }); + } + }, [idChanged, reward, setReward]); - return ( -
-
-
- Name - setReward({ ...reward, name: e.target.value })} - /> -
-
- - ID - - { - setReward({ ...reward, id: e.target.value }); - setIdChanged(true); - }} - /> -
-
-
-
- Promotional Code - 0 ? reward.promo_codes[0].code : "" - } - onChange={(e) => - setReward({ - ...reward, - promo_codes: [{ code: e.target.value }], - }) - } - /> -
-
- Type - -
-
- {reward.type === RewardType.FreeProduct ? ( -
- - Product - - setReward({ ...reward, name: e.target.value })} + /> +
+
+ + ID + + { + setReward({ ...reward, id: e.target.value }); + setIdChanged(true); + }} + /> +
+
+
+
+ Promotional Code + 0 ? reward.promo_codes[0].code : "" + } + onChange={(e) => + setReward({ + ...reward, + promo_codes: [{ code: e.target.value }], + }) + } + /> +
+
+ Type + +
+
+ {reward.type === RewardType.FreeProduct ? ( +
+ + Product + + -
- ) : notNullish(reward.type) ? ( - - ) : null} - - ); + const empty = freeAddOns.length === 0; + return ( + <> + + + + + {freeAddOns.map((product: ProductV2) => ( + + {product.name} + + ))} + + + ); + })()} + + + ) : notNullish(reward.type) ? ( + + ) : null} + + ); }; diff --git a/vite/src/views/products/rewards/reward-programs/CreateRewardProgram.tsx b/vite/src/views/products/rewards/reward-programs/CreateRewardProgram.tsx index adda88548..0b6918357 100644 --- a/vite/src/views/products/rewards/reward-programs/CreateRewardProgram.tsx +++ b/vite/src/views/products/rewards/reward-programs/CreateRewardProgram.tsx @@ -1,90 +1,92 @@ +import type { CreateRewardProgram } from "@autumn/shared"; +import { RewardReceivedBy, RewardTriggerEvent } from "@autumn/shared"; +import { useEffect, useState } from "react"; +import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { - Dialog, - DialogContent, - DialogTrigger, - DialogHeader, - DialogTitle, - DialogFooter, + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, } from "@/components/ui/dialog"; -import { useEffect, useState } from "react"; -import { useAxiosInstance } from "@/services/useAxiosInstance"; -import { toast } from "sonner"; - -import { RewardTriggerEvent, RewardReceivedBy } from "@autumn/shared"; -import { getBackendErr } from "@/utils/genUtils"; import { useRewardsQuery } from "@/hooks/queries/useRewardsQuery"; -import { CreateRewardProgram } from "@autumn/shared"; +import { useAxiosInstance } from "@/services/useAxiosInstance"; +import { getBackendErr } from "@/utils/genUtils"; import { RewardProgramConfig } from "./RewardProgramConfig"; const defaultRewardProgram: CreateRewardProgram = { - id: "", - // trigger: { - // type: RewardTriggerEvent.SignUp, - // product_ids: [], - // exclude_trial: false, - // }, - when: RewardTriggerEvent.CustomerCreation, - product_ids: [], - exclude_trial: false, - internal_reward_id: "", - max_redemptions: 0, - received_by: RewardReceivedBy.Referrer, + id: "", + // trigger: { + // type: RewardTriggerEvent.SignUp, + // product_ids: [], + // exclude_trial: false, + // }, + when: RewardTriggerEvent.CustomerCreation, + product_ids: [], + exclude_trial: false, + internal_reward_id: "", + max_redemptions: 0, + received_by: RewardReceivedBy.Referrer, }; function CreateRewardProgramModal() { - const { refetch } = useRewardsQuery(); - const axiosInstance = useAxiosInstance(); + const { refetch } = useRewardsQuery(); + const axiosInstance = useAxiosInstance(); - const [isLoading, setIsLoading] = useState(false); - const [open, setOpen] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [open, setOpen] = useState(false); - const [rewardProgram, setRewardProgram] = useState(defaultRewardProgram); + const [rewardProgram, setRewardProgram] = useState(defaultRewardProgram); - useEffect(() => { - if (open) { - setRewardProgram(defaultRewardProgram); - } - }, [open]); + useEffect(() => { + if (open) { + setRewardProgram(defaultRewardProgram); + } + }, [open]); - const handleCreate = async () => { - setIsLoading(true); - try { - await axiosInstance.post("/v1/reward_programs", rewardProgram); + const handleCreate = () => { + setIsLoading(true); + (async () => { + try { + await axiosInstance.post("/v1/reward_programs", rewardProgram); - await refetch(); - setOpen(false); - } catch (error) { - toast.error(getBackendErr(error, "Failed to create referral program")); - } - setIsLoading(false); - }; + await refetch(); + setOpen(false); + } catch (error) { + toast.error(getBackendErr(error, "Failed to create referral program")); + } finally { + setIsLoading(false); + } + })(); + }; - return ( - - - - - - - Create Referral Program - - - - - - - - ); + return ( + + + + + + + Create Referral Program + + + + + + + + ); } export default CreateRewardProgramModal; diff --git a/vite/src/views/products/rewards/reward-programs/RewardProgramConfig.tsx b/vite/src/views/products/rewards/reward-programs/RewardProgramConfig.tsx index 340b5954f..ce898447a 100644 --- a/vite/src/views/products/rewards/reward-programs/RewardProgramConfig.tsx +++ b/vite/src/views/products/rewards/reward-programs/RewardProgramConfig.tsx @@ -1,258 +1,257 @@ +import { + type Reward, + type RewardProgram, + RewardReceivedBy, + RewardTriggerEvent, +} from "@autumn/shared"; +import { Check, ChevronsUpDown, X } from "lucide-react"; +import { useState } from "react"; import FieldLabel from "@/components/general/modal-components/FieldLabel"; +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; import { Input } from "@/components/ui/input"; import { - Select, - SelectTrigger, - SelectContent, - SelectItem, - SelectValue, -} from "@/components/ui/select"; -import { - Reward, - RewardProgram, - RewardTriggerEvent, - RewardReceivedBy, -} from "@autumn/shared"; -import { useProductsContext } from "../../ProductsContext"; -import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; -import { useState } from "react"; -import { - Popover, - PopoverContent, - PopoverTrigger, + Popover, + PopoverContent, + PopoverTrigger, } from "@/components/ui/popover"; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command"; import { ScrollArea } from "@/components/ui/scroll-area"; -import { Button } from "@/components/ui/button"; -import { Check, ChevronsUpDown, X } from "lucide-react"; -import { useRewardsQuery } from "@/hooks/queries/useRewardsQuery"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { useProductsQuery } from "@/hooks/queries/useProductsQuery"; +import { useRewardsQuery } from "@/hooks/queries/useRewardsQuery"; +import { keyToTitle } from "@/utils/formatUtils/formatTextUtils"; export const RewardProgramConfig = ({ - rewardProgram, - setRewardProgram, + rewardProgram, + setRewardProgram, }: { - rewardProgram: RewardProgram; - setRewardProgram: (rewardProgram: RewardProgram) => void; + rewardProgram: RewardProgram; + setRewardProgram: (rewardProgram: RewardProgram) => void; }) => { - const { rewards } = useRewardsQuery(); + const { rewards } = useRewardsQuery(); - return ( -
-
-
- Program ID - - setRewardProgram({ ...rewardProgram, id: e.target.value }) - } - /> -
-
- Reward - -
-
-
-
- Redeem On - -
-
- Max Redemptions - - setRewardProgram({ - ...rewardProgram, - max_redemptions: parseInt(e.target.value), - }) - } - /> -
-
-
-
- Received by - -
-
-
- {rewardProgram.when === RewardTriggerEvent.Checkout && ( -
- Products - -
- )} -
-
- ); + return ( +
+
+
+ Program ID + + setRewardProgram({ ...rewardProgram, id: e.target.value }) + } + /> +
+
+ Reward + +
+
+
+
+ Redeem On + +
+
+ Max Redemptions + + setRewardProgram({ + ...rewardProgram, + max_redemptions: parseInt(e.target.value), + }) + } + /> +
+
+
+
+ Received by + +
+
+
+ {rewardProgram.when === RewardTriggerEvent.Checkout && ( +
+ Products + +
+ )} +
+
+ ); }; const ProductSelector = ({ - rewardProgram, - setRewardProgram, + rewardProgram, + setRewardProgram, }: { - rewardProgram: RewardProgram; - setRewardProgram: (rewardProgram: RewardProgram) => void; + rewardProgram: RewardProgram; + setRewardProgram: (rewardProgram: RewardProgram) => void; }) => { - const { products } = useProductsQuery(); - const [open, setOpen] = useState(false); + const { products } = useProductsQuery(); + const [open, setOpen] = useState(false); - // Handle selection/deselection of a product - const handleProductToggle = (productId: string) => { - let newProductIds = [...(rewardProgram.product_ids || [])]; - if (newProductIds.includes(productId)) { - newProductIds = newProductIds.filter((id) => id !== productId); - } else { - newProductIds = [...newProductIds, productId]; - } - setRewardProgram({ - ...rewardProgram, - product_ids: newProductIds, - }); - }; + // Handle selection/deselection of a product + const handleProductToggle = (productId: string) => { + let newProductIds = [...(rewardProgram.product_ids || [])]; + if (newProductIds.includes(productId)) { + newProductIds = newProductIds.filter((id) => id !== productId); + } else { + newProductIds = [...newProductIds, productId]; + } + setRewardProgram({ + ...rewardProgram, + product_ids: newProductIds, + }); + }; - if (!products || products.length === 0) { - return

No products available

; - } + if (!products || products.length === 0) { + return

No products available

; + } - const getProductText = (productId: string) => { - const product = products.find((p: any) => p.id === productId); - return product?.name || "Unknown Product"; - }; + const getProductText = (productId: string) => { + const product = products.find((p: any) => p.id === productId); + return product?.name || "Unknown Product"; + }; - return ( - - - - - ))} - - )} - - - - - - - - - No products found. - - {products.map((product: any) => ( - handleProductToggle(product.id)} - className="cursor-pointer" - > -
{product.name}
- {rewardProgram.product_ids?.includes(product.id) && ( - - )} -
- ))} -
-
-
-
-
-
- ); + return ( + + + + + ))} + + )} + + + + + + + + + No products found. + + {products.map((product: any) => ( + handleProductToggle(product.id)} + className="cursor-pointer" + > +
{product.name}
+ {rewardProgram.product_ids?.includes(product.id) && ( + + )} +
+ ))} +
+
+
+
+
+
+ ); }; diff --git a/vite/src/views/products/rewards/utils/defaultRewardModels.ts b/vite/src/views/products/rewards/utils/defaultRewardModels.ts index 0c913ea35..2fdf9ad5a 100644 --- a/vite/src/views/products/rewards/utils/defaultRewardModels.ts +++ b/vite/src/views/products/rewards/utils/defaultRewardModels.ts @@ -2,28 +2,37 @@ import { CouponDurationType, type CreateReward, type DiscountConfig, + type FreeProductConfig, RewardType, } from "@autumn/shared"; export const defaultDiscountConfig: DiscountConfig = { - discount_value: 0, - duration_type: CouponDurationType.Months, - duration_value: 0, - should_rollover: true, - apply_to_all: false, - price_ids: [], + discount_value: 0, + duration_type: CouponDurationType.Months, + duration_value: 0, + should_rollover: true, + apply_to_all: true, + price_ids: [], +}; + +export const defaultFreeProductConfig: FreeProductConfig = { + duration_type: CouponDurationType.Months, + duration_value: 0, }; export const defaultReward: CreateReward = { - name: "", - id: "", - promo_codes: [{ code: "" }], + name: "", + id: "", + promo_codes: [{ code: "" }], - type: RewardType.PercentageDiscount, + type: RewardType.PercentageDiscount, - // For free product coupons - free_product_id: null, + // For free product coupons + free_product_id: null, - // For discount type coupons - discount_config: defaultDiscountConfig, + // For discount type coupons + discount_config: defaultDiscountConfig, + + // For free product type coupons + free_product_config: defaultFreeProductConfig, };