diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..4edfd5f --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,27 @@ +Dear Copilot, + +## Project Overview + +BottleCRM is a dynamic, SaaS CRM platform designed to streamline the entire CRM needs of startups and enterprises. Built with modern web technologies, it offers a seamless experience for users through robust role-based access control (RBAC). Each user role is equipped with tailored functionalities to enhance efficiency, engagement, and management, ensuring a streamlined and secure business process. + +user types we have + +- Org + - user(s) + - Admin +- super admin - anyone with @micropyramid.com email to manage whole platform + +## Project Context + +BottleCRM is a modern CRM application built with: +- **Framework**: SvelteKit 2.21.x, Svelte 5.1, Prisma +- **Styling**: tailwind 4.1.x css +- **Database**: postgresql +- **Icons**: lucide icons +- **Form Validation**: zod + +## Important Notes +- We need to ensure access control is strictly enforced based on user roles. +- No record should be accessible unless the user or the org has the appropriate permissions. +- When implementing forms in sveltekit A form label must be associated with a control +- svelte 5+ style coding standards should be followed \ No newline at end of file diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml new file mode 100644 index 0000000..92afc5d --- /dev/null +++ b/.github/workflows/build-deploy.yml @@ -0,0 +1,56 @@ +name: Build and Deploy (Docker) + +on: + push: + branches: + - main + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: true + tags: ghcr.io/${{ github.repository }}:latest + + - name: Setup SSH + uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Add host key to known_hosts + run: | + mkdir -p ~/.ssh + ssh-keyscan -H ${{ secrets.SERVER_IP }} >> ~/.ssh/known_hosts + + - name: Deploy on server (pull and restart container) + run: | + ssh ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_IP }} "docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }} && \ + docker pull ghcr.io/${{ github.repository }}:latest && \ + docker stop svelte-crm || true && docker rm svelte-crm || true && \ + docker run -d --name svelte-crm --restart always -p 3000:3000 \ + -e NODE_ENV=production \ + -e DATABASE_URL=\"${{ secrets.DATABASE_URL }}\" \ + --env-file ${{ secrets.ENV_FILE_PATH:-/home/${{ secrets.SERVER_USER }}/.env }} \ + ghcr.io/${{ github.repository }}:latest" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 42381fc..c54de0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,14 @@ +dist node_modules - -# Output -.output -.vercel -.netlify +CLAUDE.md +.cursor +.vscode +.idea .wrangler -/.svelte-kit -/build - -# OS -.DS_Store -Thumbs.db - -# Env +.svelte-kit +build +.dev.vars .env -.env.* -!.env.example -!.env.test - -# Vite -vite.config.js.timestamp-* -vite.config.ts.timestamp-* - -generated/* -src/generated/* +.claude +local-state +.turbo/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6ebefa2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,23 @@ +{ + "editor.tabSize": 2, + "editor.insertSpaces": true, + "[javascript]": { + "editor.tabSize": 2, + "editor.insertSpaces": true + }, + "[svelte]": { + "editor.tabSize": 2, + "editor.insertSpaces": true + }, + "github.copilot.chat.codeGeneration.instructions": [ + { + "file": "prisma/schema.prisma", + }, + { + "file": "src/hooks.server.js", + }, + { + "file": "src/lib/prisma.js", + }, + ] +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ea4eca6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,178 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +BottleCRM is a multi-tenant SaaS CRM platform built as a monorepo with SvelteKit, designed for startups and enterprises with role-based access control (RBAC). The application features organization-based multi-tenancy with strict data isolation enforced at the database level. + +## Technology Stack + +- **Frontend**: SvelteKit 2.x with Svelte 5.x (TypeScript) +- **Styling**: TailwindCSS 4.x +- **Database**: PostgreSQL with Drizzle ORM +- **Authentication**: Better Auth with organization plugin +- **Icons**: Lucide Svelte +- **Validation**: Zod +- **Package Manager**: pnpm (v10.0.0) +- **Build Tool**: Turbo (monorepo management) +- **Deployment**: Cloudflare Workers/Pages + +## Monorepo Structure + +``` +├── apps/ +│ ├── web/ # SvelteKit frontend application +│ └── api/ # Node.js API service (optional) +├── shared/ +│ ├── database/ # Drizzle ORM schema and migrations +│ └── constants/ # Shared constants across apps +└── supabase/ # Supabase configuration (if used) +``` + +## Development Commands + +### Monorepo Root Commands +```bash +# Install dependencies +pnpm install + +# Development (all apps) +pnpm run dev + +# Build (all apps) +pnpm run build + +# Web app specific +pnpm run web:dev +pnpm run web:build +pnpm run web:preview + +# API app specific +pnpm run api:dev +pnpm run api:build +``` + +### Database Commands +```bash +# Generate SQL and types +pnpm run db:generate + +# Run migrations (local) +pnpm run db:migrate:local + +# Run migrations (production) +pnpm run db:migrate:prod + +# Generate, migrate, build in one command (local) +pnpm run db:gmb:local + +# Database studio UI +pnpm run db:studio +``` + +### Web App Commands (from apps/web/) +```bash +# Type checking +pnpm run check +pnpm run check:watch + +# Linting and formatting +pnpm run lint +pnpm run format +``` + +## Architecture Overview + +### Multi-Tenant Structure +- **Organizations**: Top-level tenant containers with complete data isolation +- **Members**: Users belong to organizations with specific roles (member/admin) +- **Sessions**: Track active organization via `activeOrganizationId` +- **Super Admin**: Platform-wide access (determined by business logic, not email domain) + +### Core CRM Entities +- **Leads**: Initial prospects that can be converted to Accounts/Contacts/Opportunities +- **Accounts** (`crm_account`): Company/organization records +- **Contacts**: Individual people associated with accounts +- **Opportunities**: Sales deals with pipeline stages and forecast categories +- **Tasks/Events**: Activity management linked to various entities +- **Cases**: Customer support tickets with priority and status tracking +- **Products/Quotes**: Product catalog and professional quotation system + +### Authentication & Authorization +- **Better Auth**: Session-based authentication with JWT plugin support +- **Organization Context**: Active organization stored in session (`activeOrganizationId`) +- **Route Protection** in `apps/web/src/hooks.server.ts`: + - `/app/*` routes require authentication and organization membership + - `/admin/*` routes require authentication (additional checks in route logic) + - `/org` route for organization selection post-login +- **Database Integration**: Drizzle adapter for Better Auth tables + +### Data Access Control +- All CRM queries must filter by `organizationId` +- Organization membership verified through `member` table +- Strict foreign key constraints enforce data integrity +- Audit logging tracks all data modifications + +### Route Structure +- `(site)`: Public marketing pages +- `(no-layout)`: Authentication pages (login, org selection) +- `(app)`: Main CRM application (requires auth + active org) +- `(admin)`: Platform administration + +### Key Files +- `apps/web/src/hooks.server.ts`: Authentication setup and route guards +- `apps/web/src/lib/auth.ts`: Better Auth configuration +- `shared/database/src/schema/`: Database schema definitions + - `base.ts`: Authentication tables (user, session, organization, member) + - `app.ts`: CRM-specific tables + - `enums.ts`: PostgreSQL enums for type safety + +## Environment Configuration + +### Local Development (.dev.vars) +Create `apps/web/.dev.vars` for local development: +```env +DATABASE_URL="postgresql://postgres:password@localhost:5432/bottlecrm?schema=public" +BASE_URL="http://localhost:5173" +GOOGLE_CLIENT_ID="" +GOOGLE_CLIENT_SECRET="" +``` + +### Production (wrangler.jsonc) +Configure in `apps/web/wrangler.jsonc` under `vars` section or use Cloudflare Secrets. + +## Database Schema Patterns + +### Entity Conventions +- Primary keys: `id` (UUID via `randomUUID()`) +- Timestamps: `createdAt`, `updatedAt` with defaults +- Soft deletes: `isDeleted`, `deletedAt`, `deletedById` +- Organization scoping: `organizationId` foreign key +- Owner tracking: `ownerId` references user + +### Enum Usage +All enums defined in `shared/database/src/schema/enums.ts`: +- `leadStatus`, `leadSource` +- `opportunityStage`, `opportunityType` +- `taskStatus`, `taskPriority` +- `caseStatus`, `quoteStatus` +- Industry, rating, and other business enums + +## Form Development +- Use proper TypeScript types for form data +- Implement Zod schemas for validation +- Ensure all form controls have associated labels +- Follow existing patterns in the codebase + +## Testing Strategy +- Run `pnpm run check` before committing +- Ensure `pnpm run lint` passes +- Build verification with `pnpm run build` + +## Security Requirements +- Never expose cross-organization data +- Always include `organizationId` in queries +- Validate organization membership before data access +- Use Drizzle's parameterized queries +- Audit sensitive operations \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..48456ae --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,151 @@ +# Contributing to BottleCRM + +Thank you for your interest in contributing to BottleCRM! We're excited to have you join our community of developers working to make high-quality CRM software accessible to everyone. + +This document provides guidelines and instructions for contributing to the project. + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [Getting Started](#getting-started) +- [Development Workflow](#development-workflow) +- [Pull Request Process](#pull-request-process) +- [Reporting Bugs](#reporting-bugs) +- [Feature Requests](#feature-requests) +- [Coding Standards](#coding-standards) +- [Community](#community) + +## Code of Conduct + +We are committed to providing a welcoming and inspiring community for all. + +## Getting Started + +### Prerequisites + +- Node.js (v16 or newer) +- npm, pnpm, or yarn package manager +- Git +- A database (PostgreSQL recommended) + +### Setting Up Local Development + +1. Fork the repository on GitHub +2. Clone your fork locally: + ```bash + git clone https://github.com/YOUR_USERNAME/bottlecrm.git + cd bottlecrm + ``` +3. Install dependencies: + ```bash + npm install + # or + pnpm install + # or + yarn + ``` +4. Configure your environment variables: + - Copy `.env.example` to `.env` + - Update the variables as needed for your local environment +5. Run database migrations: + ```bash + npx prisma migrate dev + ``` +6. Start the development server: + ```bash + npm run dev + ``` + +## Development Workflow + +1. Create a new branch for your work: + ```bash + git checkout -b feature/your-feature-name + # or + git checkout -b fix/issue-you-are-fixing + ``` + +2. Make your changes and commit them using descriptive commit messages: + ```bash + git commit -m "feat: add new feature X that does Y" + ``` + We follow the [Conventional Commits](https://www.conventionalcommits.org/) standard for commit messages. + +3. Push your branch to GitHub: + ```bash + git push origin feature/your-feature-name + ``` + +4. Create a pull request from your branch to the main project repository. + +## Pull Request Process + +1. Ensure your code follows the project's coding standards. +2. Update the documentation as needed. +3. Add tests for new functionality. +4. Ensure the test suite passes by running: + ```bash + npm run test + ``` +5. Your pull request will be reviewed by maintainers, who may request changes or provide feedback. +6. Once approved, your pull request will be merged by a maintainer. + +## Reporting Bugs + +Please report bugs by opening an issue on our GitHub repository. When filing a bug report, please include: + +- A clear and descriptive title +- Steps to reproduce the issue +- Expected behavior +- Actual behavior +- Screenshots (if applicable) +- Environment information (OS, browser, etc.) + +## Feature Requests + +We welcome suggestions for new features! To suggest a feature: + +1. Check if the feature has already been requested or is in development. +2. Open a new issue describing: + - The feature you'd like to see + - The problem it solves + - How it should work + - Why it would be valuable to most users + +## Coding Standards + +- We use ESLint and Prettier for code formatting and linting. +- Run `npm run lint` before submitting pull requests. +- Write meaningful comments and documentation. +- Follow the existing code style and patterns. +- Write tests for new functionality. + +### Svelte Component Guidelines + +- Each component should have a clear, single responsibility. +- Use Svelte's reactivity system effectively. +- Keep components reasonably sized; consider breaking large components into smaller ones. +- Use TypeScript for type safety when possible. + +### API Development Guidelines + +- Follow RESTful principles. +- Return consistent response structures. +- Handle errors gracefully and return appropriate status codes. +- Document new endpoints. + +## Community + +Join our community to discuss the project, get help, or just hang out with other BottleCRM contributors: + +- [GitHub Discussions](https://github.com/yourusername/bottlecrm/discussions) +- [Community Forum](#) (coming soon) +- [Discord Server](#) (coming soon) + +## License + +By contributing to BottleCRM, you agree that your contributions will be licensed under the project's [MIT License](LICENSE). + +--- + +Thank you for contributing to make CRM software accessible to everyone! ❤️ diff --git a/DEV.md b/DEV.md new file mode 100644 index 0000000..e643f4c --- /dev/null +++ b/DEV.md @@ -0,0 +1,68 @@ +## BottleCRM Dev Guide + +- Never use `$app` from SvelteKit. See: https://kit.svelte.dev/docs/packaging#best-practices + +### Monorepo + +- Package manager: pnpm +- Workspaces: + - `apps/web` (SvelteKit) + - `apps/api` (Express/Node) + - `shared/database` (Drizzle ORM + Drizzle Kit migrations) + +### Node/Tooling + +- Node: `nvm use 22.13.0` +- Install: `pnpm install` + +### Database (Drizzle) + +- Generate SQL and types: `pnpm --filter @opensource-startup-crm/database db:generate` +- Dev migrations: `pnpm --filter @opensource-startup-crm/database db:migrate:local` +- Prod migrations: `pnpm --filter @opensource-startup-crm/database db:migrate:prod` +- Studio: `pnpm --filter @opensource-startup-crm/database db:studio` + +#### Drizzle workflow + +- Edit schema in `shared/database/src/schema/*`. +- Generate migration + types: + - From repo root: `pnpm db:generate` + - Or inside package: `pnpm --filter @opensource-startup-crm/database db:generate` +- Apply migrations locally: + - From root: `pnpm db:migrate:local` +- Apply migrations in prod: + - From root: `pnpm db:migrate:prod` +- One-shot (generate + migrate + build): + - Local: `pnpm --filter @opensource-startup-crm/database db:gmb:local` + - Prod: `pnpm --filter @opensource-startup-crm/database db:gmb:prod` + +Notes + +- Drizzle config: `shared/database/drizzle.config.ts` +- Migrations output: `shared/database/migrations/` +- Ensure database env vars are set before running commands (wrangler `.dev.vars` or system env). + +### Development + +- Run Web (SvelteKit): `pnpm --filter @opensource-startup-crm/web dev` +- Run API: `pnpm --filter @opensource-startup-crm/api dev` + +Or use root scripts: + +- Web dev: `pnpm web:dev` +- Web build: `pnpm web:build` +- Web preview: `pnpm web:preview` +- API dev: `pnpm api:dev` +- API build: `pnpm api:build` + +### Lint/Type/Build + +- Type check: `pnpm --filter @opensource-startup-crm/web check` +- Lint: `pnpm --filter @opensource-startup-crm/web lint` +- Build: `pnpm --filter @opensource-startup-crm/web build` + +### Pre-commit checklist + +- `pnpm -r run lint` +- `pnpm -r run build` +- `pnpm --filter @opensource-startup-crm/web check` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fca0550 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# syntax=docker/dockerfile:1 + +FROM node:22-alpine AS builder +WORKDIR /app +COPY package.json pnpm-lock.yaml ./ +# Install pnpm using the official installation script +RUN wget -qO- https://get.pnpm.io/install.sh | sh - && \ + export PATH="/root/.local/share/pnpm:$PATH" && \ + pnpm install --frozen-lockfile +COPY . . +RUN export PATH="/root/.local/share/pnpm:$PATH" && pnpm run build && npx prisma generate + +FROM node:22-alpine +WORKDIR /app +ENV NODE_ENV=production +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/pnpm-lock.yaml ./ +COPY --from=builder /app/build ./build +COPY --from=builder /app/prisma ./prisma +COPY --from=builder /app/node_modules ./node_modules +EXPOSE 3000 +CMD ["node", "build"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8d75e40 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 MicroPyramid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index b5b2950..18852a5 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,248 @@ -# sv +# BottleCRM: Free and Open Source Customer Relationship Management -Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). +
+

Powerful, Modern Multi-Tenant CRM for Everyone

+
-## Creating a project +BottleCRM is a free, open-source Customer Relationship Management solution designed to help small and medium businesses effectively manage their customer relationships. Built with modern technologies and enterprise-grade multi-tenancy, it offers a comprehensive set of features without the enterprise price tag. -If you're seeing this, you've probably already done this step. Congrats! +## ✨ Key Highlights + +- **Multi-Tenant Architecture**: Secure organization-based data isolation +- **Role-Based Access Control**: Granular permissions for users and admins +- **Modern Technology Stack**: Built with SvelteKit 2.x, Svelte 5.x, and PostgreSQL +- **Mobile-First Design**: Responsive interface optimized for all devices + +## 🚀 Core Features + +### Sales & Lead Management + +- **Lead Management**: Track and nurture leads from initial contact to conversion +- **Account Management**: Maintain detailed records of customer accounts and organizations +- **Contact Management**: Store and organize all your customer contact information +- **Opportunity Management**: Track deals through your sales pipeline with customizable stages + +### Customer Support + +- **Case Management**: Handle customer support cases and track resolution +- **Solution Knowledge Base**: Maintain searchable solutions for common issues +- **Multi-Channel Support**: Handle cases from various origins (email, web, phone) + +### Productivity & Collaboration + +- **Task Management**: Never miss a follow-up with built-in task tracking +- **Event Management**: Schedule and manage meetings and activities +- **Board Management**: Trello-like kanban boards for project tracking +- **Comment System**: Collaborate with team members on records + +### Sales Tools + +- **Quote Management**: Generate professional quotes with line items +- **Product Catalog**: Maintain product inventory with pricing +- **Sales Pipeline**: Visual opportunity tracking with probability scoring + +### Administrative Features + +- **User Management**: Add team members with appropriate role assignments +- **Organization Management**: Multi-tenant structure with data isolation +- **Audit Logging**: Complete activity tracking for compliance +- **Super Admin Panel**: Platform-wide management for system administrators + +## 🔮 Coming Soon + +- **Invoice Management**: Create, send, and track invoices (in development) +- **Email Integration**: Connect your email accounts for seamless communication +- **Analytics Dashboard**: Make data-driven decisions with powerful reporting tools +- **API Integration**: REST API for third-party integrations + +## 🖥️ Technology Stack + +- **Frontend**: SvelteKit 2.x, Svelte 5.x, TailwindCSS 4.x +- **Backend**: Node.js (API), Drizzle ORM/Drizzle Kit (queries, schema, migrations) +- **Database**: PostgreSQL (recommended) with multi-tenant schema +- **Authentication**: Session-based authentication with organization membership +- **Icons**: Lucide Svelte icon library +- **Validation**: Zod for type-safe form validation + +## 🚀 Getting Started + +### Prerequisites + +- **Node.js**: v22.13.0 (use nvm for version management) +- **Package Manager**: pnpm (recommended) +- **Database**: PostgreSQL (required for multi-tenancy features) + +### Installation (Monorepo) + +1. **Clone the repository:** + +```bash +git clone https://github.com/micropyramid/svelte-crm.git +cd svelte-crm +``` + +2. **Set up Node.js version:** ```bash -# create a new project in the current directory -npx sv create +nvm use 22.13.0 +``` + +3. **Install dependencies (monorepo):** -# create a new project in my-app -npx sv create my-app +```bash +pnpm install ``` -## Developing +4. **Configure environment variables (Wrangler + .dev.vars):** -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +For local development (recommended), create a `.dev.vars` file inside the app that needs the vars (e.g., `apps/web/.dev.vars`). Example: ```bash -npm run dev +# apps/web/.dev.vars +DATABASE_URL="postgresql://postgres:password@localhost:5432/bottlecrm?schema=public" +JWT_SECRET="" +GOOGLE_CLIENT_ID="" +GOOGLE_CLIENT_SECRET="" +``` -# or start the server and open the app in a new browser tab -npm run dev -- --open +To define variables for preview/production with Cloudflare Wrangler, add them to `wrangler.jsonc` under `vars` (e.g., `apps/web/wrangler.jsonc`): + +```jsonc +{ + // ...existing config + "vars": { + "DATABASE_URL": "postgresql://...", + "JWT_SECRET": "", + "GOOGLE_CLIENT_ID": "", + "GOOGLE_CLIENT_SECRET": "" + } +} ``` -## Building +For production, best practice is to use Cloudflare's Secrets Store and Hyperdrive. + +Notes + +- `.dev.vars` is not committed and is used by `wrangler dev` for local runs. +- For non-Cloudflare processes (e.g., Node API), export env vars via your shell or a process manager as needed. -To create a production version of your app: +5. **Set up the database (shared/database/ Drizzle):** ```bash -npm run build +# Generate SQL and types +pnpm --filter @opensource-startup-crm/database db:generate + +# Run database migrations (dev) +pnpm --filter @opensource-startup-crm/database db:migrate:local + + ``` -You can preview the production build with `npm run preview`. +6. **Start development servers:** + +```bash +# Web (SvelteKit) +pnpm --filter @opensource-startup-crm/web dev + +# API (Node/Express) +pnpm --filter @opensource-startup-crm/api dev +``` + +### Development Workflow + +Before committing code, ensure quality checks pass: + +```bash +# Type checking +pnpm run check + +# Linting and formatting +pnpm run lint + +# Build verification +pnpm run build +``` + +### Production Deployment (Monorepo + Drizzle) + +```bash +# Set Node.js version +nvm use 22.13.0 + +# Generate SQL and types +pnpm --filter @opensource-startup-crm/database db:generate + +# Run production migrations +pnpm --filter @opensource-startup-crm/database db:migrate:prod + +# Build applications +pnpm --filter @opensource-startup-crm/web build +pnpm --filter @opensource-startup-crm/api build + +# Preview web +pnpm --filter @opensource-startup-crm/web preview +``` + +## 🏗️ Architecture & Security + +### Multi-Tenant Design + +- **Organization Isolation**: Complete data separation between organizations +- **Role-Based Access**: Users can have different roles across organizations +- **Session Management**: Secure cookie-based authentication with organization context + +### User Roles + +- **User**: Standard access to organization data +- **Admin**: Organization-level administrative privileges +- **Super Admin**: Platform-wide access (requires @micropyramid.com email) + +### Data Security + +- All database queries are organization-scoped +- Strict permission validation on all routes +- Audit logging for compliance and tracking + +## 📁 Project Structure + +``` +src/ +├── routes/ +│ ├── (site)/ # Public marketing pages +│ ├── (no-layout)/ # Authentication pages +│ ├── (app)/ # Main CRM application +│ └── (admin)/ # Super admin panel +├── lib/ +│ ├── stores/ # Svelte stores for state management +│ ├── data/ # Static data and configurations +│ └── utils/ # Utility functions +└── hooks.server.js # Authentication and route protection +``` + +## 💬 Community and Feedback + +We love to hear from our users! Please share your feedback, report bugs, or suggest new features: + +- **Issues**: Open an issue on GitHub for bugs and feature requests +- **Discussions**: Join community discussions for general questions +- **Pull Requests**: Contribute code improvements and new features + +## 🤝 Contributing + +We welcome contributions of all kinds! See our [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to get started. + +### Development Guidelines + +- Follow existing code patterns and conventions +- Ensure all forms have proper accessibility (labels associated with controls) +- Never use `$app` imports from SvelteKit (see packaging best practices) +- Always filter database queries by organization membership +- Add appropriate error handling and validation + +## 📄 License + +BottleCRM is open source software [licensed as MIT](LICENSE). + +--- -> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. +_Built with ❤️ for small businesses everywhere. We believe quality CRM software should be accessible to everyone._ diff --git a/apps/api/README.md b/apps/api/README.md new file mode 100644 index 0000000..74e4fce --- /dev/null +++ b/apps/api/README.md @@ -0,0 +1,137 @@ +# BottleCRM API + +Express.js API for BottleCRM with JWT authentication, Swagger documentation, and configurable request logging. + +## Features + +- **Google OAuth Authentication**: Secure Google Sign-In for mobile apps +- **Multi-tenant**: Organization-based data isolation using existing Prisma schema +- **Swagger Documentation**: Interactive API documentation at `/api-docs` +- **Request Logging**: Configurable input/output HTTP request logging +- **Security**: Helmet, CORS, rate limiting +- **Organization Access Control**: Ensures users can only access their organization's data + +## Quick Start + +1. The required environment variables are already added to your existing `.env` file. + +2. **Generate a secure JWT secret** (required for production): +```bash +# Using Node.js +node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" + +# Using OpenSSL (if available) +openssl rand -hex 32 + +# Using online generator (for development only) +# Visit: https://generate-secret.vercel.app/32 +``` + +3. Update your `.env` file with the generated secret: +```env +JWT_SECRET=your-generated-secret-key-here +``` + +4. Start the API server: +```bash +# Development with auto-reload +pnpm run api:dev + +# Production +pnpm run api:start +``` + +5. Visit Swagger documentation: +``` +http://localhost:3001/api-docs +``` + +## Authentication + +1. **Google Login**: POST `/api/auth/google` + - Request: `{ "idToken": "google-id-token-from-mobile-app" }` + - Response: `{ "token": "jwt-token", "user": {...} }` + +2. **Use Token**: Include in Authorization header: + ``` + Authorization: Bearer + ``` + +3. **Select Organization**: Include organization ID in header: + ``` + X-Organization-ID: + ``` + +## API Endpoints + +### Authentication +- `POST /api/auth/google` - Google OAuth mobile login +- `GET /api/auth/me` - Get current user profile + +### Leads +- `GET /api/leads` - Get organization leads (paginated) +- `GET /api/leads/:id` - Get lead by ID +- `POST /api/leads` - Create new lead + +### Accounts +- `GET /api/accounts` - Get organization accounts +- `POST /api/accounts` - Create new account + +### Contacts +- `GET /api/contacts` - Get organization contacts +- `POST /api/contacts` - Create new contact + +### Opportunities +- `GET /api/opportunities` - Get organization opportunities +- `POST /api/opportunities` - Create new opportunity + +## Configuration + +### Environment Variables + +- `API_PORT`: Server port (default: 3001) +- `JWT_SECRET`: Secret key for JWT tokens (required) - **Generate using the commands above** +- `JWT_EXPIRES_IN`: Token expiration time (default: 24h) +- `FRONTEND_URL`: Frontend URL for CORS (default: http://localhost:5173) + +### Logging Configuration + +- `LOG_LEVEL`: Logging level (info, debug, error) +- `ENABLE_REQUEST_LOGGING`: Enable/disable request logging (true/false) +- `LOG_REQUEST_BODY`: Log request bodies (true/false) +- `LOG_RESPONSE_BODY`: Log response bodies (true/false) + +### Security Features + +- **Rate Limiting**: 100 requests per 15 minutes per IP +- **Helmet**: Security headers +- **CORS**: Cross-origin request handling +- **JWT Validation**: Token verification on protected routes +- **Organization Isolation**: Users can only access their organization's data + +## Data Access Control + +All API endpoints enforce organization-based access control: + +1. **Authentication Required**: All endpoints (except login) require valid JWT token +2. **Organization Header**: Protected endpoints require `X-Organization-ID` header +3. **Membership Validation**: User must be a member of the specified organization +4. **Data Filtering**: All database queries are filtered by organization ID + +## Development + +The API uses the same Prisma schema as the main SvelteKit application, ensuring data consistency and leveraging existing: + +- Database models and relationships +- Organization-based multi-tenancy +- User role management (ADMIN/USER) +- Super admin access (@micropyramid.com domain) + +## Testing with Swagger + +Access the interactive API documentation at `http://localhost:3001/api-docs` to: + +1. Test authentication endpoints +2. Explore available endpoints +3. Test API calls with different parameters +4. View request/response schemas \ No newline at end of file diff --git a/apps/api/config/logger.js b/apps/api/config/logger.js new file mode 100644 index 0000000..fed578c --- /dev/null +++ b/apps/api/config/logger.js @@ -0,0 +1,22 @@ +export const createLogger = () => { + return { + info: (message, meta) => { + console.log(`[INFO] ${message}`); + if (meta) { + console.log(JSON.stringify(meta, null, 2)); + } + }, + error: (message, meta) => { + console.error(`[ERROR] ${message}`); + if (meta) { + console.error(JSON.stringify(meta, null, 2)); + } + }, + warn: (message, meta) => { + console.warn(`[WARN] ${message}`); + if (meta) { + console.warn(JSON.stringify(meta, null, 2)); + } + } + }; +}; \ No newline at end of file diff --git a/apps/api/lib/db.js b/apps/api/lib/db.js new file mode 100644 index 0000000..2642881 --- /dev/null +++ b/apps/api/lib/db.js @@ -0,0 +1,12 @@ +import { getDb, schema } from '@opensource-startup-crm/database'; + +export const db = getDb({ + DATABASE_URL: process.env.DATABASE_URL, + DEV_DATABASE_URL: process.env.DEV_DATABASE_URL, + ENV_TYPE: process.env.ENV_TYPE, + HYPERDRIVE: process.env.HYPERDRIVE +}); + +export { schema }; + + diff --git a/apps/api/middleware/auth.js b/apps/api/middleware/auth.js new file mode 100644 index 0000000..41cb850 --- /dev/null +++ b/apps/api/middleware/auth.js @@ -0,0 +1,103 @@ +import { createRemoteJWKSet, jwtVerify } from 'jose'; +import { db, schema } from '../lib/db.js'; +import { eq, innerJoin } from 'drizzle-orm'; + +// Verify Better Auth JWTs using JWKS +export const verifyToken = async (req, res, next) => { + try { + const token = req.header('Authorization')?.replace('Bearer ', ''); + if (!token) { + return res.status(401).json({ error: 'Access denied. No token provided.' }); + } + + const baseUrl = process.env.PUBLIC_APP_URL || 'http://localhost:5173'; + const jwks = createRemoteJWKSet(new URL(`${baseUrl}/api/auth/jwks`)); + + const { payload } = await jwtVerify(token, jwks, { + // audience / issuer can be configured if you set them in Better Auth + }); + + const userId = payload.sub; + if (!userId) { + return res.status(401).json({ error: 'Invalid token: missing subject.' }); + } + + // Fetch user and memberships via Drizzle + const [user] = await db + .select({ + id: schema.user.id, + email: schema.user.email, + name: schema.user.name, + image: schema.user.image + }) + .from(schema.user) + .where(eq(schema.user.id, userId)); + + if (!user) { + return res.status(401).json({ error: 'User not found.' }); + } + + const memberships = await db + .select({ + organizationId: schema.member.organizationId, + role: schema.member.role, + organization: { + id: schema.organization.id, + name: schema.organization.name, + domain: schema.organization.domain + } + }) + .from(schema.member) + .innerJoin(schema.organization, eq(schema.organization.id, schema.member.organizationId)) + .where(eq(schema.member.userId, userId)); + + req.user = user; + req.userId = user.id; + req.memberships = memberships; + next(); + } catch (error) { + console.error('Token verification error:', error); + return res.status(401).json({ error: 'Token validation failed.' }); + } +}; + +export const requireOrganization = async (req, res, next) => { + try { + const organizationId = req.header('X-Organization-ID'); + + if (!organizationId) { + return res.status(400).json({ error: 'Organization ID is required in X-Organization-ID header.' }); + } + + const userOrg = (req.memberships || []).find( + (uo) => uo.organizationId === organizationId + ); + + if (!userOrg) { + return res.status(403).json({ error: 'Access denied to this organization.' }); + } + + req.organizationId = organizationId; + req.userRole = userOrg.role; + req.organization = userOrg.organization; + next(); + } catch (error) { + return res.status(500).json({ error: 'Internal server error.' }); + } +}; + +export const requireRole = (roles) => { + return (req, res, next) => { + if (!roles.includes(req.userRole)) { + return res.status(403).json({ error: 'Insufficient permissions.' }); + } + next(); + }; +}; + +export const requireSuperAdmin = (req, res, next) => { + if (!req.user.email.endsWith('@micropyramid.com')) { + return res.status(403).json({ error: 'Super admin access required.' }); + } + next(); +}; \ No newline at end of file diff --git a/apps/api/middleware/errorHandler.js b/apps/api/middleware/errorHandler.js new file mode 100644 index 0000000..b8af078 --- /dev/null +++ b/apps/api/middleware/errorHandler.js @@ -0,0 +1,24 @@ +import { createLogger } from '../config/logger.js'; + +const logger = createLogger(); + +export const errorHandler = (err, req, res, next) => { + logger.error('Unhandled Error', { + error: err.message, + stack: err.stack, + method: req.method, + url: req.url, + userId: req.user?.id, + organizationId: req.organizationId, + timestamp: new Date().toISOString(), + }); + + if (process.env.NODE_ENV === 'production') { + res.status(500).json({ error: 'Internal server error' }); + } else { + res.status(500).json({ + error: err.message, + stack: err.stack + }); + } +}; \ No newline at end of file diff --git a/apps/api/middleware/requestLogger.js b/apps/api/middleware/requestLogger.js new file mode 100644 index 0000000..d6989d8 --- /dev/null +++ b/apps/api/middleware/requestLogger.js @@ -0,0 +1,76 @@ +export const requestLogger = (req, res, next) => { + const start = Date.now(); + + const originalSend = res.send; + const originalJson = res.json; + + let responseBody = null; + let requestBody = null; + + if (req.body && Object.keys(req.body).length > 0) { + requestBody = { ...req.body }; + if (requestBody.password) requestBody.password = '[REDACTED]'; + if (requestBody.token) requestBody.token = '[REDACTED]'; + } + + res.send = function(body) { + responseBody = body; + return originalSend.call(this, body); + }; + + res.json = function(body) { + responseBody = body; + return originalJson.call(this, body); + }; + + res.on('finish', () => { + const duration = Date.now() - start; + + const logData = { + method: req.method, + url: req.url, + statusCode: res.statusCode, + duration: `${duration}ms`, + userAgent: req.get('User-Agent'), + ip: req.ip, + timestamp: new Date().toISOString(), + }; + + if (process.env.LOG_REQUEST_BODY === 'true' && requestBody) { + logData.requestBody = requestBody; + } + + if (process.env.LOG_RESPONSE_BODY === 'true' && responseBody) { + try { + logData.responseBody = typeof responseBody === 'string' ? JSON.parse(responseBody) : responseBody; + } catch (e) { + logData.responseBody = responseBody; + } + } + + if (req.user) { + logData.userId = req.user.id; + logData.userEmail = req.user.email; + } + + if (req.organizationId) { + logData.organizationId = req.organizationId; + } + + console.log(`\n=== HTTP REQUEST LOG ===`); + console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`); + + if (requestBody) { + console.log('REQUEST BODY:', JSON.stringify(requestBody, null, 2)); + } + + if (responseBody) { + console.log('RESPONSE BODY:', JSON.stringify(responseBody, null, 2)); + } + + console.log('FULL LOG DATA:', JSON.stringify(logData, null, 2)); + console.log(`=== END LOG ===\n`); + }); + + next(); +}; \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..47ce6f0 --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,54 @@ +{ + "name": "@opensource-startup-crm/api", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "nodemon server.js", + "start": "node server.js" + }, + "devDependencies": { + "@better-auth/cli": "^1.3.4", + "@types/bcryptjs": "^3.0.0", + "@types/cors": "^2.8.19", + "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/morgan": "^1.9.10", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.8", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.4", + "globals": "^16.3.0", + "nodemon": "^3.1.10", + "typescript": "^5.8.3" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild" + ] + }, + "dependencies": { + "@opensource-startup-crm/database": "workspace:*", + "axios": "^1.11.0", + "bcryptjs": "^3.0.2", + "better-auth": "^1.3.4", + "cors": "^2.8.5", + "date-fns": "^4.1.0", + "dotenv": "^17.2.1", + "express": "^5.1.0", + "express-rate-limit": "^8.0.1", + "google-auth-library": "^10.2.0", + "helmet": "^8.1.0", + "jose": "^5.9.6", + "jsonwebtoken": "^9.0.2", + "libphonenumber-js": "^1.12.10", + "marked": "^16.1.1", + "morgan": "^1.10.1", + "postgres": "^3.4.7", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", + "uuid": "^11.1.0", + "winston": "^3.17.0", + "zod": "^4.0.8" + } +} diff --git a/apps/api/routes/accounts.js b/apps/api/routes/accounts.js new file mode 100644 index 0000000..cf61584 --- /dev/null +++ b/apps/api/routes/accounts.js @@ -0,0 +1,155 @@ +import express from 'express'; +import { db, schema } from '../lib/db.js'; +import { desc, eq } from 'drizzle-orm'; +import { verifyToken, requireOrganization } from '../middleware/auth.js'; + +const router = express.Router(); + +router.use(verifyToken); +router.use(requireOrganization); + +/** + * @swagger + * components: + * schemas: + * Account: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * industry: + * type: string + * phone: + * type: string + * email: + * type: string + * website: + * type: string + * createdAt: + * type: string + * format: date-time + */ + +/** + * @swagger + * /accounts: + * get: + * summary: Get all accounts for organization + * tags: [Accounts] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * responses: + * 200: + * description: List of accounts + */ +router.get('/', async (req, res) => { + try { + const accounts = await db + .select({ + id: schema.crmAccount.id, + name: schema.crmAccount.name, + industry: schema.crmAccount.industry, + phone: schema.crmAccount.phone, + email: schema.crmAccount.website, // no email field in schema; website kept + website: schema.crmAccount.website, + createdAt: schema.crmAccount.createdAt, + owner: { + id: schema.user.id, + name: schema.user.name, + email: schema.user.email + } + }) + .from(schema.crmAccount) + .leftJoin(schema.user, eq(schema.user.id, schema.crmAccount.ownerId)) + .where(eq(schema.crmAccount.organizationId, req.organizationId)) + .orderBy(desc(schema.crmAccount.createdAt)); + + res.json({ accounts }); + } catch (error) { + console.error('Get accounts error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /accounts: + * post: + * summary: Create a new account + * tags: [Accounts] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * properties: + * name: + * type: string + * industry: + * type: string + * phone: + * type: string + * email: + * type: string + * website: + * type: string + * responses: + * 201: + * description: Account created successfully + */ +router.post('/', async (req, res) => { + try { + const { name, industry, phone, email, website } = req.body; + + if (!name) { + return res.status(400).json({ error: 'Account name is required' }); + } + + const [account] = await db + .insert(schema.crmAccount) + .values({ + name, + industry, + phone, + website, + organizationId: req.organizationId, + ownerId: req.userId + }) + .returning({ + id: schema.crmAccount.id, + name: schema.crmAccount.name, + industry: schema.crmAccount.industry, + phone: schema.crmAccount.phone, + website: schema.crmAccount.website + }); + + const [owner] = await db + .select({ id: schema.user.id, name: schema.user.name, email: schema.user.email }) + .from(schema.user) + .where(eq(schema.user.id, req.userId)); + + const response = { ...account, owner }; + + res.status(201).json(response); + } catch (error) { + console.error('Create account error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +export default router; \ No newline at end of file diff --git a/apps/api/routes/auth.js b/apps/api/routes/auth.js new file mode 100644 index 0000000..e9c84f6 --- /dev/null +++ b/apps/api/routes/auth.js @@ -0,0 +1,296 @@ +import express from 'express'; +import jwt from 'jsonwebtoken'; +import { OAuth2Client } from 'google-auth-library'; +import { verifyToken } from '../middleware/auth.js'; +import { db, schema } from '../lib/db.js'; +import { eq, ilike, and } from 'drizzle-orm'; + +const router = express.Router(); +const googleClient = new OAuth2Client(process.env.GOOGLE_CLIENT_ID); + +/** + * @swagger + * components: + * schemas: + * GoogleLoginRequest: + * type: object + * required: + * - idToken + * properties: + * idToken: + * type: string + * description: Google ID token from mobile app + * LoginResponse: + * type: object + * properties: + * token: + * type: string + * user: + * type: object + * properties: + * id: + * type: string + * email: + * type: string + * firstName: + * type: string + * lastName: + * type: string + * profileImage: + * type: string + * organizations: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * role: + * type: string + */ + + +/** + * @swagger + * /auth/me: + * get: + * summary: Get current user profile + * tags: [Authentication] + * responses: + * 200: + * description: User profile + * content: + * application/json: + * schema: + * type: object + * properties: + * user: + * type: object + * properties: + * id: + * type: string + * email: + * type: string + * firstName: + * type: string + * lastName: + * type: string + * organizations: + * type: array + * 401: + * description: Unauthorized + */ +router.get('/me', verifyToken, async (req, res) => { + try { + const userResponse = { + id: req.user.id, + email: req.user.email, + firstName: req.user.firstName, + lastName: req.user.lastName, + organizations: req.user.userOrganizations.map(uo => ({ + id: uo.organization.id, + name: uo.organization.name, + role: uo.role + })) + }; + + res.json({ user: userResponse }); + } catch (error) { + console.error('Profile error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /auth/google: + * post: + * summary: Google OAuth mobile login + * tags: [Authentication] + * security: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/GoogleLoginRequest' + * responses: + * 200: + * description: Login successful + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/LoginResponse' + * 400: + * description: Invalid Google token or user not found + * 500: + * description: Server error + */ +router.post('/google', async (req, res) => { + try { + const { idToken } = req.body; + + if (!idToken) { + return res.status(400).json({ error: 'Google ID token is required' }); + } + + // Support both web and mobile client IDs + const audiences = [ + process.env.GOOGLE_CLIENT_ID + ]; + + const ticket = await googleClient.verifyIdToken({ + idToken, + audience: audiences + }); + + const payload = ticket.getPayload(); + + if (!payload || !payload.email) { + return res.status(400).json({ error: 'Invalid Google token' }); + } + + // Upsert user via Drizzle using onConflictDoUpdate + const now = new Date(); + const fallbackName = (payload.name || `${payload.given_name || ''} ${payload.family_name || ''}`.trim() || (payload.email?.split('@')[0] || 'User')).trim(); + const updateSet = { + image: payload.picture, + lastLogin: now + }; + if (payload.name) Object.assign(updateSet, { name: payload.name }); + + const [user] = await db + .insert(schema.user) + .values({ + email: payload.email, + name: fallbackName, + image: payload.picture, + lastLogin: now + }) + .onConflictDoUpdate({ target: schema.user.email, set: updateSet }) + .returning({ + id: schema.user.id, + email: schema.user.email, + name: schema.user.name, + image: schema.user.image + }); + + // Create JWT token for API access + const JWTtoken = jwt.sign( + { userId: user.id }, + process.env.JWT_SECRET, + { expiresIn: process.env.JWT_EXPIRES_IN || '24h' } + ); + + // Calculate expiration date + const expiresIn = process.env.JWT_EXPIRES_IN || '24h'; + const expirationHours = expiresIn.includes('h') ? parseInt(expiresIn) : 24; + const expiresAt = new Date(Date.now() + expirationHours * 60 * 60 * 1000); + + // Note: We no longer store JWTs in DB; Better Auth issues/validates via JWKS. + + // Format response to match SvelteKit patterns + const userResponse = { + id: user.id, + email: user.email, + name: user.name, + profileImage: user.image, + }; + + // Fetch organizations memberships + const orgs = await db + .select({ + id: schema.organization.id, + name: schema.organization.name, + role: schema.member.role + }) + .from(schema.member) + .innerJoin(schema.organization, eq(schema.organization.id, schema.member.organizationId)) + .where(eq(schema.member.userId, user.id)); + + res.json({ + success: true, + JWTtoken, + user: userResponse, + organizations: orgs.map((o) => ({ id: o.id, name: o.name, role: o.role })) + }); + } catch (error) { + console.error('Google login error:', error); + if (error.message && error.message.includes('Invalid token')) { + return res.status(400).json({ error: 'Invalid Google token' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /auth/logout: + * post: + * summary: Logout and revoke current JWT token + * tags: [Authentication] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: Successfully logged out + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * 401: + * description: Unauthorized + */ +router.post('/logout', verifyToken, async (req, res) => { + try { + // Better Auth JWTs cannot be revoked server-side here; instruct clients to drop the token. + res.json({ success: true, message: 'Logged out (client should discard token).' }); + } catch (error) { + console.error('Logout error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /auth/revoke-all: + * post: + * summary: Revoke all JWT tokens for current user + * tags: [Authentication] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: Successfully revoked all tokens + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * message: + * type: string + * revokedCount: + * type: integer + * 401: + * description: Unauthorized + */ +router.post('/revoke-all', verifyToken, async (req, res) => { + try { + // Not applicable with stateless JWT validation. Return success. + res.json({ success: true, message: 'All tokens considered revoked (stateless JWT).' }); + } catch (error) { + console.error('Revoke all tokens error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +export default router; \ No newline at end of file diff --git a/apps/api/routes/contacts.js b/apps/api/routes/contacts.js new file mode 100644 index 0000000..3660936 --- /dev/null +++ b/apps/api/routes/contacts.js @@ -0,0 +1,372 @@ +import express from 'express'; +import { db, schema } from '../lib/db.js'; +import { and, desc, eq } from 'drizzle-orm'; +import { verifyToken, requireOrganization } from '../middleware/auth.js'; + +const router = express.Router(); + +router.use(verifyToken); +router.use(requireOrganization); + +/** + * @swagger + * /contacts: + * get: + * summary: Get all contacts for organization + * tags: [Contacts] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * responses: + * 200: + * description: List of contacts + */ +router.get('/', async (req, res) => { + try { + // Single query with joins for owner and primary related account + const rows = await db + .select({ + id: schema.contact.id, + firstName: schema.contact.firstName, + lastName: schema.contact.lastName, + email: schema.contact.email, + phone: schema.contact.phone, + title: schema.contact.title, + department: schema.contact.department, + createdAt: schema.contact.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + relContactId: schema.accountContactRelationship.contactId, + relAccountId: schema.crmAccount.id, + relAccountName: schema.crmAccount.name + }) + .from(schema.contact) + .leftJoin(schema.user, eq(schema.user.id, schema.contact.ownerId)) + .leftJoin( + schema.accountContactRelationship, + and( + eq(schema.accountContactRelationship.contactId, schema.contact.id), + eq(schema.accountContactRelationship.isPrimary, true) + ) + ) + .leftJoin( + schema.crmAccount, + and( + eq(schema.crmAccount.id, schema.accountContactRelationship.accountId), + eq(schema.crmAccount.organizationId, req.organizationId) + ) + ) + .where(eq(schema.contact.organizationId, req.organizationId)) + .orderBy(desc(schema.contact.createdAt)); + + // Group by contact + const byId = new Map(); + for (const r of rows) { + if (!byId.has(r.id)) { + byId.set(r.id, { + id: r.id, + firstName: r.firstName, + lastName: r.lastName, + email: r.email, + phone: r.phone, + title: r.title, + department: r.department, + createdAt: r.createdAt, + relatedAccounts: [], + owner: r.ownerId ? { id: r.ownerId, name: r.ownerName, email: r.ownerEmail } : null + }); + } + if (r.relAccountId) { + byId.get(r.id).relatedAccounts.push({ account: { id: r.relAccountId, name: r.relAccountName } }); + } + } + + res.json({ contacts: Array.from(byId.values()) }); + } catch (error) { + console.error('Get contacts error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /contacts: + * post: + * summary: Create a new contact + * tags: [Contacts] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - firstName + * - lastName + * properties: + * firstName: + * type: string + * lastName: + * type: string + * email: + * type: string + * phone: + * type: string + * title: + * type: string + * department: + * type: string + * street: + * type: string + * city: + * type: string + * state: + * type: string + * postalCode: + * type: string + * country: + * type: string + * description: + * type: string + * accountId: + * type: string + * description: UUID of the account to associate with this contact + * responses: + * 201: + * description: Contact created successfully + * 400: + * description: Validation error + */ +router.post('/', async (req, res) => { + try { + const { firstName, lastName, email, phone, title, department, street, city, state, postalCode, country, description, accountId } = req.body; + + if (!firstName || !lastName) { + return res.status(400).json({ error: 'First name and last name are required' }); + } + + // Validate account if provided + if (accountId) { + const [account] = await db + .select({ id: schema.crmAccount.id }) + .from(schema.crmAccount) + .where(and(eq(schema.crmAccount.id, accountId), eq(schema.crmAccount.organizationId, req.organizationId))); + if (!account) return res.status(400).json({ error: 'Account not found in your organization' }); + } + + // Check for duplicate email within the organization if email is provided + if (email) { + const [existingContact] = await db + .select({ id: schema.contact.id }) + .from(schema.contact) + .where(and(eq(schema.contact.email, email), eq(schema.contact.organizationId, req.organizationId))); + if (existingContact) return res.status(400).json({ error: 'A contact with this email already exists in this organization' }); + } + + // Create the contact + const [contact] = await db + .insert(schema.contact) + .values({ + firstName, + lastName, + email: email || null, + phone: phone || null, + title: title || null, + department: department || null, + street: street || null, + city: city || null, + state: state || null, + postalCode: postalCode || null, + country: country || null, + description: description || null, + organizationId: req.organizationId, + ownerId: req.userId + }) + .returning({ id: schema.contact.id }); + + // Create account-contact relationship if accountId is provided + if (accountId) { + await db.insert(schema.accountContactRelationship).values({ + accountId, + contactId: contact.id, + isPrimary: true + }); + } + + // Fetch the created contact with relationships (single joined query) + const createdRows = await db + .select({ + id: schema.contact.id, + firstName: schema.contact.firstName, + lastName: schema.contact.lastName, + email: schema.contact.email, + phone: schema.contact.phone, + title: schema.contact.title, + department: schema.contact.department, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + relAccountId: schema.crmAccount.id, + relAccountName: schema.crmAccount.name + }) + .from(schema.contact) + .leftJoin(schema.user, eq(schema.user.id, schema.contact.ownerId)) + .leftJoin(schema.accountContactRelationship, eq(schema.accountContactRelationship.contactId, schema.contact.id)) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.accountContactRelationship.accountId)) + .where(eq(schema.contact.id, contact.id)); + + const base = createdRows[0]; + const response = { + id: base.id, + firstName: base.firstName, + lastName: base.lastName, + email: base.email, + phone: base.phone, + title: base.title, + department: base.department, + ownerId: base.ownerId, + ownerName: base.ownerName, + ownerEmail: base.ownerEmail, + relatedAccounts: createdRows + .filter((r) => r.relAccountId) + .map((r) => ({ account: { id: r.relAccountId, name: r.relAccountName } })) + }; + + res.status(201).json(response); + } catch (error) { + console.error('Create contact error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /contacts/{id}: + * get: + * summary: Get a specific contact by ID + * tags: [Contacts] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: Contact ID + * responses: + * 200: + * description: Contact details + * 404: + * description: Contact not found + */ +router.get('/:id', async (req, res) => { + try { + const { id } = req.params; + + const [row] = await db + .select({ + id: schema.contact.id, + firstName: schema.contact.firstName, + lastName: schema.contact.lastName, + email: schema.contact.email, + phone: schema.contact.phone, + title: schema.contact.title, + department: schema.contact.department, + description: schema.contact.description, + street: schema.contact.street, + city: schema.contact.city, + state: schema.contact.state, + postalCode: schema.contact.postalCode, + country: schema.contact.country, + createdAt: schema.contact.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + orgId: schema.organization.id, + orgName: schema.organization.name + }) + .from(schema.contact) + .leftJoin(schema.user, eq(schema.user.id, schema.contact.ownerId)) + .leftJoin(schema.organization, eq(schema.organization.id, schema.contact.organizationId)) + .where(and(eq(schema.contact.id, id), eq(schema.contact.organizationId, req.organizationId))); + + if (!row) return res.status(404).json({ error: 'Contact not found' }); + + // Fetch related accounts via single join query and aggregate + const rows = await db + .select({ + id: schema.contact.id, + firstName: schema.contact.firstName, + lastName: schema.contact.lastName, + email: schema.contact.email, + phone: schema.contact.phone, + title: schema.contact.title, + department: schema.contact.department, + description: schema.contact.description, + street: schema.contact.street, + city: schema.contact.city, + state: schema.contact.state, + postalCode: schema.contact.postalCode, + country: schema.contact.country, + createdAt: schema.contact.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + orgId: schema.organization.id, + orgName: schema.organization.name, + relAccountId: schema.crmAccount.id, + relAccountName: schema.crmAccount.name, + relAccountType: schema.crmAccount.type, + relAccountWebsite: schema.crmAccount.website, + relAccountPhone: schema.crmAccount.phone + }) + .from(schema.contact) + .leftJoin(schema.user, eq(schema.user.id, schema.contact.ownerId)) + .leftJoin(schema.organization, eq(schema.organization.id, schema.contact.organizationId)) + .leftJoin(schema.accountContactRelationship, eq(schema.accountContactRelationship.contactId, schema.contact.id)) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.accountContactRelationship.accountId)) + .where(and(eq(schema.contact.id, id), eq(schema.contact.organizationId, req.organizationId))); + + const baseRow = rows[0]; + const response = { + id: baseRow.id, + firstName: baseRow.firstName, + lastName: baseRow.lastName, + email: baseRow.email, + phone: baseRow.phone, + title: baseRow.title, + department: baseRow.department, + description: baseRow.description, + street: baseRow.street, + city: baseRow.city, + state: baseRow.state, + postalCode: baseRow.postalCode, + country: baseRow.country, + createdAt: baseRow.createdAt, + relatedAccounts: rows.filter((r) => r.relAccountId).map((r) => ({ account: { id: r.relAccountId, name: r.relAccountName, type: r.relAccountType, website: r.relAccountWebsite, phone: r.relAccountPhone } })), + owner: baseRow.ownerId ? { id: baseRow.ownerId, name: baseRow.ownerName, email: baseRow.ownerEmail } : null, + organization: baseRow.orgId ? { id: baseRow.orgId, name: baseRow.orgName } : null + }; + + res.json(response); + } catch (error) { + console.error('Get contact details error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +export default router; \ No newline at end of file diff --git a/apps/api/routes/dashboard.js b/apps/api/routes/dashboard.js new file mode 100644 index 0000000..55432bd --- /dev/null +++ b/apps/api/routes/dashboard.js @@ -0,0 +1,309 @@ +import express from 'express'; +import { verifyToken, requireOrganization } from '../middleware/auth.js'; +import { db, schema } from '../lib/db.js'; +import { and, desc, eq, gte, not, sql } from 'drizzle-orm'; + +const router = express.Router(); + +/** + * @swagger + * components: + * schemas: + * DashboardMetrics: + * type: object + * properties: + * totalLeads: + * type: integer + * description: Number of active leads + * totalOpportunities: + * type: integer + * description: Number of open opportunities + * totalAccounts: + * type: integer + * description: Number of active accounts + * totalContacts: + * type: integer + * description: Number of contacts + * pendingTasks: + * type: integer + * description: Number of pending tasks for the user + * opportunityRevenue: + * type: number + * description: Total pipeline value + * DashboardRecentData: + * type: object + * properties: + * leads: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * firstName: + * type: string + * lastName: + * type: string + * company: + * type: string + * status: + * type: string + * createdAt: + * type: string + * format: date-time + * opportunities: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * amount: + * type: number + * account: + * type: object + * properties: + * name: + * type: string + * createdAt: + * type: string + * format: date-time + * tasks: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * subject: + * type: string + * status: + * type: string + * priority: + * type: string + * dueDate: + * type: string + * format: date-time + * activities: + * type: array + * items: + * type: object + * properties: + * id: + * type: string + * action: + * type: string + * entityType: + * type: string + * description: + * type: string + * timestamp: + * type: string + * format: date-time + * user: + * type: object + * properties: + * name: + * type: string + * DashboardResponse: + * type: object + * properties: + * success: + * type: boolean + * metrics: + * $ref: '#/components/schemas/DashboardMetrics' + * recentData: + * $ref: '#/components/schemas/DashboardRecentData' + */ + +/** + * @swagger + * /dashboard: + * get: + * summary: Get dashboard data with metrics and recent activity + * tags: [Dashboard] + * security: + * - bearerAuth: [] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * description: Organization ID + * responses: + * 200: + * description: Dashboard data retrieved successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/DashboardResponse' + * 400: + * description: Missing organization ID + * 401: + * description: Unauthorized + * 403: + * description: Access denied to organization + * 500: + * description: Internal server error + */ +router.get('/', verifyToken, requireOrganization, async (req, res) => { + try { + const userId = req.userId; + const organizationId = req.organizationId; + + // Fetch dashboard metrics - parallel execution for performance + const [countsRow] = await db + .select({ + totalLeads: count(), + totalOpportunities: db.$count(schema.opportunity, and(eq(schema.opportunity.organizationId, organizationId), not(eq(schema.opportunity.stage, 'CLOSED_WON')))), + totalAccounts: db.$count(schema.crmAccount, and(eq(schema.crmAccount.organizationId, organizationId), eq(schema.crmAccount.isActive, true))), + totalContacts: db.$count(schema.contact, eq(schema.contact.organizationId, organizationId)), + pendingTasks: db.$count(schema.task, and(eq(schema.task.organizationId, organizationId), not(eq(schema.task.status, 'Completed')), eq(schema.task.ownerId, userId))) + }).from(schema.lead).where(and(eq(schema.lead.organizationId, organizationId), eq(schema.lead.isConverted, false))); + + const recentLeads = await db + .select({ id: schema.lead.id, firstName: schema.lead.firstName, lastName: schema.lead.lastName, company: schema.lead.company, status: schema.lead.status, createdAt: schema.lead.createdAt }) + .from(schema.lead) + .where(eq(schema.lead.organizationId, organizationId)) + .orderBy(desc(schema.lead.createdAt)) + .limit(5); + + const recentOpportunities = await db + .select({ id: schema.opportunity.id, name: schema.opportunity.name, amount: schema.opportunity.amount, account: { name: schema.crmAccount.name } }) + .from(schema.opportunity) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.opportunity.accountId)) + .where(eq(schema.opportunity.organizationId, organizationId)) + .orderBy(desc(schema.opportunity.createdAt)) + .limit(5); + + const upcomingTasks = await db + .select({ id: schema.task.id, subject: schema.task.subject, status: schema.task.status, priority: schema.task.priority, dueDate: schema.task.dueDate }) + .from(schema.task) + .where(and(eq(schema.task.organizationId, organizationId), eq(schema.task.ownerId, userId), not(eq(schema.task.status, 'Completed')), gte(schema.task.dueDate, new Date()))) + .orderBy(desc(schema.task.dueDate)) + .limit(5); + + const recentActivities = await db + .select({ id: schema.auditLog.id, action: schema.auditLog.action, entityType: schema.auditLog.entityType, description: schema.auditLog.description, timestamp: schema.auditLog.timestamp, user: { name: schema.user.name } }) + .from(schema.auditLog) + .leftJoin(schema.user, eq(schema.user.id, schema.auditLog.userId)) + .where(eq(schema.auditLog.organizationId, organizationId)) + .orderBy(desc(schema.auditLog.timestamp)) + .limit(10); + + // Calculate opportunity revenue + const [{ sumAmount }] = await db + .select({ sumAmount: sql`sum(${schema.opportunity.amount})` }) + .from(schema.opportunity) + .where(eq(schema.opportunity.organizationId, organizationId)); + + const response = { + success: true, + metrics: { + totalLeads: countsRow.totalLeads, + totalOpportunities: countsRow.totalOpportunities, + totalAccounts: countsRow.totalAccounts, + totalContacts: countsRow.totalContacts, + pendingTasks: countsRow.pendingTasks, + opportunityRevenue: Number(sumAmount || 0) + }, + recentData: { + leads: recentLeads, + opportunities: recentOpportunities, + tasks: upcomingTasks, + activities: recentActivities + } + }; + + res.json(response); + } catch (error) { + console.error('Dashboard API error:', error); + res.status(500).json({ + success: false, + error: 'Failed to load dashboard data' + }); + } +}); + +/** + * @swagger + * /dashboard/metrics: + * get: + * summary: Get dashboard metrics only (lightweight endpoint) + * tags: [Dashboard] + * security: + * - bearerAuth: [] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * description: Organization ID + * responses: + * 200: + * description: Dashboard metrics retrieved successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * metrics: + * $ref: '#/components/schemas/DashboardMetrics' + * 400: + * description: Missing organization ID + * 401: + * description: Unauthorized + * 403: + * description: Access denied to organization + * 500: + * description: Internal server error + */ +router.get('/metrics', verifyToken, requireOrganization, async (req, res) => { + try { + const userId = req.userId; + const organizationId = req.organizationId; + + // Fetch only metrics for lightweight response + const [countsRow2] = await db + .select({ + totalLeads: db.$count(schema.lead, and(eq(schema.lead.organizationId, organizationId), eq(schema.lead.isConverted, false))), + totalOpportunities: db.$count(schema.opportunity, and(eq(schema.opportunity.organizationId, organizationId), not(eq(schema.opportunity.stage, 'CLOSED_WON')))), + totalAccounts: db.$count(schema.crmAccount, and(eq(schema.crmAccount.organizationId, organizationId), eq(schema.crmAccount.isActive, true))), + totalContacts: db.$count(schema.contact, eq(schema.contact.organizationId, organizationId)), + pendingTasks: db.$count(schema.task, and(eq(schema.task.organizationId, organizationId), not(eq(schema.task.status, 'Completed')), eq(schema.task.ownerId, userId))) + }); + + const [{ sumAmount: sumAmount2 }] = await db + .select({ sumAmount: sql`sum(${schema.opportunity.amount})` }) + .from(schema.opportunity) + .where(eq(schema.opportunity.organizationId, organizationId)); + + const response = { + success: true, + metrics: { + totalLeads: countsRow2.totalLeads, + totalOpportunities: countsRow2.totalOpportunities, + totalAccounts: countsRow2.totalAccounts, + totalContacts: countsRow2.totalContacts, + pendingTasks: countsRow2.pendingTasks, + opportunityRevenue: Number(sumAmount2 || 0) + } + }; + + res.json(response); + } catch (error) { + console.error('Dashboard metrics API error:', error); + res.status(500).json({ + success: false, + error: 'Failed to load dashboard metrics' + }); + } +}); + +export default router; \ No newline at end of file diff --git a/apps/api/routes/leads.js b/apps/api/routes/leads.js new file mode 100644 index 0000000..519e533 --- /dev/null +++ b/apps/api/routes/leads.js @@ -0,0 +1,558 @@ +import express from 'express'; +import { verifyToken, requireOrganization } from '../middleware/auth.js'; +import { db, schema } from '../lib/db.js'; +import { and, desc, eq, ilike, not, sql } from 'drizzle-orm'; + +const router = express.Router(); + +router.use(verifyToken); +router.use(requireOrganization); + +/** + * @swagger + * /leads/metadata: + * get: + * summary: Get leads metadata (enums, options, etc.) + * tags: [Leads] + * responses: + * 200: + * description: Leads metadata + * content: + * application/json: + * schema: + * type: object + * properties: + * leadStatuses: + * type: array + * items: + * type: string + * leadSources: + * type: array + * items: + * type: string + * ratings: + * type: array + * items: + * type: string + * industries: + * type: array + * items: + * type: string + */ +router.get('/metadata', async (req, res) => { + try { + const metadata = { + leadStatuses: [ + 'NEW', + 'PENDING', + 'CONTACTED', + 'QUALIFIED', + 'UNQUALIFIED', + 'CONVERTED' + ], + leadSources: [ + 'WEB', + 'PHONE_INQUIRY', + 'PARTNER_REFERRAL', + 'COLD_CALL', + 'TRADE_SHOW', + 'EMPLOYEE_REFERRAL', + 'ADVERTISEMENT', + 'OTHER' + ], + ratings: [ + 'Hot', + 'Warm', + 'Cold' + ], + industries: [ + 'Technology', + 'Healthcare', + 'Finance', + 'Education', + 'Manufacturing', + 'Retail', + 'Real Estate', + 'Consulting', + 'Media', + 'Transportation', + 'Energy', + 'Government', + 'Non-profit', + 'Other' + ] + }; + + res.json(metadata); + } catch (error) { + console.error('Get leads metadata error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * components: + * schemas: + * Lead: + * type: object + * properties: + * id: + * type: string + * firstName: + * type: string + * lastName: + * type: string + * email: + * type: string + * phone: + * type: string + * company: + * type: string + * title: + * type: string + * status: + * type: string + * enum: [NEW, PENDING, CONTACTED, QUALIFIED, UNQUALIFIED, CONVERTED] + * leadSource: + * type: string + * enum: [WEB, PHONE_INQUIRY, PARTNER_REFERRAL, COLD_CALL, TRADE_SHOW, EMPLOYEE_REFERRAL, ADVERTISEMENT, OTHER] + * industry: + * type: string + * rating: + * type: string + * enum: [Hot, Warm, Cold] + * description: + * type: string + * isConverted: + * type: boolean + * convertedAt: + * type: string + * format: date-time + * createdAt: + * type: string + * format: date-time + * updatedAt: + * type: string + * format: date-time + */ + +/** + * @swagger + * /leads: + * get: + * summary: Get all leads for organization + * tags: [Leads] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: query + * name: page + * schema: + * type: integer + * default: 1 + * - in: query + * name: limit + * schema: + * type: integer + * default: 10 + * - in: query + * name: search + * schema: + * type: string + * description: Search by name, email, or company + * - in: query + * name: status + * schema: + * type: string + * enum: [NEW, PENDING, CONTACTED, QUALIFIED, UNQUALIFIED, CONVERTED] + * description: Filter by lead status + * - in: query + * name: leadSource + * schema: + * type: string + * enum: [WEB, PHONE_INQUIRY, PARTNER_REFERRAL, COLD_CALL, TRADE_SHOW, EMPLOYEE_REFERRAL, ADVERTISEMENT, OTHER] + * description: Filter by lead source + * - in: query + * name: industry + * schema: + * type: string + * description: Filter by industry + * - in: query + * name: rating + * schema: + * type: string + * enum: [Hot, Warm, Cold] + * description: Filter by rating + * - in: query + * name: converted + * schema: + * type: boolean + * description: Filter by conversion status + * responses: + * 200: + * description: List of leads + * content: + * application/json: + * schema: + * type: object + * properties: + * leads: + * type: array + * items: + * $ref: '#/components/schemas/Lead' + * pagination: + * type: object + */ +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 10; + const skip = (page - 1) * limit; + + const { + search, + status, + leadSource, + industry, + rating, + converted + } = req.query; + + // Build where conditions + const conditions = [eq(schema.lead.organizationId, req.organizationId)]; + if (search) { + const q = `%${search}%`; + conditions.push(sql`${schema.lead.firstName} ILIKE ${q} OR ${schema.lead.lastName} ILIKE ${q} OR ${schema.lead.email} ILIKE ${q} OR ${schema.lead.company} ILIKE ${q}`); + } + if (status) conditions.push(eq(schema.lead.status, status)); + if (leadSource) conditions.push(eq(schema.lead.leadSource, leadSource)); + if (industry) conditions.push(ilike(schema.lead.industry, `%${industry}%`)); + if (rating) conditions.push(eq(schema.lead.rating, rating)); + if (converted !== undefined) conditions.push(eq(schema.lead.isConverted, converted === 'true')); + + const rows = await db + .select({ + id: schema.lead.id, + firstName: schema.lead.firstName, + lastName: schema.lead.lastName, + email: schema.lead.email, + phone: schema.lead.phone, + company: schema.lead.company, + title: schema.lead.title, + status: schema.lead.status, + leadSource: schema.lead.leadSource, + industry: schema.lead.industry, + rating: schema.lead.rating, + description: schema.lead.description, + isConverted: schema.lead.isConverted, + createdAt: schema.lead.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email + }) + .from(schema.lead) + .leftJoin(schema.user, eq(schema.user.id, schema.lead.ownerId)) + .where(and(...conditions)) + .orderBy(desc(schema.lead.createdAt)) + .limit(limit) + .offset(skip); + + const [{ count: total }] = await db + .select({ count: sql`count(*)`.as('count') }) + .from(schema.lead) + .where(and(...conditions)); + + // Calculate pagination info + const totalPages = Math.ceil(total / limit); + const hasNext = page < totalPages; + const hasPrev = page > 1; + + res.json({ + success: true, + leads: rows.map((r) => ({ + id: r.id, + firstName: r.firstName, + lastName: r.lastName, + email: r.email, + phone: r.phone, + company: r.company, + title: r.title, + status: r.status, + leadSource: r.leadSource, + industry: r.industry, + rating: r.rating, + description: r.description, + isConverted: r.isConverted, + createdAt: r.createdAt, + owner: r.ownerId ? { id: r.ownerId, name: r.ownerName, email: r.ownerEmail } : null + })), + pagination: { + page, + limit, + total, + totalPages, + hasNext, + hasPrev + } + }); + } catch (error) { + console.error('Get leads error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /leads/{id}: + * get: + * summary: Get lead by ID + * tags: [Leads] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: string + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Lead details + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/Lead' + * 404: + * description: Lead not found + */ +router.get('/:id', async (req, res) => { + try { + const [lead] = await db + .select({ + id: schema.lead.id, + firstName: schema.lead.firstName, + lastName: schema.lead.lastName, + email: schema.lead.email, + phone: schema.lead.phone, + company: schema.lead.company, + title: schema.lead.title, + status: schema.lead.status, + leadSource: schema.lead.leadSource, + industry: schema.lead.industry, + rating: schema.lead.rating, + description: schema.lead.description, + isConverted: schema.lead.isConverted, + createdAt: schema.lead.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email + }) + .from(schema.lead) + .leftJoin(schema.user, eq(schema.user.id, schema.lead.ownerId)) + .where(and(eq(schema.lead.id, req.params.id), eq(schema.lead.organizationId, req.organizationId))) + .limit(1); + + if (!lead) { + return res.status(404).json({ error: 'Lead not found' }); + } + + if (!lead) return res.status(404).json({ error: 'Lead not found' }); + res.json({ + id: lead.id, + firstName: lead.firstName, + lastName: lead.lastName, + email: lead.email, + phone: lead.phone, + company: lead.company, + title: lead.title, + status: lead.status, + leadSource: lead.leadSource, + industry: lead.industry, + rating: lead.rating, + description: lead.description, + isConverted: lead.isConverted, + createdAt: lead.createdAt, + owner: lead.ownerId ? { id: lead.ownerId, name: lead.ownerName, email: lead.ownerEmail } : null + }); + } catch (error) { + console.error('Get lead error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /leads: + * post: + * summary: Create a new lead + * tags: [Leads] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - firstName + * - lastName + * - email + * properties: + * firstName: + * type: string + * lastName: + * type: string + * email: + * type: string + * phone: + * type: string + * company: + * type: string + * title: + * type: string + * status: + * type: string + * enum: [NEW, PENDING, CONTACTED, QUALIFIED, UNQUALIFIED, CONVERTED] + * leadSource: + * type: string + * enum: [WEB, PHONE_INQUIRY, PARTNER_REFERRAL, COLD_CALL, TRADE_SHOW, EMPLOYEE_REFERRAL, ADVERTISEMENT, OTHER] + * industry: + * type: string + * rating: + * type: string + * enum: [Hot, Warm, Cold] + * description: + * type: string + * responses: + * 201: + * description: Lead created successfully + * 400: + * description: Invalid input + */ +router.post('/', async (req, res) => { + try { + const { + firstName, + lastName, + email, + phone, + company, + title, + status, + leadSource, + industry, + rating, + description + } = req.body; + + if (!firstName || !lastName || !email) { + return res.status(400).json({ error: 'First name, last name, and email are required' }); + } + + // Validate email format + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + return res.status(400).json({ error: 'Invalid email format' }); + } + + // Validate status if provided + const validStatuses = ['NEW', 'PENDING', 'CONTACTED', 'QUALIFIED', 'UNQUALIFIED', 'CONVERTED']; + if (status && !validStatuses.includes(status)) { + return res.status(400).json({ error: 'Invalid status value' }); + } + + // Validate leadSource if provided + const validSources = ['WEB', 'PHONE_INQUIRY', 'PARTNER_REFERRAL', 'COLD_CALL', 'TRADE_SHOW', 'EMPLOYEE_REFERRAL', 'ADVERTISEMENT', 'OTHER']; + if (leadSource && !validSources.includes(leadSource)) { + return res.status(400).json({ error: 'Invalid lead source value' }); + } + + // Validate rating if provided + const validRatings = ['Hot', 'Warm', 'Cold']; + if (rating && !validRatings.includes(rating)) { + return res.status(400).json({ error: 'Invalid rating value' }); + } + + const [inserted] = await db + .insert(schema.lead) + .values({ + firstName: firstName.trim(), + lastName: lastName.trim(), + email: email.trim().toLowerCase(), + phone: phone?.trim() || null, + company: company?.trim() || null, + title: title?.trim() || null, + status: status || 'PENDING', + leadSource: leadSource || null, + industry: industry?.trim() || null, + rating: rating || null, + description: description?.trim() || null, + organizationId: req.organizationId, + ownerId: req.userId + }) + .returning({ id: schema.lead.id }); + + const [row] = await db + .select({ + id: schema.lead.id, + firstName: schema.lead.firstName, + lastName: schema.lead.lastName, + email: schema.lead.email, + phone: schema.lead.phone, + company: schema.lead.company, + title: schema.lead.title, + status: schema.lead.status, + leadSource: schema.lead.leadSource, + industry: schema.lead.industry, + rating: schema.lead.rating, + description: schema.lead.description, + isConverted: schema.lead.isConverted, + createdAt: schema.lead.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email + }) + .from(schema.lead) + .leftJoin(schema.user, eq(schema.user.id, schema.lead.ownerId)) + .where(eq(schema.lead.id, inserted.id)) + .limit(1); + + res.status(201).json({ + id: row.id, + firstName: row.firstName, + lastName: row.lastName, + email: row.email, + phone: row.phone, + company: row.company, + title: row.title, + status: row.status, + leadSource: row.leadSource, + industry: row.industry, + rating: row.rating, + description: row.description, + isConverted: row.isConverted, + createdAt: row.createdAt, + owner: row.ownerId ? { id: row.ownerId, name: row.ownerName, email: row.ownerEmail } : null + }); + } catch (error) { + console.error('Create lead error:', error); + if (error.code === 'P2002') { + return res.status(409).json({ error: 'A lead with this email already exists in this organization' }); + } + res.status(500).json({ error: 'Internal server error' }); + } +}); + +export default router; \ No newline at end of file diff --git a/apps/api/routes/opportunities.js b/apps/api/routes/opportunities.js new file mode 100644 index 0000000..1237eb2 --- /dev/null +++ b/apps/api/routes/opportunities.js @@ -0,0 +1,172 @@ +import express from 'express'; +import { verifyToken, requireOrganization } from '../middleware/auth.js'; +import { db, schema } from '../lib/db.js'; +import { and, desc, eq } from 'drizzle-orm'; + +const router = express.Router(); + +router.use(verifyToken); +router.use(requireOrganization); + +/** + * @swagger + * /opportunities: + * get: + * summary: Get all opportunities for organization + * tags: [Opportunities] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * responses: + * 200: + * description: List of opportunities + */ +router.get('/', async (req, res) => { + try { + const rows = await db + .select({ + id: schema.opportunity.id, + name: schema.opportunity.name, + amount: schema.opportunity.amount, + stage: schema.opportunity.stage, + closeDate: schema.opportunity.closeDate, + createdAt: schema.opportunity.createdAt, + accountId: schema.crmAccount.id, + accountName: schema.crmAccount.name, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email + }) + .from(schema.opportunity) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.opportunity.accountId)) + .leftJoin(schema.user, eq(schema.user.id, schema.opportunity.ownerId)) + .where(eq(schema.opportunity.organizationId, req.organizationId)) + .orderBy(desc(schema.opportunity.createdAt)); + + res.json({ + opportunities: rows.map((r) => ({ + id: r.id, + name: r.name, + amount: r.amount, + stage: r.stage, + closeDate: r.closeDate, + createdAt: r.createdAt, + account: r.accountId ? { id: r.accountId, name: r.accountName } : null, + owner: r.ownerId ? { id: r.ownerId, name: r.ownerName, email: r.ownerEmail } : null + })) + }); + } catch (error) { + console.error('Get opportunities error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /opportunities: + * post: + * summary: Create a new opportunity + * tags: [Opportunities] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - name + * - amount + * - closeDate + * - stage + * properties: + * name: + * type: string + * amount: + * type: number + * closeDate: + * type: string + * format: date + * stage: + * type: string + * accountId: + * type: string + * responses: + * 201: + * description: Opportunity created successfully + */ +router.post('/', async (req, res) => { + try { + const { name, amount, closeDate, stage, accountId } = req.body; + + if (!name || !amount || !closeDate || !stage) { + return res.status(400).json({ error: 'Name, amount, close date, and stage are required' }); + } + + if (accountId) { + const [account] = await db + .select({ id: schema.crmAccount.id }) + .from(schema.crmAccount) + .where(and(eq(schema.crmAccount.id, accountId), eq(schema.crmAccount.organizationId, req.organizationId))) + .limit(1); + if (!account) return res.status(400).json({ error: 'Account not found in your organization' }); + } + + const [inserted] = await db + .insert(schema.opportunity) + .values({ + name, + amount: parseFloat(amount), + closeDate: new Date(closeDate), + stage, + accountId: accountId || null, + organizationId: req.organizationId, + ownerId: req.userId + }) + .returning({ id: schema.opportunity.id }); + + const [row] = await db + .select({ + id: schema.opportunity.id, + name: schema.opportunity.name, + amount: schema.opportunity.amount, + stage: schema.opportunity.stage, + closeDate: schema.opportunity.closeDate, + createdAt: schema.opportunity.createdAt, + accountId: schema.crmAccount.id, + accountName: schema.crmAccount.name, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email + }) + .from(schema.opportunity) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.opportunity.accountId)) + .leftJoin(schema.user, eq(schema.user.id, schema.opportunity.ownerId)) + .where(eq(schema.opportunity.id, inserted.id)) + .limit(1); + + res.status(201).json({ + id: row.id, + name: row.name, + amount: row.amount, + stage: row.stage, + closeDate: row.closeDate, + createdAt: row.createdAt, + account: row.accountId ? { id: row.accountId, name: row.accountName } : null, + owner: row.ownerId ? { id: row.ownerId, name: row.ownerName, email: row.ownerEmail } : null + }); + } catch (error) { + console.error('Create opportunity error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +export default router; \ No newline at end of file diff --git a/apps/api/routes/organizations.js b/apps/api/routes/organizations.js new file mode 100644 index 0000000..80690f1 --- /dev/null +++ b/apps/api/routes/organizations.js @@ -0,0 +1,417 @@ +import express from 'express'; +import { verifyToken } from '../middleware/auth.js'; +import { db, schema } from '../lib/db.js'; +import { and, eq, ilike, sql } from 'drizzle-orm'; + +const router = express.Router(); + +/** + * @swagger + * components: + * schemas: + * Organization: + * type: object + * properties: + * id: + * type: string + * name: + * type: string + * domain: + * type: string + * logo: + * type: string + * website: + * type: string + * industry: + * type: string + * description: + * type: string + * isActive: + * type: boolean + * createdAt: + * type: string + * format: date-time + * updatedAt: + * type: string + * format: date-time + * userRole: + * type: string + * enum: [ADMIN, USER] + * CreateOrganizationRequest: + * type: object + * required: + * - name + * properties: + * name: + * type: string + * domain: + * type: string + * logo: + * type: string + * website: + * type: string + * industry: + * type: string + * description: + * type: string + */ + +/** + * @swagger + * /organizations: + * get: + * summary: Get organizations list for the authenticated user + * tags: [Organizations] + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: page + * schema: + * type: integer + * minimum: 1 + * default: 1 + * description: Page number for pagination + * - in: query + * name: limit + * schema: + * type: integer + * minimum: 1 + * maximum: 100 + * default: 10 + * description: Number of organizations per page + * - in: query + * name: search + * schema: + * type: string + * description: Search term to filter organizations by name + * - in: query + * name: industry + * schema: + * type: string + * description: Filter by industry + * - in: query + * name: active + * schema: + * type: boolean + * description: Filter by active status + * responses: + * 200: + * description: List of organizations + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * organizations: + * type: array + * items: + * $ref: '#/components/schemas/Organization' + * pagination: + * type: object + * properties: + * page: + * type: integer + * limit: + * type: integer + * total: + * type: integer + * totalPages: + * type: integer + * hasNext: + * type: boolean + * hasPrev: + * type: boolean + * 401: + * description: Unauthorized + * 500: + * description: Internal server error + */ +router.get('/', verifyToken, async (req, res) => { + try { + const userId = req.userId; + const { + page = 1, + limit = 10, + search, + industry, + active + } = req.query; + + // Validate pagination parameters + const pageNum = Math.max(1, parseInt(page) || 1); + const limitNum = Math.min(100, Math.max(1, parseInt(limit) || 10)); + const skip = (pageNum - 1) * limitNum; + + // Build where clause for filtering + let whereClause = { + users: { + some: { + userId: userId + } + } + }; + + // Add search filter + if (search) { + whereClause.name = { + contains: search, + mode: 'insensitive' + }; + } + + // Add industry filter + if (industry) { + whereClause.industry = { + contains: industry, + mode: 'insensitive' + }; + } + + // Add active status filter + if (active !== undefined) { + whereClause.isActive = active === 'true'; + } + + // Get organizations with user role + // Build Drizzle conditions + const conditions = [eq(schema.member.userId, userId)]; + if (search) { + conditions.push(ilike(schema.organization.name, `%${search}%`)); + } + if (industry) { + conditions.push(ilike(schema.organization.industry, `%${industry}%`)); + } + if (active !== undefined) { + conditions.push(eq(schema.organization.isActive, active === 'true')); + } + + const organizations = await db + .select({ + id: schema.organization.id, + name: schema.organization.name, + domain: schema.organization.domain, + logo: schema.organization.logo, + website: schema.organization.website, + industry: schema.organization.industry, + description: schema.organization.description, + isActive: schema.organization.isActive, + createdAt: schema.organization.createdAt, + updatedAt: schema.organization.updatedAt, + userRole: schema.member.role, + joinedAt: schema.member.createdAt + }) + .from(schema.organization) + .innerJoin(schema.member, eq(schema.member.organizationId, schema.organization.id)) + .where(and(...conditions)) + .orderBy(schema.organization.name) + .limit(limitNum) + .offset(skip); + + const [{ count: totalCount }] = await db + .select({ count: sql`count(distinct ${schema.organization.id})`.as('count') }) + .from(schema.organization) + .innerJoin(schema.member, eq(schema.member.organizationId, schema.organization.id)) + .where(and(...conditions)); + + // Format response + const formattedOrganizations = organizations.map(org => ({ + id: org.id, + name: org.name, + domain: org.domain, + logo: org.logo, + website: org.website, + industry: org.industry, + description: org.description, + isActive: org.isActive, + createdAt: org.createdAt, + updatedAt: org.updatedAt, + userRole: (org.userRole || 'user').toUpperCase(), + joinedAt: org.joinedAt + })); + + // Calculate pagination info + const totalPages = Math.ceil(totalCount / limitNum); + const hasNext = pageNum < totalPages; + const hasPrev = pageNum > 1; + + res.json({ + success: true, + organizations: formattedOrganizations, + pagination: { + page: pageNum, + limit: limitNum, + total: totalCount, + totalPages, + hasNext, + hasPrev + } + }); + + } catch (error) { + console.error('Organizations list error:', error); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +/** + * @swagger + * /organizations: + * post: + * summary: Create a new organization + * tags: [Organizations] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/CreateOrganizationRequest' + * responses: + * 201: + * description: Organization created successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * success: + * type: boolean + * organization: + * $ref: '#/components/schemas/Organization' + * 400: + * description: Bad request - validation error + * 401: + * description: Unauthorized + * 409: + * description: Organization with this name already exists + * 500: + * description: Internal server error + */ +router.post('/', verifyToken, async (req, res) => { + try { + const userId = req.userId; + const { + name, + domain, + logo, + website, + industry, + description + } = req.body; + + // Validate required fields + if (!name || !name.trim()) { + return res.status(400).json({ + success: false, + error: 'Organization name is required' + }); + } + + // Check if organization with this name already exists (case-insensitive) + const existing = await db + .select({ id: schema.organization.id }) + .from(schema.organization) + .where(ilike(schema.organization.name, name.trim())) + .limit(1); + + if (existing.length > 0) { + return res.status(409).json({ + success: false, + error: 'Organization with this name already exists' + }); + } + + // Validate website URL format if provided + if (website && website.trim()) { + try { + new URL(website.trim()); + } catch (error) { + return res.status(400).json({ + success: false, + error: 'Invalid website URL format' + }); + } + } + + // Create organization + const [orgInserted] = await db + .insert(schema.organization) + .values({ + name: name.trim(), + domain: domain?.trim() || null, + logo: logo?.trim() || null, + website: website?.trim() || null, + industry: industry?.trim() || null, + description: description?.trim() || null, + isActive: true + }) + .returning({ id: schema.organization.id, createdAt: schema.organization.createdAt, updatedAt: schema.organization.updatedAt }); + + // Add current user as admin member + await db + .insert(schema.member) + .values({ + organizationId: orgInserted.id, + userId: userId, + role: 'admin' + }); + + // Fetch organization back with membership info for response + const [organization] = await db + .select({ + id: schema.organization.id, + name: schema.organization.name, + domain: schema.organization.domain, + logo: schema.organization.logo, + website: schema.organization.website, + industry: schema.organization.industry, + description: schema.organization.description, + isActive: schema.organization.isActive, + createdAt: schema.organization.createdAt, + updatedAt: schema.organization.updatedAt, + userRole: schema.member.role, + joinedAt: schema.member.createdAt + }) + .from(schema.organization) + .innerJoin(schema.member, eq(schema.member.organizationId, schema.organization.id)) + .where(and(eq(schema.organization.id, orgInserted.id), eq(schema.member.userId, userId))) + .limit(1); + + // Format response + const formattedOrganization = { + id: organization.id, + name: organization.name, + domain: organization.domain, + logo: organization.logo, + website: organization.website, + industry: organization.industry, + description: organization.description, + isActive: organization.isActive, + createdAt: organization.createdAt, + updatedAt: organization.updatedAt, + userRole: (organization.userRole || 'admin').toUpperCase(), + joinedAt: organization.joinedAt + }; + + res.status(201).json({ + success: true, + organization: formattedOrganization + }); + + } catch (error) { + console.error('Organization creation error:', error); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + +export default router; diff --git a/apps/api/routes/tasks.js b/apps/api/routes/tasks.js new file mode 100644 index 0000000..3301523 --- /dev/null +++ b/apps/api/routes/tasks.js @@ -0,0 +1,1033 @@ +import express from 'express'; +import { verifyToken, requireOrganization } from '../middleware/auth.js'; +import { db, schema } from '../lib/db.js'; +import { and, desc, eq, ilike, not, sql } from 'drizzle-orm'; + +const router = express.Router(); + +router.use(verifyToken); +router.use(requireOrganization); + +/** + * @swagger + * components: + * schemas: + * Task: + * type: object + * properties: + * id: + * type: string + * subject: + * type: string + * status: + * type: string + * enum: [Not Started, In Progress, Completed, Deferred, Waiting] + * priority: + * type: string + * enum: [High, Normal, Low] + * dueDate: + * type: string + * format: date-time + * description: + * type: string + * createdAt: + * type: string + * format: date-time + * updatedAt: + * type: string + * format: date-time + */ + +/** + * @swagger + * /tasks: + * get: + * summary: Get all tasks for organization + * tags: [Tasks] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: query + * name: status + * schema: + * type: string + * description: Filter tasks by status + * - in: query + * name: priority + * schema: + * type: string + * description: Filter tasks by priority + * - in: query + * name: ownerId + * schema: + * type: string + * description: Filter tasks by owner ID + * - in: query + * name: accountId + * schema: + * type: string + * description: Filter tasks by account ID + * - in: query + * name: contactId + * schema: + * type: string + * description: Filter tasks by contact ID + * - in: query + * name: leadId + * schema: + * type: string + * description: Filter tasks by lead ID + * - in: query + * name: opportunityId + * schema: + * type: string + * description: Filter tasks by opportunity ID + * - in: query + * name: caseId + * schema: + * type: string + * description: Filter tasks by case ID + * - in: query + * name: limit + * schema: + * type: integer + * default: 50 + * description: Limit number of results + * - in: query + * name: offset + * schema: + * type: integer + * default: 0 + * description: Offset for pagination + * responses: + * 200: + * description: List of tasks + */ +router.get('/', async (req, res) => { + try { + const { + status, + priority, + ownerId, + accountId, + contactId, + leadId, + opportunityId, + caseId, + limit = 50, + offset = 0 + } = req.query; + + // Build where clause for filtering + const where = { + organizationId: req.organizationId, + ...(status && { status }), + ...(priority && { priority }), + ...(ownerId && { ownerId }), + ...(accountId && { accountId }), + ...(contactId && { contactId }), + ...(leadId && { leadId }), + ...(opportunityId && { opportunityId }), + ...(caseId && { caseId }) + }; + + // Build conditions for Drizzle + const conditions = [eq(schema.task.organizationId, req.organizationId)]; + if (status) conditions.push(eq(schema.task.status, status)); + if (priority) conditions.push(eq(schema.task.priority, priority)); + if (ownerId) conditions.push(eq(schema.task.ownerId, ownerId)); + if (accountId) conditions.push(eq(schema.task.accountId, accountId)); + if (contactId) conditions.push(eq(schema.task.contactId, contactId)); + if (leadId) conditions.push(eq(schema.task.leadId, leadId)); + if (opportunityId) conditions.push(eq(schema.task.opportunityId, opportunityId)); + if (caseId) conditions.push(eq(schema.task.caseId, caseId)); + + const rows = await db + .select({ + id: schema.task.id, + subject: schema.task.subject, + status: schema.task.status, + priority: schema.task.priority, + dueDate: schema.task.dueDate, + createdAt: schema.task.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + createdById: schema.user.id, + createdByName: schema.user.name, + createdByEmail: schema.user.email, + accountId: schema.crmAccount.id, + accountName: schema.crmAccount.name, + accountType: schema.crmAccount.type, + contactId: schema.contact.id, + contactFirstName: schema.contact.firstName, + contactLastName: schema.contact.lastName, + contactEmail: schema.contact.email, + leadId: schema.lead.id, + leadFirstName: schema.lead.firstName, + leadLastName: schema.lead.lastName, + leadEmail: schema.lead.email, + leadCompany: schema.lead.company, + opportunityId: schema.opportunity.id, + opportunityName: schema.opportunity.name, + opportunityAmount: schema.opportunity.amount, + caseId: schema.caseTable.id, + caseNumber: schema.caseTable.caseNumber, + caseSubject: schema.caseTable.subject, + caseStatus: schema.caseTable.status + }) + .from(schema.task) + .leftJoin(schema.user, eq(schema.user.id, schema.task.ownerId)) + .leftJoin(schema.user.as('creator'), eq(schema.user.as('creator').id, schema.task.createdById)) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.task.accountId)) + .leftJoin(schema.contact, eq(schema.contact.id, schema.task.contactId)) + .leftJoin(schema.lead, eq(schema.lead.id, schema.task.leadId)) + .leftJoin(schema.opportunity, eq(schema.opportunity.id, schema.task.opportunityId)) + .leftJoin(schema.caseTable, eq(schema.caseTable.id, schema.task.caseId)) + .where(and(...conditions)) + .orderBy(desc(schema.task.createdAt)) + .limit(parseInt(limit)) + .offset(parseInt(offset)); + + const [{ count: totalCount }] = await db + .select({ count: sql`count(*)`.as('count') }) + .from(schema.task) + .where(and(...conditions)); + + const tasks = rows.map((r) => ({ + id: r.id, + subject: r.subject, + status: r.status, + priority: r.priority, + dueDate: r.dueDate, + createdAt: r.createdAt, + owner: r.ownerId ? { id: r.ownerId, name: r.ownerName, email: r.ownerEmail } : null, + createdBy: r.createdById ? { id: r.createdById, name: r.createdByName, email: r.createdByEmail } : null, + account: r.accountId ? { id: r.accountId, name: r.accountName, type: r.accountType } : null, + contact: r.contactId ? { id: r.contactId, firstName: r.contactFirstName, lastName: r.contactLastName, email: r.contactEmail } : null, + lead: r.leadId ? { id: r.leadId, firstName: r.leadFirstName, lastName: r.leadLastName, email: r.leadEmail, company: r.leadCompany } : null, + opportunity: r.opportunityId ? { id: r.opportunityId, name: r.opportunityName, amount: r.opportunityAmount, status: r.status } : null, + case: r.caseId ? { id: r.caseId, caseNumber: r.caseNumber, subject: r.caseSubject, status: r.caseStatus } : null + })); + + res.json({ + tasks, + pagination: { + total: Number(totalCount || 0), + limit: parseInt(limit), + offset: parseInt(offset), + hasMore: parseInt(offset) + parseInt(limit) < Number(totalCount || 0) + } + }); + } catch (error) { + console.error('Get tasks error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /tasks: + * post: + * summary: Create a new task + * tags: [Tasks] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - subject + * properties: + * subject: + * type: string + * status: + * type: string + * enum: [Not Started, In Progress, Completed, Deferred, Waiting] + * default: Not Started + * priority: + * type: string + * enum: [High, Normal, Low] + * default: Normal + * dueDate: + * type: string + * format: date-time + * description: + * type: string + * ownerId: + * type: string + * description: UUID of the user who owns this task + * accountId: + * type: string + * description: UUID of the related account + * contactId: + * type: string + * description: UUID of the related contact + * leadId: + * type: string + * description: UUID of the related lead + * opportunityId: + * type: string + * description: UUID of the related opportunity + * caseId: + * type: string + * description: UUID of the related case + * responses: + * 201: + * description: Task created successfully + * 400: + * description: Validation error + */ +router.post('/', async (req, res) => { + try { + const { + subject, + status = 'Not Started', + priority = 'Normal', + dueDate, + description, + ownerId, + accountId, + contactId, + leadId, + opportunityId, + caseId + } = req.body; + + if (!subject) { + return res.status(400).json({ error: 'Subject is required' }); + } + + // Validate status + const validStatuses = ['Not Started', 'In Progress', 'Completed', 'Deferred', 'Waiting']; + if (status && !validStatuses.includes(status)) { + return res.status(400).json({ error: 'Invalid status. Valid options: ' + validStatuses.join(', ') }); + } + + // Validate priority + const validPriorities = ['High', 'Normal', 'Low']; + if (priority && !validPriorities.includes(priority)) { + return res.status(400).json({ error: 'Invalid priority. Valid options: ' + validPriorities.join(', ') }); + } + + // Validate owner exists in organization + let owner = null; + const finalOwnerId = ownerId || req.userId; // Default to current user if no owner specified + + const [member] = await db + .select({ id: schema.member.id }) + .from(schema.member) + .where(and(eq(schema.member.userId, finalOwnerId), eq(schema.member.organizationId, req.organizationId))) + .limit(1); + + if (!member) { + return res.status(400).json({ error: 'Owner must be a member of this organization' }); + } + + // Validate related entities if provided + if (accountId) { + const [account] = await db + .select({ id: schema.crmAccount.id }) + .from(schema.crmAccount) + .where(and(eq(schema.crmAccount.id, accountId), eq(schema.crmAccount.organizationId, req.organizationId))) + .limit(1); + if (!account) { + return res.status(400).json({ error: 'Account not found in your organization' }); + } + } + + if (contactId) { + const [contact] = await db + .select({ id: schema.contact.id }) + .from(schema.contact) + .where(and(eq(schema.contact.id, contactId), eq(schema.contact.organizationId, req.organizationId))) + .limit(1); + if (!contact) { + return res.status(400).json({ error: 'Contact not found in your organization' }); + } + } + + if (leadId) { + const [lead] = await db + .select({ id: schema.lead.id }) + .from(schema.lead) + .where(and(eq(schema.lead.id, leadId), eq(schema.lead.organizationId, req.organizationId))) + .limit(1); + if (!lead) { + return res.status(400).json({ error: 'Lead not found in your organization' }); + } + } + + if (opportunityId) { + const [opp] = await db + .select({ id: schema.opportunity.id }) + .from(schema.opportunity) + .where(and(eq(schema.opportunity.id, opportunityId), eq(schema.opportunity.organizationId, req.organizationId))) + .limit(1); + const opportunity = opp; + if (!opportunity) { + return res.status(400).json({ error: 'Opportunity not found in your organization' }); + } + } + + if (caseId) { + const [caseRecord] = await db + .select({ id: schema.caseTable.id }) + .from(schema.caseTable) + .where(and(eq(schema.caseTable.id, caseId), eq(schema.caseTable.organizationId, req.organizationId))) + .limit(1); + if (!caseRecord) { + return res.status(400).json({ error: 'Case not found in your organization' }); + } + } + + // Create the task + const [inserted] = await db + .insert(schema.task) + .values({ + subject, + status, + priority, + dueDate: dueDate ? new Date(dueDate) : null, + description: description || null, + ownerId: finalOwnerId, + createdById: req.userId, + organizationId: req.organizationId, + accountId: accountId || null, + contactId: contactId || null, + leadId: leadId || null, + opportunityId: opportunityId || null, + caseId: caseId || null + }) + .returning({ id: schema.task.id }); + + const [row] = await db + .select({ + id: schema.task.id, + subject: schema.task.subject, + status: schema.task.status, + priority: schema.task.priority, + dueDate: schema.task.dueDate, + createdAt: schema.task.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + createdById: schema.user.id, + createdByName: schema.user.name, + createdByEmail: schema.user.email, + accountId: schema.crmAccount.id, + accountName: schema.crmAccount.name, + accountType: schema.crmAccount.type, + contactId: schema.contact.id, + contactFirstName: schema.contact.firstName, + contactLastName: schema.contact.lastName, + contactEmail: schema.contact.email, + leadId: schema.lead.id, + leadFirstName: schema.lead.firstName, + leadLastName: schema.lead.lastName, + leadEmail: schema.lead.email, + leadCompany: schema.lead.company, + opportunityId: schema.opportunity.id, + opportunityName: schema.opportunity.name, + opportunityAmount: schema.opportunity.amount, + caseId: schema.caseTable.id, + caseNumber: schema.caseTable.caseNumber, + caseSubject: schema.caseTable.subject, + caseStatus: schema.caseTable.status + }) + .from(schema.task) + .leftJoin(schema.user, eq(schema.user.id, schema.task.ownerId)) + .leftJoin(schema.user.as('creator'), eq(schema.user.as('creator').id, schema.task.createdById)) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.task.accountId)) + .leftJoin(schema.contact, eq(schema.contact.id, schema.task.contactId)) + .leftJoin(schema.lead, eq(schema.lead.id, schema.task.leadId)) + .leftJoin(schema.opportunity, eq(schema.opportunity.id, schema.task.opportunityId)) + .leftJoin(schema.caseTable, eq(schema.caseTable.id, schema.task.caseId)) + .where(eq(schema.task.id, inserted.id)) + .limit(1); + + res.status(201).json({ + id: row.id, + subject: row.subject, + status: row.status, + priority: row.priority, + dueDate: row.dueDate, + createdAt: row.createdAt, + owner: row.ownerId ? { id: row.ownerId, name: row.ownerName, email: row.ownerEmail } : null, + createdBy: row.createdById ? { id: row.createdById, name: row.createdByName, email: row.createdByEmail } : null, + account: row.accountId ? { id: row.accountId, name: row.accountName, type: row.accountType } : null, + contact: row.contactId ? { id: row.contactId, firstName: row.contactFirstName, lastName: row.contactLastName, email: row.contactEmail } : null, + lead: row.leadId ? { id: row.leadId, firstName: row.leadFirstName, lastName: row.leadLastName, email: row.leadEmail, company: row.leadCompany } : null, + opportunity: row.opportunityId ? { id: row.opportunityId, name: row.opportunityName, amount: row.opportunityAmount, status: row.status } : null, + case: row.caseId ? { id: row.caseId, caseNumber: row.caseNumber, subject: row.caseSubject, status: row.caseStatus } : null + }); + } catch (error) { + console.error('Create task error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /tasks/{id}: + * get: + * summary: Get a specific task by ID + * tags: [Tasks] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: Task ID + * responses: + * 200: + * description: Task details + * 404: + * description: Task not found + */ +router.get('/:id', async (req, res) => { + try { + const { id } = req.params; + + const rows = await db + .select({ + id: schema.task.id, + subject: schema.task.subject, + status: schema.task.status, + priority: schema.task.priority, + dueDate: schema.task.dueDate, + description: schema.task.description, + createdAt: schema.task.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + createdById: schema.user.id, + createdByName: schema.user.name, + createdByEmail: schema.user.email, + accountId: schema.crmAccount.id, + accountName: schema.crmAccount.name, + accountType: schema.crmAccount.type, + accountWebsite: schema.crmAccount.website, + accountPhone: schema.crmAccount.phone, + contactId: schema.contact.id, + contactFirstName: schema.contact.firstName, + contactLastName: schema.contact.lastName, + contactEmail: schema.contact.email, + contactPhone: schema.contact.phone, + contactTitle: schema.contact.title, + leadId: schema.lead.id, + leadFirstName: schema.lead.firstName, + leadLastName: schema.lead.lastName, + leadEmail: schema.lead.email, + leadPhone: schema.lead.phone, + leadCompany: schema.lead.company, + leadStatus: schema.lead.status, + opportunityId: schema.opportunity.id, + opportunityName: schema.opportunity.name, + opportunityAmount: schema.opportunity.amount, + opportunityStatus: schema.opportunity.stage, + opportunityStage: schema.opportunity.stage, + opportunityCloseDate: schema.opportunity.closeDate, + caseId: schema.caseTable.id, + caseNumber: schema.caseTable.caseNumber, + caseSubject: schema.caseTable.subject, + caseStatus: schema.caseTable.status, + casePriority: schema.caseTable.priority, + orgId: schema.organization.id, + orgName: schema.organization.name + }) + .from(schema.task) + .leftJoin(schema.user, eq(schema.user.id, schema.task.ownerId)) + .leftJoin(schema.user.as('creator'), eq(schema.user.as('creator').id, schema.task.createdById)) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.task.accountId)) + .leftJoin(schema.contact, eq(schema.contact.id, schema.task.contactId)) + .leftJoin(schema.lead, eq(schema.lead.id, schema.task.leadId)) + .leftJoin(schema.opportunity, eq(schema.opportunity.id, schema.task.opportunityId)) + .leftJoin(schema.caseTable, eq(schema.caseTable.id, schema.task.caseId)) + .leftJoin(schema.organization, eq(schema.organization.id, schema.task.organizationId)) + .where(and(eq(schema.task.id, id), eq(schema.task.organizationId, req.organizationId))) + .limit(1); + + if (!rows.length) return res.status(404).json({ error: 'Task not found' }); + + const [base] = rows; + + // Load comments separately with author; joining all can be heavy + const comments = await db + .select({ + id: schema.comment.id, + body: schema.comment.body, + isPrivate: schema.comment.isPrivate, + createdAt: schema.comment.createdAt, + authorId: schema.user.id, + authorName: schema.user.name, + authorEmail: schema.user.email + }) + .from(schema.comment) + .leftJoin(schema.user, eq(schema.user.id, schema.comment.authorId)) + .where(eq(schema.comment.taskId, id)) + .orderBy(desc(schema.comment.createdAt)); + + res.json({ + id: base.id, + subject: base.subject, + status: base.status, + priority: base.priority, + dueDate: base.dueDate, + description: base.description, + createdAt: base.createdAt, + owner: base.ownerId ? { id: base.ownerId, name: base.ownerName, email: base.ownerEmail } : null, + createdBy: base.createdById ? { id: base.createdById, name: base.createdByName, email: base.createdByEmail } : null, + account: base.accountId ? { id: base.accountId, name: base.accountName, type: base.accountType, website: base.accountWebsite, phone: base.accountPhone } : null, + contact: base.contactId ? { id: base.contactId, firstName: base.contactFirstName, lastName: base.contactLastName, email: base.contactEmail, phone: base.contactPhone, title: base.contactTitle } : null, + lead: base.leadId ? { id: base.leadId, firstName: base.leadFirstName, lastName: base.leadLastName, email: base.leadEmail, phone: base.leadPhone, company: base.leadCompany, status: base.leadStatus } : null, + opportunity: base.opportunityId ? { id: base.opportunityId, name: base.opportunityName, amount: base.opportunityAmount, status: base.opportunityStatus, stage: base.opportunityStage, closeDate: base.opportunityCloseDate } : null, + case: base.caseId ? { id: base.caseId, caseNumber: base.caseNumber, subject: base.caseSubject, status: base.caseStatus, priority: base.casePriority } : null, + organization: base.orgId ? { id: base.orgId, name: base.orgName } : null, + comments: comments.map((c) => ({ id: c.id, body: c.body, isPrivate: c.isPrivate, createdAt: c.createdAt, author: c.authorId ? { id: c.authorId, name: c.authorName, email: c.authorEmail } : null })) + }); + } catch (error) { + console.error('Get task details error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /tasks/{id}: + * put: + * summary: Update a specific task + * tags: [Tasks] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: Task ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * subject: + * type: string + * status: + * type: string + * enum: [Not Started, In Progress, Completed, Deferred, Waiting] + * priority: + * type: string + * enum: [High, Normal, Low] + * dueDate: + * type: string + * format: date-time + * description: + * type: string + * ownerId: + * type: string + * description: UUID of the user who owns this task + * accountId: + * type: string + * description: UUID of the related account + * contactId: + * type: string + * description: UUID of the related contact + * leadId: + * type: string + * description: UUID of the related lead + * opportunityId: + * type: string + * description: UUID of the related opportunity + * caseId: + * type: string + * description: UUID of the related case + * responses: + * 200: + * description: Task updated successfully + * 400: + * description: Validation error + * 404: + * description: Task not found + */ +router.put('/:id', async (req, res) => { + try { + const { id } = req.params; + const { + subject, + status, + priority, + dueDate, + description, + ownerId, + accountId, + contactId, + leadId, + opportunityId, + caseId + } = req.body; + + // Check if task exists and belongs to organization + const [existingTask] = await db + .select({ id: schema.task.id }) + .from(schema.task) + .where(and(eq(schema.task.id, id), eq(schema.task.organizationId, req.organizationId))) + .limit(1); + + if (!existingTask) { + return res.status(404).json({ error: 'Task not found' }); + } + + // Validate status if provided + if (status) { + const validStatuses = ['Not Started', 'In Progress', 'Completed', 'Deferred', 'Waiting']; + if (!validStatuses.includes(status)) { + return res.status(400).json({ error: 'Invalid status. Valid options: ' + validStatuses.join(', ') }); + } + } + + // Validate priority if provided + if (priority) { + const validPriorities = ['High', 'Normal', 'Low']; + if (!validPriorities.includes(priority)) { + return res.status(400).json({ error: 'Invalid priority. Valid options: ' + validPriorities.join(', ') }); + } + } + + // Validate owner exists in organization if provided + if (ownerId) { + const [member] = await db + .select({ id: schema.member.id }) + .from(schema.member) + .where(and(eq(schema.member.userId, ownerId), eq(schema.member.organizationId, req.organizationId))) + .limit(1); + + if (!member) { + return res.status(400).json({ error: 'Owner must be a member of this organization' }); + } + } + + // Validate related entities if provided + if (accountId !== undefined) { + if (accountId) { + const [account] = await db + .select({ id: schema.crmAccount.id }) + .from(schema.crmAccount) + .where(and(eq(schema.crmAccount.id, accountId), eq(schema.crmAccount.organizationId, req.organizationId))) + .limit(1); + if (!account) { + return res.status(400).json({ error: 'Account not found in your organization' }); + } + } + } + + if (contactId !== undefined) { + if (contactId) { + const [contact] = await db + .select({ id: schema.contact.id }) + .from(schema.contact) + .where(and(eq(schema.contact.id, contactId), eq(schema.contact.organizationId, req.organizationId))) + .limit(1); + if (!contact) { + return res.status(400).json({ error: 'Contact not found in your organization' }); + } + } + } + + if (leadId !== undefined) { + if (leadId) { + const [lead] = await db + .select({ id: schema.lead.id }) + .from(schema.lead) + .where(and(eq(schema.lead.id, leadId), eq(schema.lead.organizationId, req.organizationId))) + .limit(1); + if (!lead) { + return res.status(400).json({ error: 'Lead not found in your organization' }); + } + } + } + + if (opportunityId !== undefined) { + if (opportunityId) { + const [opp] = await db + .select({ id: schema.opportunity.id }) + .from(schema.opportunity) + .where(and(eq(schema.opportunity.id, opportunityId), eq(schema.opportunity.organizationId, req.organizationId))) + .limit(1); + const opportunity = opp; + if (!opportunity) { + return res.status(400).json({ error: 'Opportunity not found in your organization' }); + } + } + } + + if (caseId !== undefined) { + if (caseId) { + const [caseRecord] = await db + .select({ id: schema.caseTable.id }) + .from(schema.caseTable) + .where(and(eq(schema.caseTable.id, caseId), eq(schema.caseTable.organizationId, req.organizationId))) + .limit(1); + if (!caseRecord) { + return res.status(400).json({ error: 'Case not found in your organization' }); + } + } + } + + // Build update data object + const updateData = {}; + if (subject !== undefined) updateData.subject = subject; + if (status !== undefined) updateData.status = status; + if (priority !== undefined) updateData.priority = priority; + if (dueDate !== undefined) updateData.dueDate = dueDate ? new Date(dueDate) : null; + if (description !== undefined) updateData.description = description; + if (ownerId !== undefined) updateData.ownerId = ownerId; + if (accountId !== undefined) updateData.accountId = accountId; + if (contactId !== undefined) updateData.contactId = contactId; + if (leadId !== undefined) updateData.leadId = leadId; + if (opportunityId !== undefined) updateData.opportunityId = opportunityId; + if (caseId !== undefined) updateData.caseId = caseId; + + // Update the task + const [updated] = await db + .update(schema.task) + .set(updateData) + .where(eq(schema.task.id, id)) + .returning({ id: schema.task.id }); + + const [row2] = await db + .select({ + id: schema.task.id, + subject: schema.task.subject, + status: schema.task.status, + priority: schema.task.priority, + dueDate: schema.task.dueDate, + createdAt: schema.task.createdAt, + ownerId: schema.user.id, + ownerName: schema.user.name, + ownerEmail: schema.user.email, + createdById: schema.user.id, + createdByName: schema.user.name, + createdByEmail: schema.user.email, + accountId: schema.crmAccount.id, + accountName: schema.crmAccount.name, + accountType: schema.crmAccount.type, + contactId: schema.contact.id, + contactFirstName: schema.contact.firstName, + contactLastName: schema.contact.lastName, + contactEmail: schema.contact.email, + leadId: schema.lead.id, + leadFirstName: schema.lead.firstName, + leadLastName: schema.lead.lastName, + leadEmail: schema.lead.email, + leadCompany: schema.lead.company, + opportunityId: schema.opportunity.id, + opportunityName: schema.opportunity.name, + opportunityAmount: schema.opportunity.amount, + caseId: schema.caseTable.id, + caseNumber: schema.caseTable.caseNumber, + caseSubject: schema.caseTable.subject, + caseStatus: schema.caseTable.status + }) + .from(schema.task) + .leftJoin(schema.user, eq(schema.user.id, schema.task.ownerId)) + .leftJoin(schema.user.as('creator'), eq(schema.user.as('creator').id, schema.task.createdById)) + .leftJoin(schema.crmAccount, eq(schema.crmAccount.id, schema.task.accountId)) + .leftJoin(schema.contact, eq(schema.contact.id, schema.task.contactId)) + .leftJoin(schema.lead, eq(schema.lead.id, schema.task.leadId)) + .leftJoin(schema.opportunity, eq(schema.opportunity.id, schema.task.opportunityId)) + .leftJoin(schema.caseTable, eq(schema.caseTable.id, schema.task.caseId)) + .where(eq(schema.task.id, updated.id)) + .limit(1); + + res.json({ + id: row2.id, + subject: row2.subject, + status: row2.status, + priority: row2.priority, + dueDate: row2.dueDate, + createdAt: row2.createdAt, + owner: row2.ownerId ? { id: row2.ownerId, name: row2.ownerName, email: row2.ownerEmail } : null, + createdBy: row2.createdById ? { id: row2.createdById, name: row2.createdByName, email: row2.createdByEmail } : null, + account: row2.accountId ? { id: row2.accountId, name: row2.accountName, type: row2.accountType } : null, + contact: row2.contactId ? { id: row2.contactId, firstName: row2.contactFirstName, lastName: row2.contactLastName, email: row2.contactEmail } : null, + lead: row2.leadId ? { id: row2.leadId, firstName: row2.leadFirstName, lastName: row2.leadLastName, email: row2.leadEmail, company: row2.leadCompany } : null, + opportunity: row2.opportunityId ? { id: row2.opportunityId, name: row2.opportunityName, amount: row2.opportunityAmount, status: row2.status } : null, + case: row2.caseId ? { id: row2.caseId, caseNumber: row2.caseNumber, subject: row2.caseSubject, status: row2.caseStatus } : null + }); + } catch (error) { + console.error('Update task error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /tasks/{id}: + * delete: + * summary: Delete a specific task + * tags: [Tasks] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: Task ID + * responses: + * 200: + * description: Task deleted successfully + * 404: + * description: Task not found + */ +router.delete('/:id', async (req, res) => { + try { + const { id } = req.params; + + // Check if task exists and belongs to organization + const [existingTask] = await db + .select({ id: schema.task.id }) + .from(schema.task) + .where(and(eq(schema.task.id, id), eq(schema.task.organizationId, req.organizationId))) + .limit(1); + + if (!existingTask) { + return res.status(404).json({ error: 'Task not found' }); + } + + // Delete the task (this will also cascade delete related comments due to schema relationship) + await db.delete(schema.task).where(eq(schema.task.id, id)); + + res.json({ message: 'Task deleted successfully' }); + } catch (error) { + console.error('Delete task error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +/** + * @swagger + * /tasks/{id}/comments: + * post: + * summary: Add a comment to a task + * tags: [Tasks] + * parameters: + * - in: header + * name: X-Organization-ID + * required: true + * schema: + * type: string + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: Task ID + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - body + * properties: + * body: + * type: string + * isPrivate: + * type: boolean + * default: false + * responses: + * 201: + * description: Comment added successfully + * 404: + * description: Task not found + */ +router.post('/:id/comments', async (req, res) => { + try { + const { id } = req.params; + const { body, isPrivate = false } = req.body; + + if (!body) { + return res.status(400).json({ error: 'Comment body is required' }); + } + + // Check if task exists and belongs to organization + const [existingTask] = await db + .select({ id: schema.task.id }) + .from(schema.task) + .where(and(eq(schema.task.id, id), eq(schema.task.organizationId, req.organizationId))) + .limit(1); + + if (!existingTask) { + return res.status(404).json({ error: 'Task not found' }); + } + + // Create the comment + const [inserted] = await db + .insert(schema.comment) + .values({ + body, + isPrivate, + authorId: req.userId, + organizationId: req.organizationId, + taskId: id + }) + .returning({ id: schema.comment.id }); + + const [comment] = await db + .select({ + id: schema.comment.id, + body: schema.comment.body, + isPrivate: schema.comment.isPrivate, + createdAt: schema.comment.createdAt, + authorId: schema.user.id, + authorName: schema.user.name, + authorEmail: schema.user.email + }) + .from(schema.comment) + .leftJoin(schema.user, eq(schema.user.id, schema.comment.authorId)) + .where(eq(schema.comment.id, inserted.id)) + .limit(1); + + res.status(201).json({ + id: comment.id, + body: comment.body, + isPrivate: comment.isPrivate, + createdAt: comment.createdAt, + author: comment.authorId ? { id: comment.authorId, name: comment.authorName, email: comment.authorEmail } : null + }); + } catch (error) { + console.error('Add task comment error:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +export default router; diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..a431deb --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1,18 @@ +GOOGLE_CLIENT_ID="your-google-client-id-here" +GOOGLE_CLIENT_SECRET="your-google-client-secret-here" +GOOGLE_LOGIN_DOMAIN="http://localhost:5173" +DATABASE_URL="postgresql://username:password@localhost:5432/bottlecrm?schema=public" + +# API Configuration +API_PORT=3001 +JWT_SECRET=your-super-secure-jwt-secret-key-change-this-in-production +JWT_EXPIRES_IN=24h +FRONTEND_URL=http://localhost:5173 + +# Logging Configuration +ENABLE_REQUEST_LOGGING=true +LOG_REQUEST_BODY=false +LOG_RESPONSE_BODY=false + +# Environment +NODE_ENV=development \ No newline at end of file diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 0000000..7f47028 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,28 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +generated/* +src/generated/* +.github/prompts/* +supabase \ No newline at end of file diff --git a/.npmrc b/apps/web/.npmrc similarity index 100% rename from .npmrc rename to apps/web/.npmrc diff --git a/.prettierignore b/apps/web/.prettierignore similarity index 100% rename from .prettierignore rename to apps/web/.prettierignore diff --git a/.prettierrc b/apps/web/.prettierrc similarity index 100% rename from .prettierrc rename to apps/web/.prettierrc diff --git a/eslint.config.js b/apps/web/eslint.config.js similarity index 100% rename from eslint.config.js rename to apps/web/eslint.config.js diff --git a/jsconfig.json b/apps/web/jsconfig.json similarity index 100% rename from jsconfig.json rename to apps/web/jsconfig.json diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..7f7c2f5 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,59 @@ +{ + "name": "@opensource-startup-crm/web", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "prettier --write .", + "lint": "prettier --check . && eslint ." + }, + "devDependencies": { + "@better-auth/cli": "^1.3.4", + "@sveltejs/adapter-cloudflare": "^7.1.3", + "@sveltejs/kit": "^2.25.2", + "@sveltejs/vite-plugin-svelte": "^6.1.0", + "@tailwindcss/typography": "^0.5.16", + "@tailwindcss/vite": "^4.1.11", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "0.44.3", + "eslint": "^9.31.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-svelte": "^3.11.0", + "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", + "prettier-plugin-tailwindcss": "^0.6.14", + "svelte": "^5.36.14", + "svelte-check": "^4.3.0", + "svelte-dnd-action": "^0.9.64", + "tailwindcss": "^4.1.11", + "typescript": "^5.8.3", + "vite": "^7.0.5" + }, + "pnpm": { + "onlyBuiltDependencies": [ + "esbuild" + ] + }, + "dependencies": { + "@opensource-startup-crm/constants": "workspace:*", + "@lucide/svelte": "^0.525.0", + "@opensource-startup-crm/database": "workspace:*", + "better-auth": "^1.3.4", + "date-fns": "^4.1.0", + "dotenv": "^17.2.1", + "libphonenumber-js": "^1.12.10", + "marked": "^16.1.1", + "optimistikit": "^1.0.2", + "postgres": "^3.4.7", + "svelte-highlight": "^7.8.3", + "svelte-meta-tags": "^4.4.0", + "uuid": "^11.1.0", + "zod": "^4.0.8" + } +} diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml new file mode 100644 index 0000000..2711880 --- /dev/null +++ b/apps/web/pnpm-lock.yaml @@ -0,0 +1,6771 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@lucide/svelte': + specifier: ^0.525.0 + version: 0.525.0(svelte@5.36.14) + '@prisma/client': + specifier: 6.12.0 + version: 6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3) + axios: + specifier: ^1.11.0 + version: 1.11.0 + bcryptjs: + specifier: ^3.0.2 + version: 3.0.2 + better-auth: + specifier: ^1.3.4 + version: 1.3.4 + cors: + specifier: ^2.8.5 + version: 2.8.5 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + dotenv: + specifier: ^17.2.1 + version: 17.2.1 + express: + specifier: ^5.1.0 + version: 5.1.0 + express-rate-limit: + specifier: ^8.0.1 + version: 8.0.1(express@5.1.0) + google-auth-library: + specifier: ^10.2.0 + version: 10.2.0 + helmet: + specifier: ^8.1.0 + version: 8.1.0 + jsonwebtoken: + specifier: ^9.0.2 + version: 9.0.2 + libphonenumber-js: + specifier: ^1.12.10 + version: 1.12.10 + marked: + specifier: ^16.1.1 + version: 16.1.1 + morgan: + specifier: ^1.10.1 + version: 1.10.1 + postgres: + specifier: ^3.4.7 + version: 3.4.7 + svelte-highlight: + specifier: ^7.8.3 + version: 7.8.3 + svelte-meta-tags: + specifier: ^4.4.0 + version: 4.4.0(svelte@5.36.14) + swagger-jsdoc: + specifier: ^6.2.8 + version: 6.2.8(openapi-types@12.1.3) + swagger-ui-express: + specifier: ^5.0.1 + version: 5.0.1(express@5.1.0) + uuid: + specifier: ^11.1.0 + version: 11.1.0 + winston: + specifier: ^3.17.0 + version: 3.17.0 + zod: + specifier: ^4.0.8 + version: 4.0.8 + devDependencies: + '@better-auth/cli': + specifier: ^1.3.4 + version: 1.3.4(kysely@0.28.4)(postgres@3.4.7) + '@eslint/compat': + specifier: ^1.3.1 + version: 1.3.1(eslint@9.31.0(jiti@2.4.2)) + '@eslint/js': + specifier: ^9.31.0 + version: 9.31.0 + '@sveltejs/adapter-node': + specifier: ^5.2.13 + version: 5.2.13(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1))) + '@sveltejs/kit': + specifier: ^2.25.2 + version: 2.25.2(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + '@sveltejs/vite-plugin-svelte': + specifier: ^6.1.0 + version: 6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + '@tailwindcss/typography': + specifier: ^0.5.16 + version: 0.5.16(tailwindcss@4.1.11) + '@tailwindcss/vite': + specifier: ^4.1.11 + version: 4.1.11(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + '@types/bcryptjs': + specifier: ^3.0.0 + version: 3.0.0 + '@types/cors': + specifier: ^2.8.19 + version: 2.8.19 + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 + '@types/jsonwebtoken': + specifier: ^9.0.10 + version: 9.0.10 + '@types/morgan': + specifier: ^1.9.10 + version: 1.9.10 + '@types/swagger-jsdoc': + specifier: ^6.0.4 + version: 6.0.4 + '@types/swagger-ui-express': + specifier: ^4.1.8 + version: 4.1.8 + drizzle-kit: + specifier: ^0.31.4 + version: 0.31.4 + drizzle-orm: + specifier: ^0.44.4 + version: 0.44.4(@prisma/client@6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3))(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(gel@2.1.1)(kysely@0.28.4)(postgres@3.4.7)(prisma@6.12.0(typescript@5.8.3)) + eslint: + specifier: ^9.31.0 + version: 9.31.0(jiti@2.4.2) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.31.0(jiti@2.4.2)) + eslint-plugin-svelte: + specifier: ^3.11.0 + version: 3.11.0(eslint@9.31.0(jiti@2.4.2))(svelte@5.36.14) + globals: + specifier: ^16.3.0 + version: 16.3.0 + nodemon: + specifier: ^3.1.10 + version: 3.1.10 + prettier: + specifier: ^3.6.2 + version: 3.6.2 + prettier-plugin-svelte: + specifier: ^3.4.0 + version: 3.4.0(prettier@3.6.2)(svelte@5.36.14) + prettier-plugin-tailwindcss: + specifier: ^0.6.14 + version: 0.6.14(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.36.14))(prettier@3.6.2) + prisma: + specifier: 6.12.0 + version: 6.12.0(typescript@5.8.3) + svelte: + specifier: ^5.36.14 + version: 5.36.14 + svelte-check: + specifier: ^4.3.0 + version: 4.3.0(picomatch@4.0.2)(svelte@5.36.14)(typescript@5.8.3) + svelte-dnd-action: + specifier: ^0.9.64 + version: 0.9.64(svelte@5.36.14) + tailwindcss: + specifier: ^4.1.11 + version: 4.1.11 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + vite: + specifier: ^7.0.5 + version: 7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@apidevtools/json-schema-ref-parser@9.1.2': + resolution: {integrity: sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==} + + '@apidevtools/openapi-schemas@2.1.0': + resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} + engines: {node: '>=10'} + + '@apidevtools/swagger-methods@3.0.2': + resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} + + '@apidevtools/swagger-parser@10.0.3': + resolution: {integrity: sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==} + peerDependencies: + openapi-types: '>=7' + + '@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.0': + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + 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.27.1': + resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} + 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.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + 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.2': + resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-display-name@7.28.0': + resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-development@7.27.1': + resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx@7.27.1': + resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-pure-annotations@7.27.1': + resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.0': + resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-react@7.27.1': + resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.27.1': + resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} + + '@better-auth/cli@1.3.4': + resolution: {integrity: sha512-nEVd/j2d2CQd+62+AxuD2v/NxFwaEYr0tkM0A8pVMv/b2TWHL09P7NzRVWCn7QlU1grAlPudMNolAzZ4IyLmNQ==} + hasBin: true + + '@better-auth/utils@0.2.5': + resolution: {integrity: sha512-uI2+/8h/zVsH8RrYdG8eUErbuGBk16rZKQfz8CjxQOyCE6v7BqFYEbFwvOkvl1KbUdxhqOnXp78+uE5h8qVEgQ==} + + '@better-fetch/fetch@1.1.18': + resolution: {integrity: sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==} + + '@chevrotain/cst-dts-gen@10.5.0': + resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} + + '@chevrotain/gast@10.5.0': + resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} + + '@chevrotain/types@10.5.0': + resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} + + '@chevrotain/utils@10.5.0': + resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} + + '@clack/core@0.4.2': + resolution: {integrity: sha512-NYQfcEy8MWIxrT5Fj8nIVchfRFA26yYKJcvBS7WlUIlw2OmQOY9DhGGXMovyI5J5PpxrCPGkgUi207EBrjpBvg==} + + '@clack/prompts@0.10.1': + resolution: {integrity: sha512-Q0T02vx8ZM9XSv9/Yde0jTmmBQufZhPJfYAg2XrrrxWWaZgq1rr8nU8Hv710BQ1dhoP8rtY7YUdpGej2Qza/cw==} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + + '@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.2': + resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.25.8': + resolution: {integrity: sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==} + 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.2': + resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.25.8': + resolution: {integrity: sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==} + 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.2': + resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.25.8': + resolution: {integrity: sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==} + 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.2': + resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.25.8': + resolution: {integrity: sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==} + 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.2': + resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.25.8': + resolution: {integrity: sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==} + 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.2': + resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.8': + resolution: {integrity: sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==} + 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.2': + resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.25.8': + resolution: {integrity: sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==} + 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.2': + resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.8': + resolution: {integrity: sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==} + 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.2': + resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.25.8': + resolution: {integrity: sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==} + 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.2': + resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.25.8': + resolution: {integrity: sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==} + 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.2': + resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.25.8': + resolution: {integrity: sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==} + 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.2': + resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.25.8': + resolution: {integrity: sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==} + 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.2': + resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.25.8': + resolution: {integrity: sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==} + 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.2': + resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.25.8': + resolution: {integrity: sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==} + 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.2': + resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.8': + resolution: {integrity: sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==} + 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.2': + resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.25.8': + resolution: {integrity: sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==} + 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.2': + resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.25.8': + resolution: {integrity: sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.2': + resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.25.8': + resolution: {integrity: sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==} + 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.2': + resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.8': + resolution: {integrity: sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.2': + resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.25.8': + resolution: {integrity: sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==} + 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.2': + resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.8': + resolution: {integrity: sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.8': + resolution: {integrity: sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==} + 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.2': + resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.25.8': + resolution: {integrity: sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==} + 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.2': + resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.25.8': + resolution: {integrity: sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==} + 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.2': + resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.25.8': + resolution: {integrity: sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==} + 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.2': + resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.25.8': + resolution: {integrity: sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==} + 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/compat@1.3.1': + resolution: {integrity: sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + + '@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.0': + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + 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.31.0': + resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} + 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.1': + resolution: {integrity: sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@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.2': + resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} + engines: {node: '>=18.18'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@jsdevtools/ono@7.1.3': + resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + + '@levischuck/tiny-cbor@0.2.11': + resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} + + '@lucide/svelte@0.525.0': + resolution: {integrity: sha512-dyUxkXzepagLUzL8jHQNdeH286nC66ClLACsg+Neu/bjkRJWPWMzkT+H0DKlE70QdkicGCfs1ZGmXCc351hmZA==} + peerDependencies: + svelte: ^5 + + '@mrleebo/prisma-ast@0.12.1': + resolution: {integrity: sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==} + engines: {node: '>=16'} + + '@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} + + '@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==} + + '@petamoriken/float16@3.9.2': + resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@prisma/client@5.22.0': + resolution: {integrity: sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==} + engines: {node: '>=16.13'} + peerDependencies: + prisma: '*' + peerDependenciesMeta: + prisma: + optional: true + + '@prisma/client@6.12.0': + resolution: {integrity: sha512-wn98bJ3Cj6edlF4jjpgXwbnQIo/fQLqqQHPk2POrZPxTlhY3+n90SSIF3LMRVa8VzRFC/Gec3YKJRxRu+AIGVA==} + engines: {node: '>=18.18'} + peerDependencies: + prisma: '*' + typescript: '>=5.1.0' + peerDependenciesMeta: + prisma: + optional: true + typescript: + optional: true + + '@prisma/config@6.12.0': + resolution: {integrity: sha512-HovZWzhWEMedHxmjefQBRZa40P81N7/+74khKFz9e1AFjakcIQdXgMWKgt20HaACzY+d1LRBC+L4tiz71t9fkg==} + + '@prisma/debug@5.22.0': + resolution: {integrity: sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==} + + '@prisma/debug@6.12.0': + resolution: {integrity: sha512-plbz6z72orcqr0eeio7zgUrZj5EudZUpAeWkFTA/DDdXEj28YHDXuiakvR6S7sD6tZi+jiwQEJAPeV6J6m/tEQ==} + + '@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': + resolution: {integrity: sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==} + + '@prisma/engines-version@6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc': + resolution: {integrity: sha512-70vhecxBJlRr06VfahDzk9ow4k1HIaSfVUT3X0/kZoHCMl9zbabut4gEXAyzJZxaCGi5igAA7SyyfBI//mmkbQ==} + + '@prisma/engines@5.22.0': + resolution: {integrity: sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==} + + '@prisma/engines@6.12.0': + resolution: {integrity: sha512-4BRZZUaAuB4p0XhTauxelvFs7IllhPmNLvmla0bO1nkECs8n/o1pUvAVbQ/VOrZR5DnF4HED0PrGai+rIOVePA==} + + '@prisma/fetch-engine@5.22.0': + resolution: {integrity: sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==} + + '@prisma/fetch-engine@6.12.0': + resolution: {integrity: sha512-EamoiwrK46rpWaEbLX9aqKDPOd8IyLnZAkiYXFNuq0YsU0Z8K09/rH8S7feOWAVJ3xzeSgcEJtBlVDrajM9Sag==} + + '@prisma/get-platform@5.22.0': + resolution: {integrity: sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==} + + '@prisma/get-platform@6.12.0': + resolution: {integrity: sha512-nRerTGhTlgyvcBlyWgt8OLNIV7QgJS2XYXMJD1hysorMCuLAjuDDuoxmVt7C2nLxbuxbWPp7OuFRHC23HqD9dA==} + + '@rollup/plugin-commonjs@28.0.3': + resolution: {integrity: sha512-pyltgilam1QPdn+Zd9gaCfOLcnjMEJ9gV+bTw6/r73INdvzf1ah9zLIJBm+kW7R6IUFIQ1YO+VqZtYxZNWFPEQ==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.1': + resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.4': + resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.39.0': + resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm-eabi@4.45.1': + resolution: {integrity: sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.39.0': + resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-android-arm64@4.45.1': + resolution: {integrity: sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.39.0': + resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-arm64@4.45.1': + resolution: {integrity: sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.39.0': + resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.45.1': + resolution: {integrity: sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.39.0': + resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-arm64@4.45.1': + resolution: {integrity: sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.39.0': + resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.45.1': + resolution: {integrity: sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + resolution: {integrity: sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.45.1': + resolution: {integrity: sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.39.0': + resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.45.1': + resolution: {integrity: sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.39.0': + resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.45.1': + resolution: {integrity: sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + resolution: {integrity: sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + resolution: {integrity: sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.45.1': + resolution: {integrity: sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.39.0': + resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.45.1': + resolution: {integrity: sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.39.0': + resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.45.1': + resolution: {integrity: sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.39.0': + resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.45.1': + resolution: {integrity: sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.39.0': + resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.45.1': + resolution: {integrity: sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.39.0': + resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-arm64-msvc@4.45.1': + resolution: {integrity: sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.39.0': + resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.45.1': + resolution: {integrity: sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.39.0': + resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.45.1': + resolution: {integrity: sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==} + cpu: [x64] + os: [win32] + + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + + '@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'} + + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-node@5.2.13': + resolution: {integrity: sha512-yS2TVFmIrxjGhYaV5/iIUrJ3mJl6zjaYn0lBD70vTLnYvJeqf3cjvLXeXCUCuYinhSBoyF4DpfGla49BnIy7sQ==} + peerDependencies: + '@sveltejs/kit': ^2.4.0 + + '@sveltejs/kit@2.25.2': + resolution: {integrity: sha512-aKfj82vqEINedoH9Pw4Ip16jj3w8soNq9F3nJqc56kxXW74TcEu/gdTAuLUI+gsl8i+KXfetRqg1F+gG/AZRVQ==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + + '@sveltejs/vite-plugin-svelte-inspector@5.0.0': + resolution: {integrity: sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.1.0': + resolution: {integrity: sha512-+U6lz1wvGEG/BvQyL4z/flyNdQ9xDNv5vrh+vWBWTHaebqT0c9RNggpZTo/XSPoHsSCWBlYaTlRX8pZ9GATXCw==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@tailwindcss/node@4.1.11': + resolution: {integrity: sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==} + + '@tailwindcss/oxide-android-arm64@4.1.11': + resolution: {integrity: sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.11': + resolution: {integrity: sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.11': + resolution: {integrity: sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.11': + resolution: {integrity: sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + resolution: {integrity: sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + resolution: {integrity: sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + resolution: {integrity: sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + resolution: {integrity: sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.11': + resolution: {integrity: sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.11': + resolution: {integrity: sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==} + 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.11': + resolution: {integrity: sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + resolution: {integrity: sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.11': + resolution: {integrity: sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==} + engines: {node: '>= 10'} + + '@tailwindcss/typography@0.5.16': + resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tailwindcss/vite@4.1.11': + resolution: {integrity: sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@types/bcryptjs@3.0.0': + resolution: {integrity: sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg==} + deprecated: This is a stub types definition. bcryptjs provides its own type definitions, so you do not need this installed. + + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@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/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + + '@types/mime@1.3.5': + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + + '@types/morgan@1.9.10': + resolution: {integrity: sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@24.1.0': + resolution: {integrity: sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==} + + '@types/prompts@2.4.9': + resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} + + '@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/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@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/swagger-jsdoc@6.0.4': + resolution: {integrity: sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==} + + '@types/swagger-ui-express@4.1.8': + resolution: {integrity: sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + asn1js@3.0.6: + resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==} + engines: {node: '>=12.0.0'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.11.0: + resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + + bcryptjs@3.0.2: + resolution: {integrity: sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==} + hasBin: true + + better-auth@1.3.4: + resolution: {integrity: sha512-JbZYam6Cs3Eu5CSoMK120zSshfaKvrCftSo/+v7524H1RvhryQ7UtMbzagBcXj0Digjj8hZtVkkR4tTZD/wK2g==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + better-call@1.0.13: + resolution: {integrity: sha512-auqdP9lnNOli9tKpZIiv0nEIwmmyaD/RotM3Mucql+Ef88etoZi/t7Ph5LjlmZt/hiSahhNTt6YVnx6++rziXA==} + + better-sqlite3@11.10.0: + resolution: {integrity: sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==} + + 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'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + c12@2.0.4: + resolution: {integrity: sha512-3DbbhnFt0fKJHxU4tEUPmD1ahWE4PWPMomqfYsTJdrhpmEnRKJi3qSC4rO5U6E6zN1+pjBY7+z8fUmNRMaVKLw==} + peerDependencies: + magicast: ^0.3.5 + peerDependenciesMeta: + magicast: + optional: true + + 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'} + + call-me-maybe@1.0.2: + resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001731: + resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.5.0: + resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chevrotain@10.5.0: + resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} + + 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@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + 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@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@6.2.0: + resolution: {integrity: sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==} + engines: {node: '>= 6'} + + commander@9.5.0: + resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} + engines: {node: ^12.20.0 || >=14} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + 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 + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + 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'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.0.4: + resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + engines: {node: '>=8'} + + devalue@5.1.1: + resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.2.1: + resolution: {integrity: sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==} + engines: {node: '>=12'} + + drizzle-kit@0.31.4: + resolution: {integrity: sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==} + hasBin: true + + drizzle-orm@0.33.0: + resolution: {integrity: sha512-SHy72R2Rdkz0LEq0PSG/IdvnT3nGiWuRk+2tXZQ90GVq/XQhpCzu/EFT3V2rox+w8MlkBQxifF8pCStNYnERfA==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=3' + '@electric-sql/pglite': '>=0.1.1' + '@libsql/client': '*' + '@neondatabase/serverless': '>=0.1' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/react': '>=18' + '@types/sql.js': '*' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=13.2.0' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + react: '>=18' + 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 + '@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/react': + 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 + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + react: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + drizzle-orm@0.44.4: + resolution: {integrity: sha512-ZyzKFpTC/Ut3fIqc2c0dPZ6nhchQXriTsqTNs4ayRgl6sZcFlMs9QZKPSHXK4bdOf41GHGWf+FrpcDDYwW+W6Q==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.198: + resolution: {integrity: sha512-G5COfnp3w+ydVu80yprgWSfmfQaYRh9DOxfhAxstLyetKaLyl55QrNjx8C38Pc/C+RaDmb1M0Lk8wPEMQ+bGgQ==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + es-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'} + + 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.2: + resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.25.8: + resolution: {integrity: sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==} + 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'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-svelte@3.11.0: + resolution: {integrity: sha512-KliWlkieHyEa65aQIkRwUFfHzT5Cn4u3BQQsu3KlkJOs7c1u7ryn84EWaOjEzilbKgttT4OfBURA8Uc4JBSQIw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.1 || ^9.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + eslint-scope@8.3.0: + resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.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.31.0: + resolution: {integrity: sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + espree@10.3.0: + resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + + 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'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + express-rate-limit@8.0.1: + resolution: {integrity: sha512-aZVCnybn7TVmxO4BtlmnvX+nuz8qHW124KKJ8dumsBsmv5ZLxE0pYu7S2nwyRBGHHCAzdmnGyrc5U/rksSPO7Q==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@11.3.1: + resolution: {integrity: sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==} + engines: {node: '>=14.14'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gaxios@7.1.1: + resolution: {integrity: sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==} + engines: {node: '>=18'} + + gcp-metadata@7.0.1: + resolution: {integrity: sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==} + engines: {node: '>=18'} + + gel@2.1.1: + resolution: {integrity: sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw==} + engines: {node: '>= 18.0.0'} + hasBin: true + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + giget@1.2.5: + resolution: {integrity: sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==} + hasBin: true + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + 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@7.1.6: + resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.3.0: + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + engines: {node: '>=18'} + + google-auth-library@10.2.0: + resolution: {integrity: sha512-gy/0hRx8+Ye0HlUm3GrfpR4lbmJQ6bJ7F44DmN7GtMxxzWSojLzx0Bhv/hj7Wlj7a2On0FcT8jrz8Y1c1nxCyg==} + engines: {node: '>=18'} + + google-logging-utils@1.1.1: + resolution: {integrity: sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==} + 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==} + + gtoken@8.0.0: + resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==} + engines: {node: '>=18'} + + 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'} + + helmet@8.1.0: + resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==} + engines: {node: '>=18.0.0'} + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + 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==} + + 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'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + 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-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + + 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-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonwebtoken@9.0.2: + resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + engines: {node: '>=12', npm: '>=6'} + + jwa@1.4.2: + resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + jws@4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + kysely@0.28.4: + resolution: {integrity: sha512-pfQj8/Bo3KSzC1HIZB5MeeYRWcDmx1ZZv8H25LsyeygqXE+gfsbUAgPT1GSYZFctB1cdOVlv+OifuCls2mQSnw==} + engines: {node: '>=20.0.0'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + libphonenumber-js@1.12.10: + resolution: {integrity: sha512-E91vHJD61jekHHR/RF/E83T/CMoaLXT7cwYA75T4gim4FZjnM6hbJjVIGg7chqlSqRsSvQ3izGmOjHy1SQzcGQ==} + + 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'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.castarray@4.4.0: + resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + + lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + marked@16.1.1: + resolution: {integrity: sha512-ij/2lXfCRT71L6u0M29tJPhP0bM5shLL3u5BePhFwPELj2blMJ6GDtD7PfJhRLhJ/c2UwrK17ySVcDzy2YHjHQ==} + engines: {node: '>= 20'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + + morgan@1.10.1: + resolution: {integrity: sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==} + engines: {node: '>= 0.8.0'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanostores@0.11.4: + resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} + engines: {node: ^18.0.0 || >=20.0.0} + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-abi@3.75.0: + resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + engines: {node: '>=10'} + + 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-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + 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'} + + nypm@0.5.4: + resolution: {integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: 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'} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.1.0: + resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + 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'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + 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.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-svelte@3.4.0: + resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + peerDependencies: + prettier: ^3.0.0 + svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 + + prettier-plugin-tailwindcss@0.6.14: + resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + prisma@5.22.0: + resolution: {integrity: sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==} + engines: {node: '>=16.13'} + hasBin: true + + prisma@6.12.0: + resolution: {integrity: sha512-pmV7NEqQej9WjizN6RSNIwf7Y+jeh9mY1JEX2WjGxJi4YZWexClhde1yz/FuvAM+cTwzchcMytu2m4I6wPkIzg==} + engines: {node: '>=18.18'} + hasBin: true + peerDependencies: + typescript: '>=5.1.0' + peerDependenciesMeta: + typescript: + optional: true + + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + 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'} + + 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.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + 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'} + + regexp-to-ast@0.5.0: + resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} + + 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 + + rollup@4.39.0: + resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rollup@4.45.1: + resolution: {integrity: sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rou3@0.5.1: + resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + schema-dts@1.1.5: + resolution: {integrity: sha512-RJr9EaCmsLzBX2NDiO5Z3ux2BVosNZN5jo0gWgsyKvxKIUL5R3swNvoorulAeL9kLB0iTSX7V6aokhla2m7xbg==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + 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'} + + 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'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + 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'} + + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + 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'} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + 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-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte-check@4.3.0: + resolution: {integrity: sha512-Iz8dFXzBNAM7XlEIsUjUGQhbEE+Pvv9odb9+0+ITTgFWZBGeJRRYqHUUglwe2EkLD5LIsQaAc4IUJyvtKuOO5w==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte-dnd-action@0.9.64: + resolution: {integrity: sha512-kbbnOTuVc+VINheraVyEQ7K11jXdQii6JNTGpsyIuwUqmda030eT3rPpqckD8UVh1DuyYH3xqyJDTWb8S610Jg==} + peerDependencies: + svelte: '>=3.23.0 || ^5.0.0-next.0' + + svelte-eslint-parser@1.3.0: + resolution: {integrity: sha512-VCgMHKV7UtOGcGLGNFSbmdm6kEKjtzo5nnpGU/mnx4OsFY6bZ7QwRF5DUx+Hokw5Lvdyo8dpk8B1m8mliomrNg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + svelte: + optional: true + + svelte-highlight@7.8.3: + resolution: {integrity: sha512-i4CE/6yda1fCh0ovUVATk1S1feu1y3+CV+l1brgtMPPRO9VTGq+hPpUjVEJWQkE7hPAgwgVpHccoa5M2gpKxYQ==} + + svelte-meta-tags@4.4.0: + resolution: {integrity: sha512-0g7sksBXdCGYcNM44uipqhVwDrtImB73iZdcpWHE0q0+k96Zg0WS6ySPAV+gX34DSqrkrvcqkG/tI2lwN1KbbA==} + peerDependencies: + svelte: ^5.0.0 + + svelte@5.36.14: + resolution: {integrity: sha512-okgNwfVa4FfDGOgd0ndooKjQz1LknUFDGfEJp6QNjYP6B4hDG0KktOP+Pta3ZtE8s+JELsYP+7nqMrJzQLkf5A==} + engines: {node: '>=18'} + + swagger-jsdoc@6.2.8: + resolution: {integrity: sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==} + engines: {node: '>=12.0.0'} + hasBin: true + + swagger-parser@10.0.3: + resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==} + engines: {node: '>=10'} + + swagger-ui-dist@5.27.0: + resolution: {integrity: sha512-tS6LRyBhY6yAqxrfsA9IYpGWPUJOri6sclySa7TdC7XQfGLvTwDY531KLgfQwHEtQsn+sT4JlUspbeQDBVGWig==} + + swagger-ui-express@5.0.1: + resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} + engines: {node: '>= v0.10.32'} + peerDependencies: + express: '>=4.0.0 || >=5.0.0-beta' + + tailwindcss@4.1.11: + resolution: {integrity: sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tar-fs@2.1.3: + resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + 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'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + 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==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@11.1.0: + resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + hasBin: true + + validator@13.15.15: + resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} + engines: {node: '>= 0.10'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite@7.0.5: + resolution: {integrity: sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + 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 + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.17.0: + resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==} + engines: {node: '>= 12.0.0'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.0.0-1: + resolution: {integrity: sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==} + engines: {node: '>= 6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-spinner@0.1.2: + resolution: {integrity: sha512-VfmLIh/ZSZOJnVRQZc/dvpPP90lWL4G0bmxQMP0+U/2vKBA8GSpcBuWv17y7F+CZItRuO97HN1wdbb4p10uhOg==} + engines: {node: '>=18.19'} + + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + + z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + + zod@4.0.8: + resolution: {integrity: sha512-+MSh9cZU9r3QKlHqrgHMTSr3QwMGv4PLfR0M4N/sYWV5/x67HgXEhIGObdBkpnX8G78pTgWnIrBL2lZcNJOtfg==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@apidevtools/json-schema-ref-parser@9.1.2': + dependencies: + '@jsdevtools/ono': 7.1.3 + '@types/json-schema': 7.0.15 + call-me-maybe: 1.0.2 + js-yaml: 4.1.0 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@10.0.3(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 9.1.2 + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@jsdevtools/ono': 7.1.3 + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + z-schema: 5.0.5 + + '@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.0': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helpers': 7.28.2 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.0 + '@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.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + 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.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@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.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.0 + 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.0 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.0 + 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.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.0 + '@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.2': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.2 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.28.0) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-react@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/preset-typescript@7.27.1(@babel/core@7.28.0)': + dependencies: + '@babel/core': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.0) + '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.0) + transitivePeerDependencies: + - supports-color + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@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/cli@1.3.4(kysely@0.28.4)(postgres@3.4.7)': + dependencies: + '@babel/core': 7.28.0 + '@babel/preset-react': 7.27.1(@babel/core@7.28.0) + '@babel/preset-typescript': 7.27.1(@babel/core@7.28.0) + '@clack/prompts': 0.10.1 + '@mrleebo/prisma-ast': 0.12.1 + '@prisma/client': 5.22.0(prisma@5.22.0) + '@types/better-sqlite3': 7.6.13 + '@types/prompts': 2.4.9 + better-auth: 1.3.4 + better-sqlite3: 11.10.0 + c12: 2.0.4 + chalk: 5.5.0 + commander: 12.1.0 + dotenv: 16.6.1 + drizzle-orm: 0.33.0(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.4)(postgres@3.4.7)(prisma@5.22.0) + fs-extra: 11.3.1 + get-tsconfig: 4.10.1 + prettier: 3.6.2 + prisma: 5.22.0 + prompts: 2.4.2 + semver: 7.7.1 + tinyexec: 0.3.2 + yocto-spinner: 0.1.2 + zod: 4.0.8 + transitivePeerDependencies: + - '@aws-sdk/client-rds-data' + - '@cloudflare/workers-types' + - '@electric-sql/pglite' + - '@libsql/client' + - '@neondatabase/serverless' + - '@op-engineering/op-sqlite' + - '@opentelemetry/api' + - '@planetscale/database' + - '@tidbcloud/serverless' + - '@types/pg' + - '@types/react' + - '@types/sql.js' + - '@vercel/postgres' + - '@xata.io/client' + - bun-types + - expo-sqlite + - knex + - kysely + - magicast + - mysql2 + - pg + - postgres + - react + - react-dom + - sql.js + - sqlite3 + - supports-color + + '@better-auth/utils@0.2.5': + dependencies: + typescript: 5.8.3 + uncrypto: 0.1.3 + + '@better-fetch/fetch@1.1.18': {} + + '@chevrotain/cst-dts-gen@10.5.0': + dependencies: + '@chevrotain/gast': 10.5.0 + '@chevrotain/types': 10.5.0 + lodash: 4.17.21 + + '@chevrotain/gast@10.5.0': + dependencies: + '@chevrotain/types': 10.5.0 + lodash: 4.17.21 + + '@chevrotain/types@10.5.0': {} + + '@chevrotain/utils@10.5.0': {} + + '@clack/core@0.4.2': + dependencies: + picocolors: 1.1.1 + sisteransi: 1.0.5 + + '@clack/prompts@0.10.1': + dependencies: + '@clack/core': 0.4.2 + picocolors: 1.1.1 + sisteransi: 1.0.5 + + '@colors/colors@1.6.0': {} + + '@dabh/diagnostics@2.0.3': + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + + '@drizzle-team/brocli@0.10.2': {} + + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.10.1 + + '@esbuild/aix-ppc64@0.25.2': + optional: true + + '@esbuild/aix-ppc64@0.25.8': + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm64@0.25.2': + optional: true + + '@esbuild/android-arm64@0.25.8': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-arm@0.25.2': + optional: true + + '@esbuild/android-arm@0.25.8': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/android-x64@0.25.2': + optional: true + + '@esbuild/android-x64@0.25.8': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.25.2': + optional: true + + '@esbuild/darwin-arm64@0.25.8': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.25.2': + optional: true + + '@esbuild/darwin-x64@0.25.8': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.25.2': + optional: true + + '@esbuild/freebsd-arm64@0.25.8': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.25.2': + optional: true + + '@esbuild/freebsd-x64@0.25.8': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.25.2': + optional: true + + '@esbuild/linux-arm64@0.25.8': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-arm@0.25.2': + optional: true + + '@esbuild/linux-arm@0.25.8': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.25.2': + optional: true + + '@esbuild/linux-ia32@0.25.8': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.25.2': + optional: true + + '@esbuild/linux-loong64@0.25.8': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.25.2': + optional: true + + '@esbuild/linux-mips64el@0.25.8': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.25.2': + optional: true + + '@esbuild/linux-ppc64@0.25.8': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.25.2': + optional: true + + '@esbuild/linux-riscv64@0.25.8': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.25.2': + optional: true + + '@esbuild/linux-s390x@0.25.8': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/linux-x64@0.25.2': + optional: true + + '@esbuild/linux-x64@0.25.8': + optional: true + + '@esbuild/netbsd-arm64@0.25.2': + optional: true + + '@esbuild/netbsd-arm64@0.25.8': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.25.2': + optional: true + + '@esbuild/netbsd-x64@0.25.8': + optional: true + + '@esbuild/openbsd-arm64@0.25.2': + optional: true + + '@esbuild/openbsd-arm64@0.25.8': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.25.2': + optional: true + + '@esbuild/openbsd-x64@0.25.8': + optional: true + + '@esbuild/openharmony-arm64@0.25.8': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.25.2': + optional: true + + '@esbuild/sunos-x64@0.25.8': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.25.2': + optional: true + + '@esbuild/win32-arm64@0.25.8': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.25.2': + optional: true + + '@esbuild/win32-ia32@0.25.8': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@esbuild/win32-x64@0.25.2': + optional: true + + '@esbuild/win32-x64@0.25.8': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.31.0(jiti@2.4.2))': + dependencies: + eslint: 9.31.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.3.1(eslint@9.31.0(jiti@2.4.2))': + optionalDependencies: + eslint: 9.31.0(jiti@2.4.2) + + '@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.0': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.1': + 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.31.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.1': + dependencies: + '@eslint/core': 0.14.0 + levn: 0.4.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.2': {} + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@jsdevtools/ono@7.1.3': {} + + '@levischuck/tiny-cbor@0.2.11': {} + + '@lucide/svelte@0.525.0(svelte@5.36.14)': + dependencies: + svelte: 5.36.14 + + '@mrleebo/prisma-ast@0.12.1': + dependencies: + chevrotain: 10.5.0 + lilconfig: 2.1.0 + + '@noble/ciphers@0.6.0': {} + + '@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 + + '@petamoriken/float16@3.9.2': + optional: true + + '@polka/url@1.0.0-next.29': {} + + '@prisma/client@5.22.0(prisma@5.22.0)': + optionalDependencies: + prisma: 5.22.0 + + '@prisma/client@6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3)': + optionalDependencies: + prisma: 6.12.0(typescript@5.8.3) + typescript: 5.8.3 + + '@prisma/config@6.12.0': + dependencies: + jiti: 2.4.2 + + '@prisma/debug@5.22.0': {} + + '@prisma/debug@6.12.0': {} + + '@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': {} + + '@prisma/engines-version@6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc': {} + + '@prisma/engines@5.22.0': + dependencies: + '@prisma/debug': 5.22.0 + '@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2 + '@prisma/fetch-engine': 5.22.0 + '@prisma/get-platform': 5.22.0 + + '@prisma/engines@6.12.0': + dependencies: + '@prisma/debug': 6.12.0 + '@prisma/engines-version': 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc + '@prisma/fetch-engine': 6.12.0 + '@prisma/get-platform': 6.12.0 + + '@prisma/fetch-engine@5.22.0': + dependencies: + '@prisma/debug': 5.22.0 + '@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2 + '@prisma/get-platform': 5.22.0 + + '@prisma/fetch-engine@6.12.0': + dependencies: + '@prisma/debug': 6.12.0 + '@prisma/engines-version': 6.12.0-15.8047c96bbd92db98a2abc7c9323ce77c02c89dbc + '@prisma/get-platform': 6.12.0 + + '@prisma/get-platform@5.22.0': + dependencies: + '@prisma/debug': 5.22.0 + + '@prisma/get-platform@6.12.0': + dependencies: + '@prisma/debug': 6.12.0 + + '@rollup/plugin-commonjs@28.0.3(rollup@4.39.0)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.6(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.17 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.39.0 + + '@rollup/plugin-json@6.1.0(rollup@4.39.0)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + optionalDependencies: + rollup: 4.39.0 + + '@rollup/plugin-node-resolve@16.0.1(rollup@4.39.0)': + dependencies: + '@rollup/pluginutils': 5.1.4(rollup@4.39.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.10 + optionalDependencies: + rollup: 4.39.0 + + '@rollup/pluginutils@5.1.4(rollup@4.39.0)': + dependencies: + '@types/estree': 1.0.7 + estree-walker: 2.0.2 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.39.0 + + '@rollup/rollup-android-arm-eabi@4.39.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.45.1': + optional: true + + '@rollup/rollup-android-arm64@4.39.0': + optional: true + + '@rollup/rollup-android-arm64@4.45.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.39.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.45.1': + optional: true + + '@rollup/rollup-darwin-x64@4.39.0': + optional: true + + '@rollup/rollup-darwin-x64@4.45.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.39.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.45.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.39.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.45.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.45.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.45.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.39.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.45.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.39.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.45.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.39.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.45.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.39.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.45.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.39.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.45.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.39.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.45.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.39.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.45.1': + optional: true + + '@scarf/scarf@1.4.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 + + '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.1)': + dependencies: + acorn: 8.14.1 + + '@sveltejs/adapter-node@5.2.13(@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))': + dependencies: + '@rollup/plugin-commonjs': 28.0.3(rollup@4.39.0) + '@rollup/plugin-json': 6.1.0(rollup@4.39.0) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.39.0) + '@sveltejs/kit': 2.25.2(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + rollup: 4.39.0 + + '@sveltejs/kit@2.25.2(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1))': + dependencies: + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1) + '@sveltejs/vite-plugin-svelte': 6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + '@types/cookie': 0.6.0 + acorn: 8.14.1 + cookie: 0.6.0 + devalue: 5.1.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.17 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.1 + svelte: 5.36.14 + vite: 7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1) + + '@sveltejs/vite-plugin-svelte-inspector@5.0.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + debug: 4.4.1(supports-color@5.5.0) + svelte: 5.36.14 + vite: 7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.0(@sveltejs/vite-plugin-svelte@6.1.0(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)))(svelte@5.36.14)(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + debug: 4.4.1(supports-color@5.5.0) + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.36.14 + vite: 7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1) + vitefu: 1.1.1(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)) + transitivePeerDependencies: + - supports-color + + '@tailwindcss/node@4.1.11': + dependencies: + '@ampproject/remapping': 2.3.0 + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + lightningcss: 1.30.1 + magic-string: 0.30.17 + source-map-js: 1.2.1 + tailwindcss: 4.1.11 + + '@tailwindcss/oxide-android-arm64@4.1.11': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.11': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.11': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.11': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.11': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.11': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.11': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.11': + optional: true + + '@tailwindcss/oxide@4.1.11': + dependencies: + detect-libc: 2.0.4 + tar: 7.4.3 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.11 + '@tailwindcss/oxide-darwin-arm64': 4.1.11 + '@tailwindcss/oxide-darwin-x64': 4.1.11 + '@tailwindcss/oxide-freebsd-x64': 4.1.11 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.11 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.11 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.11 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.11 + '@tailwindcss/oxide-linux-x64-musl': 4.1.11 + '@tailwindcss/oxide-wasm32-wasi': 4.1.11 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.11 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.11 + + '@tailwindcss/typography@0.5.16(tailwindcss@4.1.11)': + dependencies: + lodash.castarray: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + postcss-selector-parser: 6.0.10 + tailwindcss: 4.1.11 + + '@tailwindcss/vite@4.1.11(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1))': + dependencies: + '@tailwindcss/node': 4.1.11 + '@tailwindcss/oxide': 4.1.11 + tailwindcss: 4.1.11 + vite: 7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1) + + '@types/bcryptjs@3.0.0': + dependencies: + bcryptjs: 3.0.2 + + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 24.1.0 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 24.1.0 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 24.1.0 + + '@types/cookie@0.6.0': {} + + '@types/cors@2.8.19': + dependencies: + '@types/node': 24.1.0 + + '@types/estree@1.0.7': {} + + '@types/estree@1.0.8': {} + + '@types/express-serve-static-core@5.0.7': + dependencies: + '@types/node': 24.1.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/http-errors@2.0.5': {} + + '@types/json-schema@7.0.15': {} + + '@types/jsonwebtoken@9.0.10': + dependencies: + '@types/ms': 2.1.0 + '@types/node': 24.1.0 + + '@types/mime@1.3.5': {} + + '@types/morgan@1.9.10': + dependencies: + '@types/node': 24.1.0 + + '@types/ms@2.1.0': {} + + '@types/node@24.1.0': + dependencies: + undici-types: 7.8.0 + + '@types/prompts@2.4.9': + dependencies: + '@types/node': 24.1.0 + kleur: 3.0.3 + + '@types/qs@6.14.0': {} + + '@types/range-parser@1.2.7': {} + + '@types/resolve@1.20.2': {} + + '@types/send@0.17.5': + dependencies: + '@types/mime': 1.3.5 + '@types/node': 24.1.0 + + '@types/serve-static@1.15.8': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 24.1.0 + '@types/send': 0.17.5 + + '@types/swagger-jsdoc@6.0.4': {} + + '@types/swagger-ui-express@4.1.8': + dependencies: + '@types/express': 5.0.3 + '@types/serve-static': 1.15.8 + + '@types/triple-beam@1.3.5': {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.14.1: {} + + acorn@8.15.0: {} + + agent-base@7.1.4: {} + + 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-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + asn1js@3.0.6: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.3 + tslib: 2.8.1 + + async@3.2.6: {} + + asynckit@0.4.0: {} + + axios@1.11.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + + bcryptjs@3.0.2: {} + + better-auth@1.3.4: + dependencies: + '@better-auth/utils': 0.2.5 + '@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.13 + defu: 6.1.4 + jose: 5.10.0 + kysely: 0.28.4 + nanostores: 0.11.4 + zod: 4.0.8 + + better-call@1.0.13: + dependencies: + '@better-fetch/fetch': 1.1.18 + rou3: 0.5.1 + set-cookie-parser: 2.7.1 + uncrypto: 0.1.3 + + better-sqlite3@11.10.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bignumber.js@9.3.1: {} + + binary-extensions@2.3.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1(supports-color@5.5.0) + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.25.1: + dependencies: + caniuse-lite: 1.0.30001731 + electron-to-chromium: 1.5.198 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.1) + + buffer-equal-constant-time@1.0.1: {} + + buffer-from@1.1.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bytes@3.1.2: {} + + c12@2.0.4: + dependencies: + chokidar: 4.0.3 + confbox: 0.1.8 + defu: 6.1.4 + dotenv: 16.6.1 + giget: 1.2.5 + jiti: 2.4.2 + mlly: 1.7.4 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 1.0.0 + pkg-types: 1.3.1 + rc9: 2.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 + + call-me-maybe@1.0.2: {} + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001731: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.5.0: {} + + chevrotain@10.5.0: + dependencies: + '@chevrotain/cst-dts-gen': 10.5.0 + '@chevrotain/gast': 10.5.0 + '@chevrotain/types': 10.5.0 + '@chevrotain/utils': 10.5.0 + lodash: 4.17.21 + regexp-to-ast: 0.5.0 + + chokidar@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@1.1.4: {} + + chownr@2.0.0: {} + + chownr@3.0.0: {} + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + clsx@2.1.1: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + + colorspace@1.1.4: + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@12.1.0: {} + + commander@6.2.0: {} + + commander@9.5.0: + optional: true + + commondir@1.0.1: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + convert-source-map@2.0.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.6.0: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + data-uri-to-buffer@4.0.1: {} + + date-fns@4.1.0: {} + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.4.1(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + defu@6.1.4: {} + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + destr@2.0.5: {} + + detect-libc@2.0.4: {} + + devalue@5.1.1: {} + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dotenv@16.6.1: {} + + dotenv@17.2.1: {} + + drizzle-kit@0.31.4: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.25.8 + esbuild-register: 3.6.0(esbuild@0.25.8) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.33.0(@prisma/client@5.22.0(prisma@5.22.0))(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(kysely@0.28.4)(postgres@3.4.7)(prisma@5.22.0): + optionalDependencies: + '@prisma/client': 5.22.0(prisma@5.22.0) + '@types/better-sqlite3': 7.6.13 + better-sqlite3: 11.10.0 + kysely: 0.28.4 + postgres: 3.4.7 + prisma: 5.22.0 + + drizzle-orm@0.44.4(@prisma/client@6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3))(@types/better-sqlite3@7.6.13)(better-sqlite3@11.10.0)(gel@2.1.1)(kysely@0.28.4)(postgres@3.4.7)(prisma@6.12.0(typescript@5.8.3)): + optionalDependencies: + '@prisma/client': 6.12.0(prisma@6.12.0(typescript@5.8.3))(typescript@5.8.3) + '@types/better-sqlite3': 7.6.13 + better-sqlite3: 11.10.0 + gel: 2.1.1 + kysely: 0.28.4 + postgres: 3.4.7 + prisma: 6.12.0(typescript@5.8.3) + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.198: {} + + enabled@2.0.0: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + env-paths@3.0.0: + optional: true + + 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 + + esbuild-register@3.6.0(esbuild@0.25.8): + dependencies: + debug: 4.4.1(supports-color@5.5.0) + esbuild: 0.25.8 + 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.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.2 + '@esbuild/android-arm': 0.25.2 + '@esbuild/android-arm64': 0.25.2 + '@esbuild/android-x64': 0.25.2 + '@esbuild/darwin-arm64': 0.25.2 + '@esbuild/darwin-x64': 0.25.2 + '@esbuild/freebsd-arm64': 0.25.2 + '@esbuild/freebsd-x64': 0.25.2 + '@esbuild/linux-arm': 0.25.2 + '@esbuild/linux-arm64': 0.25.2 + '@esbuild/linux-ia32': 0.25.2 + '@esbuild/linux-loong64': 0.25.2 + '@esbuild/linux-mips64el': 0.25.2 + '@esbuild/linux-ppc64': 0.25.2 + '@esbuild/linux-riscv64': 0.25.2 + '@esbuild/linux-s390x': 0.25.2 + '@esbuild/linux-x64': 0.25.2 + '@esbuild/netbsd-arm64': 0.25.2 + '@esbuild/netbsd-x64': 0.25.2 + '@esbuild/openbsd-arm64': 0.25.2 + '@esbuild/openbsd-x64': 0.25.2 + '@esbuild/sunos-x64': 0.25.2 + '@esbuild/win32-arm64': 0.25.2 + '@esbuild/win32-ia32': 0.25.2 + '@esbuild/win32-x64': 0.25.2 + + esbuild@0.25.8: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.8 + '@esbuild/android-arm': 0.25.8 + '@esbuild/android-arm64': 0.25.8 + '@esbuild/android-x64': 0.25.8 + '@esbuild/darwin-arm64': 0.25.8 + '@esbuild/darwin-x64': 0.25.8 + '@esbuild/freebsd-arm64': 0.25.8 + '@esbuild/freebsd-x64': 0.25.8 + '@esbuild/linux-arm': 0.25.8 + '@esbuild/linux-arm64': 0.25.8 + '@esbuild/linux-ia32': 0.25.8 + '@esbuild/linux-loong64': 0.25.8 + '@esbuild/linux-mips64el': 0.25.8 + '@esbuild/linux-ppc64': 0.25.8 + '@esbuild/linux-riscv64': 0.25.8 + '@esbuild/linux-s390x': 0.25.8 + '@esbuild/linux-x64': 0.25.8 + '@esbuild/netbsd-arm64': 0.25.8 + '@esbuild/netbsd-x64': 0.25.8 + '@esbuild/openbsd-arm64': 0.25.8 + '@esbuild/openbsd-x64': 0.25.8 + '@esbuild/openharmony-arm64': 0.25.8 + '@esbuild/sunos-x64': 0.25.8 + '@esbuild/win32-arm64': 0.25.8 + '@esbuild/win32-ia32': 0.25.8 + '@esbuild/win32-x64': 0.25.8 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.4.2)): + dependencies: + eslint: 9.31.0(jiti@2.4.2) + + eslint-plugin-svelte@3.11.0(eslint@9.31.0(jiti@2.4.2))(svelte@5.36.14): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.4.2)) + '@jridgewell/sourcemap-codec': 1.5.0 + eslint: 9.31.0(jiti@2.4.2) + esutils: 2.0.3 + globals: 16.3.0 + known-css-properties: 0.37.0 + postcss: 8.5.3 + postcss-load-config: 3.1.4(postcss@8.5.3) + postcss-safe-parser: 7.0.1(postcss@8.5.3) + semver: 7.7.1 + svelte-eslint-parser: 1.3.0(svelte@5.36.14) + optionalDependencies: + svelte: 5.36.14 + transitivePeerDependencies: + - ts-node + + eslint-scope@8.3.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.0: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.31.0(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.31.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.0 + '@eslint/core': 0.15.1 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.31.0 + '@eslint/plugin-kit': 0.3.1 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.2 + '@types/estree': 1.0.7 + '@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.4.2 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + espree@10.3.0: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 4.2.0 + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@2.1.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} + + expand-template@2.0.3: {} + + express-rate-limit@8.0.1(express@5.1.0): + dependencies: + express: 5.1.0 + ip-address: 10.0.1 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1(supports-color@5.5.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fecha@4.2.3: {} + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-uri-to-path@1.0.0: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.0: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@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 + + flatted@3.3.3: {} + + fn.name@1.1.0: {} + + follow-redirects@1.15.9: {} + + 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-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-constants@1.0.0: {} + + fs-extra@11.3.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-minipass@2.1.0: + dependencies: + minipass: 3.3.6 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gaxios@7.1.1: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + transitivePeerDependencies: + - supports-color + + gcp-metadata@7.0.1: + dependencies: + gaxios: 7.1.1 + google-logging-utils: 1.1.1 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + + gel@2.1.1: + dependencies: + '@petamoriken/float16': 3.9.2 + debug: 4.4.1(supports-color@5.5.0) + env-paths: 3.0.0 + semver: 7.7.1 + shell-quote: 1.8.3 + which: 4.0.0 + transitivePeerDependencies: + - supports-color + optional: true + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.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-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + giget@1.2.5: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + node-fetch-native: 1.6.7 + nypm: 0.5.4 + pathe: 2.0.3 + tar: 6.2.1 + + github-from-package@0.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.1.6: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + globals@16.3.0: {} + + google-auth-library@10.2.0: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.1 + gcp-metadata: 7.0.1 + google-logging-utils: 1.1.1 + gtoken: 8.0.0 + jws: 4.0.0 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.1: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + gtoken@8.0.0: + dependencies: + gaxios: 7.1.1 + jws: 4.0.0 + transitivePeerDependencies: + - supports-color + + 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 + + helmet@8.1.0: {} + + highlight.js@11.11.1: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1(supports-color@5.5.0) + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore-by-default@1.0.1: {} + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ip-address@10.0.1: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-module@1.0.0: {} + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.7 + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.7 + + is-stream@2.0.1: {} + + isexe@2.0.0: {} + + isexe@3.1.1: + optional: true + + jiti@2.4.2: {} + + jose@5.10.0: {} + + 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-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonwebtoken@9.0.2: + dependencies: + jws: 3.2.2 + lodash.includes: 4.3.0 + lodash.isboolean: 3.0.3 + lodash.isinteger: 4.0.4 + lodash.isnumber: 3.0.3 + lodash.isplainobject: 4.0.6 + lodash.isstring: 4.0.1 + lodash.once: 4.1.1 + ms: 2.1.3 + semver: 7.7.1 + + jwa@1.4.2: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@3.2.2: + dependencies: + jwa: 1.4.2 + safe-buffer: 5.2.1 + + jws@4.0.0: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@3.0.3: {} + + kleur@4.1.5: {} + + known-css-properties@0.37.0: {} + + kuler@2.0.0: {} + + kysely@0.28.4: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + libphonenumber-js@1.12.10: {} + + 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 + + lilconfig@2.1.0: {} + + locate-character@3.0.0: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.castarray@4.4.0: {} + + lodash.get@4.4.2: {} + + lodash.includes@4.3.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isequal@4.5.0: {} + + lodash.isinteger@4.0.4: {} + + lodash.isnumber@3.0.3: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isstring@4.0.1: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.once@4.1.1: {} + + lodash@4.17.21: {} + + 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 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + marked@16.1.1: {} + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mimic-response@3.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimist@1.2.8: {} + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@5.0.0: {} + + minipass@7.1.2: {} + + minizlib@2.1.2: + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp-classic@0.5.3: {} + + mkdirp@1.0.4: {} + + mkdirp@3.0.1: {} + + mlly@1.7.4: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + morgan@1.10.1: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.1.0 + transitivePeerDependencies: + - supports-color + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + nanostores@0.11.4: {} + + napi-build-utils@2.0.0: {} + + natural-compare@1.4.0: {} + + negotiator@1.0.0: {} + + node-abi@3.75.0: + dependencies: + semver: 7.7.1 + + node-domexception@1.0.0: {} + + node-fetch-native@1.6.7: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + 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.1 + simple-update-notifier: 2.0.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + normalize-path@3.0.0: {} + + nypm@0.5.4: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 1.3.1 + tinyexec: 0.3.2 + ufo: 1.6.1 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + ohash@2.0.11: {} + + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.1.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + + openapi-types@12.1.3: {} + + 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 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@8.2.0: {} + + pathe@2.0.3: {} + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.4 + pathe: 2.0.3 + + postcss-load-config@3.1.4(postcss@8.5.3): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.3 + + postcss-safe-parser@7.0.1(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-scss@4.0.9(postcss@8.5.3): + dependencies: + postcss: 8.5.3 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss@8.5.3: + 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@3.4.7: {} + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.4 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.75.0 + pump: 3.0.3 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.3 + tunnel-agent: 0.6.0 + + prelude-ls@1.2.1: {} + + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.36.14): + dependencies: + prettier: 3.6.2 + svelte: 5.36.14 + + prettier-plugin-tailwindcss@0.6.14(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.36.14))(prettier@3.6.2): + dependencies: + prettier: 3.6.2 + optionalDependencies: + prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.36.14) + + prettier@3.6.2: {} + + prisma@5.22.0: + dependencies: + '@prisma/engines': 5.22.0 + optionalDependencies: + fsevents: 2.3.3 + + prisma@6.12.0(typescript@5.8.3): + dependencies: + '@prisma/config': 6.12.0 + '@prisma/engines': 6.12.0 + optionalDependencies: + typescript: 5.8.3 + + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + 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: {} + + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.3: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.5 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + regexp-to-ast@0.5.0: {} + + 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 + + rollup@4.39.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.39.0 + '@rollup/rollup-android-arm64': 4.39.0 + '@rollup/rollup-darwin-arm64': 4.39.0 + '@rollup/rollup-darwin-x64': 4.39.0 + '@rollup/rollup-freebsd-arm64': 4.39.0 + '@rollup/rollup-freebsd-x64': 4.39.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.39.0 + '@rollup/rollup-linux-arm-musleabihf': 4.39.0 + '@rollup/rollup-linux-arm64-gnu': 4.39.0 + '@rollup/rollup-linux-arm64-musl': 4.39.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.39.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-musl': 4.39.0 + '@rollup/rollup-linux-s390x-gnu': 4.39.0 + '@rollup/rollup-linux-x64-gnu': 4.39.0 + '@rollup/rollup-linux-x64-musl': 4.39.0 + '@rollup/rollup-win32-arm64-msvc': 4.39.0 + '@rollup/rollup-win32-ia32-msvc': 4.39.0 + '@rollup/rollup-win32-x64-msvc': 4.39.0 + fsevents: 2.3.3 + + rollup@4.45.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.45.1 + '@rollup/rollup-android-arm64': 4.45.1 + '@rollup/rollup-darwin-arm64': 4.45.1 + '@rollup/rollup-darwin-x64': 4.45.1 + '@rollup/rollup-freebsd-arm64': 4.45.1 + '@rollup/rollup-freebsd-x64': 4.45.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.1 + '@rollup/rollup-linux-arm-musleabihf': 4.45.1 + '@rollup/rollup-linux-arm64-gnu': 4.45.1 + '@rollup/rollup-linux-arm64-musl': 4.45.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.1 + '@rollup/rollup-linux-riscv64-gnu': 4.45.1 + '@rollup/rollup-linux-riscv64-musl': 4.45.1 + '@rollup/rollup-linux-s390x-gnu': 4.45.1 + '@rollup/rollup-linux-x64-gnu': 4.45.1 + '@rollup/rollup-linux-x64-musl': 4.45.1 + '@rollup/rollup-win32-arm64-msvc': 4.45.1 + '@rollup/rollup-win32-ia32-msvc': 4.45.1 + '@rollup/rollup-win32-x64-msvc': 4.45.1 + fsevents: 2.3.3 + + rou3@0.5.1: {} + + router@2.2.0: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-stable-stringify@2.5.0: {} + + safer-buffer@2.1.2: {} + + schema-dts@1.1.5: {} + + semver@6.3.1: {} + + semver@7.7.1: {} + + send@1.2.0: + dependencies: + debug: 4.4.1(supports-color@5.5.0) + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-cookie-parser@2.7.1: {} + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: + optional: true + + 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 + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + simple-update-notifier@2.0.0: + dependencies: + semver: 7.7.1 + + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + sisteransi@1.0.5: {} + + 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: {} + + stack-trace@0.0.10: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte-check@4.3.0(picomatch@4.0.2)(svelte@5.36.14)(typescript@5.8.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.3 + fdir: 6.4.6(picomatch@4.0.2) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.36.14 + typescript: 5.8.3 + transitivePeerDependencies: + - picomatch + + svelte-dnd-action@0.9.64(svelte@5.36.14): + dependencies: + svelte: 5.36.14 + + svelte-eslint-parser@1.3.0(svelte@5.36.14): + dependencies: + eslint-scope: 8.3.0 + eslint-visitor-keys: 4.2.0 + espree: 10.3.0 + postcss: 8.5.3 + postcss-scss: 4.0.9(postcss@8.5.3) + postcss-selector-parser: 7.1.0 + optionalDependencies: + svelte: 5.36.14 + + svelte-highlight@7.8.3: + dependencies: + highlight.js: 11.11.1 + + svelte-meta-tags@4.4.0(svelte@5.36.14): + dependencies: + schema-dts: 1.1.5 + svelte: 5.36.14 + + svelte@5.36.14: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.14.1) + '@types/estree': 1.0.7 + acorn: 8.14.1 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 2.1.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 + + swagger-jsdoc@6.2.8(openapi-types@12.1.3): + dependencies: + commander: 6.2.0 + doctrine: 3.0.0 + glob: 7.1.6 + lodash.mergewith: 4.6.2 + swagger-parser: 10.0.3(openapi-types@12.1.3) + yaml: 2.0.0-1 + transitivePeerDependencies: + - openapi-types + + swagger-parser@10.0.3(openapi-types@12.1.3): + dependencies: + '@apidevtools/swagger-parser': 10.0.3(openapi-types@12.1.3) + transitivePeerDependencies: + - openapi-types + + swagger-ui-dist@5.27.0: + dependencies: + '@scarf/scarf': 1.4.0 + + swagger-ui-express@5.0.1(express@5.1.0): + dependencies: + express: 5.1.0 + swagger-ui-dist: 5.27.0 + + tailwindcss@4.1.11: {} + + tapable@2.2.1: {} + + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.3 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar@6.2.1: + dependencies: + chownr: 2.0.0 + fs-minipass: 2.1.0 + minipass: 5.0.0 + minizlib: 2.1.2 + mkdirp: 1.0.4 + yallist: 4.0.0 + + 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-hex@1.0.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + totalist@3.0.1: {} + + touch@3.1.1: {} + + triple-beam@1.4.1: {} + + tslib@2.8.1: {} + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typescript@5.8.3: {} + + ufo@1.6.1: {} + + uncrypto@0.1.3: {} + + undefsafe@2.0.5: {} + + undici-types@7.8.0: {} + + universalify@2.0.1: {} + + unpipe@1.0.0: {} + + update-browserslist-db@1.1.3(browserslist@4.25.1): + dependencies: + browserslist: 4.25.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + uuid@11.1.0: {} + + validator@13.15.15: {} + + vary@1.1.2: {} + + vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1): + dependencies: + esbuild: 0.25.2 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.45.1 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.1.0 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.30.1 + + vitefu@1.1.1(vite@7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1)): + optionalDependencies: + vite: 7.0.5(@types/node@24.1.0)(jiti@2.4.2)(lightningcss@1.30.1) + + web-streams-polyfill@3.3.3: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + which@4.0.0: + dependencies: + isexe: 3.1.1 + optional: true + + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.17.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml@1.10.2: {} + + yaml@2.0.0-1: {} + + yocto-queue@0.1.0: {} + + yocto-spinner@0.1.2: + dependencies: + yoctocolors: 2.1.1 + + yoctocolors@2.1.1: {} + + z-schema@5.0.5: + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.15.15 + optionalDependencies: + commander: 9.5.0 + + zimmerframe@1.1.2: {} + + zod@4.0.8: {} diff --git a/apps/web/server.js b/apps/web/server.js new file mode 100644 index 0000000..ba39648 --- /dev/null +++ b/apps/web/server.js @@ -0,0 +1,102 @@ +import express from 'express'; +import cors from 'cors'; +import helmet from 'helmet'; +import rateLimit from 'express-rate-limit'; +import swaggerJsdoc from 'swagger-jsdoc'; +import swaggerUi from 'swagger-ui-express'; +import dotenv from 'dotenv'; +import { createLogger } from '../api/config/logger.js'; +import { requestLogger } from '../api/middleware/requestLogger.js'; +import { errorHandler } from '../api/middleware/errorHandler.js'; +import authRoutes from '../api/routes/auth.js'; +import dashboardRoutes from '../api/routes/dashboard.js'; +import leadRoutes from '../api/routes/leads.js'; +import accountRoutes from '../api/routes/accounts.js'; +import contactRoutes from '../api/routes/contacts.js'; +import opportunityRoutes from '../api/routes/opportunities.js'; +import taskRoutes from '../api/routes/tasks.js'; +import organizationRoutes from '../api/routes/organizations.js'; + +dotenv.config(); + +const app = express(); +const logger = createLogger(); +const PORT = process.env.PORT || 3001; + +// Trust proxy setting for rate limiting +app.set('trust proxy', 1); + +const rateLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, + message: 'Too many requests from this IP, please try again later.', +}); + +const swaggerOptions = { + definition: { + openapi: '3.0.0', + info: { + title: 'BottleCRM API', + version: '1.0.0', + description: 'Multi-tenant CRM API with JWT authentication', + }, + servers: [ + { + url: `http://localhost:${PORT}`, + description: 'Development server', + }, + ], + components: { + securitySchemes: { + bearerAuth: { + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + }, + }, + }, + security: [ + { + bearerAuth: [], + }, + ], + }, + apis: ['./api/routes/*.js'], +}; + +const specs = swaggerJsdoc(swaggerOptions); + +app.use(helmet()); +app.use(cors({ + origin: process.env.FRONTEND_URL || 'http://localhost:5173', + credentials: true, +})); +app.use(rateLimiter); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true })); + +app.use(requestLogger); + +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs)); + +app.use('/auth', authRoutes); +app.use('/dashboard', dashboardRoutes); +app.use('/leads', leadRoutes); +app.use('/accounts', accountRoutes); +app.use('/contacts', contactRoutes); +app.use('/opportunities', opportunityRoutes); +app.use('/tasks', taskRoutes); +app.use('/organizations', organizationRoutes); + +app.get('/health', (req, res) => { + res.json({ status: 'OK', timestamp: new Date().toISOString() }); +}); + +app.use(errorHandler); + +app.listen(PORT, () => { + logger.info(`BottleCRM API server running on port ${PORT}`); + logger.info(`Swagger documentation available at http://localhost:${PORT}/api-docs`); +}); + +export default app; \ No newline at end of file diff --git a/apps/web/src/app.css b/apps/web/src/app.css new file mode 100644 index 0000000..a596c08 --- /dev/null +++ b/apps/web/src/app.css @@ -0,0 +1,3 @@ +@import 'tailwindcss'; +@plugin '@tailwindcss/typography'; + diff --git a/apps/web/src/app.d.ts b/apps/web/src/app.d.ts new file mode 100644 index 0000000..7d8cb20 --- /dev/null +++ b/apps/web/src/app.d.ts @@ -0,0 +1,24 @@ +import type { AuthType } from './lib/auth'; +import type { DrizzleClient } from '@opensource-startup-crm/database'; + +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + interface Locals { + db: DrizzleClient; + auth: AuthType; + user: AuthType["$Infer"]["Session"]["user"] | null; + org?: { id: string; name: string } | null; + session: AuthType["$Infer"]["Session"]["session"] | null; + } + // interface PageData {} + // interface PageState {} + interface Platform { + env: Env; + } + } +} + +export { }; diff --git a/apps/web/src/app.html b/apps/web/src/app.html new file mode 100644 index 0000000..c09a9ae --- /dev/null +++ b/apps/web/src/app.html @@ -0,0 +1,21 @@ + + + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/apps/web/src/hooks.server.ts b/apps/web/src/hooks.server.ts new file mode 100644 index 0000000..2376683 --- /dev/null +++ b/apps/web/src/hooks.server.ts @@ -0,0 +1,55 @@ +import { error, redirect, type Handle } from '@sveltejs/kit'; +import { building } from '$app/environment'; +import { svelteKitHandler } from 'better-auth/svelte-kit'; +import { createAuth } from '$lib/auth'; +import { getDb, schema } from '@opensource-startup-crm/database'; +import { eq } from 'drizzle-orm'; +import { sequence } from '@sveltejs/kit/hooks'; + +const handleAuth: Handle = async ({ event, resolve }) => { + const env = event.platform?.env; + if (!env) throw error(500, 'Platform env is not defined'); + const db = getDb(env); + event.locals.db = db; + event.locals.auth = createAuth(env, db); + + const auth = event.locals.auth as ReturnType; + const sessionData: { user: App.Locals['user']; session: App.Locals['session'] } | null = + await auth.api.getSession({ headers: event.request.headers }); + event.locals.user = sessionData?.user ?? null; + event.locals.session = sessionData?.session ?? null; + return resolve(event); +}; + +// Resolve org from activeOrganizationId and apply route guards +const handleOrgAndGuards: Handle = async ({ event, resolve }) => { + const db = event.locals.db as ReturnType; + if (event.locals.user && event.locals.session?.activeOrganizationId) { + const activeOrgId = event.locals.session.activeOrganizationId as string; + const [org] = await db + .select({ id: schema.organization.id, name: schema.organization.name }) + .from(schema.organization) + .where(eq(schema.organization.id, activeOrgId)); + event.locals.org = org ?? null; + } else { + event.locals.org = null; + } + if (event.url.pathname.startsWith('/app')) { + if (!event.locals.user) throw redirect(307, '/login'); + if (!event.locals.org) throw redirect(307, '/org'); + } else if (event.url.pathname.startsWith('/admin')) { + if (!event.locals.user) throw redirect(307, '/login'); + if (!event.locals.user?.email) { + throw redirect(307, '/app'); + } + } else if (event.url.pathname.startsWith('/org')) { + if (!event.locals.user) throw redirect(307, '/login'); + } + return resolve(event); +}; + +export const handle = sequence( + handleAuth, + ({ event, resolve }) => svelteKitHandler({ event, resolve, auth: event.locals.auth, building }), + handleOrgAndGuards +); diff --git a/apps/web/src/lib/assets/images/banner.png b/apps/web/src/lib/assets/images/banner.png new file mode 100644 index 0000000..641cc18 Binary files /dev/null and b/apps/web/src/lib/assets/images/banner.png differ diff --git a/src/lib/assets/images/google.svg b/apps/web/src/lib/assets/images/google.svg similarity index 100% rename from src/lib/assets/images/google.svg rename to apps/web/src/lib/assets/images/google.svg diff --git a/src/lib/assets/images/img_login.png b/apps/web/src/lib/assets/images/img_login.png similarity index 100% rename from src/lib/assets/images/img_login.png rename to apps/web/src/lib/assets/images/img_login.png diff --git a/apps/web/src/lib/assets/images/logo.png b/apps/web/src/lib/assets/images/logo.png new file mode 100644 index 0000000..1433123 Binary files /dev/null and b/apps/web/src/lib/assets/images/logo.png differ diff --git a/apps/web/src/lib/auth-client.ts b/apps/web/src/lib/auth-client.ts new file mode 100644 index 0000000..41e4c30 --- /dev/null +++ b/apps/web/src/lib/auth-client.ts @@ -0,0 +1,14 @@ +import { createAuthClient } from 'better-auth/client'; +import { organizationClient, inferOrgAdditionalFields } from 'better-auth/client/plugins'; +import type { AuthType } from '$lib/auth'; + +export const authClient = createAuthClient({ + baseURL: typeof window !== 'undefined' ? window.location.origin : process.env.PUBLIC_APP_URL || 'http://localhost:5173', + plugins: [ + organizationClient({ + schema: inferOrgAdditionalFields() + }) + ] +}); + + diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts new file mode 100644 index 0000000..9299e45 --- /dev/null +++ b/apps/web/src/lib/auth.ts @@ -0,0 +1,49 @@ +import { betterAuth } from 'better-auth'; +import { organization } from 'better-auth/plugins'; +import { jwt } from 'better-auth/plugins'; +import { drizzleAdapter } from 'better-auth/adapters/drizzle'; +import type { DrizzleClient } from '@opensource-startup-crm/database'; + + +export function createAuth(env: Env, db: DrizzleClient) { + return betterAuth({ + baseURL: env.BASE_URL || 'http://localhost:5173', + emailAndPassword: { enabled: true }, + database: drizzleAdapter(db, { + provider: 'pg', + }), + socialProviders: { + google: { + clientId: env?.GOOGLE_CLIENT_ID || '', + clientSecret: env?.GOOGLE_CLIENT_SECRET || '', + enabled: !!env?.GOOGLE_CLIENT_ID && !!env?.GOOGLE_CLIENT_SECRET + } + }, + plugins: [ + organization({ + schema: { + organization: { + additionalFields: { + domain: { type: 'string', input: true, required: false }, + website: { type: 'string', input: true, required: false }, + industry: { type: 'string', input: true, required: false }, + description: { type: 'string', input: true, required: false }, + isActive: { type: 'boolean', input: true, required: false } + } + }, + member: { + additionalFields: {} + }, + invitation: { + additionalFields: {} + } + } + }), + jwt() + ] + }); +} + +export type AuthType = ReturnType; + + diff --git a/apps/web/src/lib/data/enum-helpers.ts b/apps/web/src/lib/data/enum-helpers.ts new file mode 100644 index 0000000..e6455e3 --- /dev/null +++ b/apps/web/src/lib/data/enum-helpers.ts @@ -0,0 +1,33 @@ +function normalizeInput(input: unknown): string { + return typeof input === 'string' ? input.trim() : ''; +} + +export function validateEnumOrDefault( + input: unknown, + allowed: T, + fallback: T[number] +): T[number] { + const value = normalizeInput(input); + return (allowed as readonly string[]).includes(value) ? (value as T[number]) : fallback; +} + +export function validateEnumOrNull( + input: unknown, + allowed: T +): T[number] | null { + const value = normalizeInput(input); + return (allowed as readonly string[]).includes(value) ? (value as T[number]) : null; +} + +export type ValueLabel = { value: string; label: string }; + +export function toLabel( + value: string | null | undefined, + options: readonly ValueLabel[], + fallback = 'N/A' +): string { + if (!value) return fallback; + const match = options.find((o) => o.value === value); + return match?.label ?? fallback; +} + diff --git a/apps/web/src/lib/data/index.ts b/apps/web/src/lib/data/index.ts new file mode 100644 index 0000000..1c9aa5d --- /dev/null +++ b/apps/web/src/lib/data/index.ts @@ -0,0 +1,190 @@ +// Consolidated simple lookup tuples. All are readonly tuple arrays. + +import { + // Value/Label options + INDUSTRY_OPTIONS, + ACCOUNT_TYPE_OPTIONS, + ACCOUNT_OWNERSHIP_OPTIONS, + RATING_OPTIONS, + LEAD_SOURCE_OPTIONS, + LEAD_STATUS_OPTIONS, + OPPORTUNITY_TYPE_OPTIONS, + FORECAST_CATEGORY_OPTIONS, + CASE_STATUS_OPTIONS, + OPPORTUNITY_STAGE_OPTIONS, + TASK_STATUS_OPTIONS, + TASK_PRIORITY_OPTIONS, + // Types + type ValueLabel, + type TASK_PRIORITIES, + CASE_STATUSES, + QUOTE_STATUSES, + TASK_STATUSES, + LEAD_STATUSES, + OPPORTUNITY_STAGES +} from '@opensource-startup-crm/constants'; + +// Tuples removed; rely on constants options/values +// industries tuples not needed in app layer + +// accountTypes tuples not needed in app layer + +// accountOwnership tuples not needed in app layer + +// ratings tuples not needed in app layer + +export const countries = [ + ['US', 'United States'], + ['UK', 'United Kingdom'], + ['CA', 'Canada'], + ['AU', 'Australia'], + ['IN', 'India'], + ['DE', 'Germany'], + ['FR', 'France'], + ['JP', 'Japan'], + ['CN', 'China'], + ['BR', 'Brazil'], + ['MX', 'Mexico'], + ['IT', 'Italy'], + ['ES', 'Spain'], + ['NL', 'Netherlands'], + ['SE', 'Sweden'], + ['NO', 'Norway'], + ['DK', 'Denmark'], + ['FI', 'Finland'], + ['CH', 'Switzerland'], + ['AT', 'Austria'], + ['BE', 'Belgium'], + ['IE', 'Ireland'], + ['PL', 'Poland'], + ['RU', 'Russia'], + ['KR', 'South Korea'], + ['SG', 'Singapore'], + ['TH', 'Thailand'], + ['MY', 'Malaysia'], + ['ID', 'Indonesia'], + ['PH', 'Philippines'], + ['VN', 'Vietnam'], + ['NZ', 'New Zealand'], + ['ZA', 'South Africa'], + ['EG', 'Egypt'], + ['NG', 'Nigeria'], + ['KE', 'Kenya'], + ['AR', 'Argentina'], + ['CL', 'Chile'], + ['CO', 'Colombia'], + ['PE', 'Peru'], + ['OTHER', 'Other'] +] as const; + +// Opportunity stages with display colors (labels sourced from constants) +const stageColorMap: Record = { + PROSPECTING: 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200', + QUALIFICATION: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200', + PROPOSAL: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200', + NEGOTIATION: 'bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200', + CLOSED_WON: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200', + CLOSED_LOST: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' +}; +export const opportunityStages: ReadonlyArray<{ value: string; label: string; color: string }> = + OPPORTUNITY_STAGE_OPTIONS.map(({ value, label }) => ({ value, label, color: stageColorMap[value as keyof typeof stageColorMap] })); + +export type Option = ValueLabel; + +export const sourceOptions: Option[] = [{ value: '', label: 'Select Source' }, ...LEAD_SOURCE_OPTIONS]; +export const leadStatusOptions: Option[] = LEAD_STATUS_OPTIONS; +export const ratingOptions: Option[] = [{ value: '', label: 'Select Rating' }, ...RATING_OPTIONS]; +export const opportunityTypeOptions: Option[] = [{ value: '', label: 'Select Type' }, ...OPPORTUNITY_TYPE_OPTIONS]; +export const forecastCategoryOptions: Option[] = [{ value: '', label: 'Select Category' }, ...FORECAST_CATEGORY_OPTIONS]; +export const accountTypeOptions: Option[] = [{ value: '', label: 'Select Type' }, ...ACCOUNT_TYPE_OPTIONS]; +export const accountOwnershipOptions: Option[] = [{ value: '', label: 'Select Ownership' }, ...ACCOUNT_OWNERSHIP_OPTIONS]; +export const industryOptions: Option[] = [{ value: '', label: 'Select Industry' }, ...INDUSTRY_OPTIONS]; +export const countryOptions: Option[] = [{ value: '', label: 'Select Country' }, ...countries.map(([value, label]) => ({ value, label }))]; + +export const caseStatusOptions: Option[] = [{ value: '', label: 'Select Status' }, ...CASE_STATUS_OPTIONS]; + + +// Lead visuals and filters +import { Star, TrendingUp, CheckCircle2 as LeadCheckCircle2, Clock as LeadClock, XCircle as LeadXCircle, AlertCircle as LeadAlertCircle } from '@lucide/svelte'; +type LeadIconComponent = typeof Star; +export const leadStatusVisuals: Record = { + NEW: { icon: Star, color: 'border-blue-200 bg-blue-100 text-blue-800' }, + PENDING: { icon: LeadClock, color: 'border-yellow-200 bg-yellow-100 text-yellow-800' }, + CONTACTED: { icon: LeadCheckCircle2, color: 'border-green-200 bg-green-100 text-green-800' }, + QUALIFIED: { icon: TrendingUp, color: 'border-indigo-200 bg-indigo-100 text-indigo-800' }, + UNQUALIFIED: { icon: LeadXCircle, color: 'border-red-200 bg-red-100 text-red-800' }, + CONVERTED: { icon: LeadCheckCircle2, color: 'border-gray-200 bg-gray-100 text-gray-800' } +}; +export const leadStatusOptionsWithColor: Array