From eb614d0c17e0b8ccdb3fd8fbad662296633c8c57 Mon Sep 17 00:00:00 2001 From: Gokul Shaji Date: Wed, 10 Jun 2026 08:18:44 +0530 Subject: [PATCH 01/16] Initial commit --- bmvBackend/.env.example | 10 + bmvBackend/.gitignore | 56 + bmvBackend/.prettierrc | 4 + bmvBackend/CONTRIBUTING.md | 64 + bmvBackend/LICENSE | 21 + bmvBackend/README.md | 48 + bmvBackend/eslint.config.mjs | 35 + bmvBackend/nest-cli.json | 8 + bmvBackend/package-lock.json | 11675 ++++++++++++++++ bmvBackend/package.json | 91 + bmvBackend/src/app.controller.spec.ts | 22 + bmvBackend/src/app.controller.ts | 13 + bmvBackend/src/app.module.ts | 29 + bmvBackend/src/app.service.ts | 8 + bmvBackend/src/auth/auth.controller.spec.ts | 18 + bmvBackend/src/auth/auth.controller.ts | 80 + bmvBackend/src/auth/auth.module.ts | 38 + bmvBackend/src/auth/auth.service.spec.ts | 18 + bmvBackend/src/auth/auth.service.ts | 273 + bmvBackend/src/auth/dto/login.dto.ts | 12 + bmvBackend/src/auth/dto/refresh-token.dto.ts | 7 + bmvBackend/src/auth/dto/send-otp.dto.ts | 10 + bmvBackend/src/auth/dto/verify-otp.dto.ts | 16 + .../src/auth/entities/phone-otp.entity.ts | 27 + .../src/auth/entities/refresh-token.entity.ts | 37 + bmvBackend/src/auth/otp.util.ts | 7 + .../src/cloudinary/cloudinary.module.ts | 12 + .../src/cloudinary/cloudinary.provider.ts | 16 + .../src/cloudinary/cloudinary.service.ts | 85 + .../decorators/current-user.decorator.ts | 25 + .../src/common/decorators/roles.decorator.ts | 14 + .../src/common/enums/booking-type.enum.ts | 9 + .../src/common/enums/document-status.enum.ts | 8 + .../src/common/enums/document-type.enum.ts | 11 + .../src/common/enums/image-type.enum.ts | 11 + bmvBackend/src/common/enums/user-role.enum.ts | 12 + .../src/common/enums/venue-status.enum.ts | 17 + .../src/common/enums/venue-type.enum.ts | 15 + .../common/enums/verification-status.enum.ts | 10 + .../src/common/guards/jwt-auth.guard.ts | 30 + bmvBackend/src/common/guards/roles.guard.ts | 40 + .../src/common/guards/venue-owner.guard.ts | 62 + .../src/common/strategies/jwt.strategy.ts | 39 + bmvBackend/src/config/config.module.ts | 2 + bmvBackend/src/config/env.config.ts | 2 + bmvBackend/src/db/db.module.ts | 30 + bmvBackend/src/db/seeds/facilities.sql | 21 + bmvBackend/src/main.ts | 19 + .../src/users/dto/onboard-customer.dto.ts | 32 + bmvBackend/src/users/dto/register-user.dto.ts | 42 + .../users/entities/customer-profile.entity.ts | 47 + bmvBackend/src/users/entities/user.entity.ts | 59 + bmvBackend/src/users/users.controller.spec.ts | 18 + bmvBackend/src/users/users.controller.ts | 39 + bmvBackend/src/users/users.module.ts | 17 + bmvBackend/src/users/users.service.spec.ts | 18 + bmvBackend/src/users/users.service.ts | 176 + .../admin-venue.controller.spec.ts | 18 + .../admin-venue/admin-venue.controller.ts | 37 + .../venues/admin-venue/admin-venue.module.ts | 18 + .../admin-venue/admin-venue.service.spec.ts | 18 + .../venues/admin-venue/admin-venue.service.ts | 134 + .../src/venues/dto/add-venue-document.dto.ts | 14 + .../src/venues/dto/add-venue-image.dto.ts | 22 + bmvBackend/src/venues/dto/create-venue.dto.ts | 95 + bmvBackend/src/venues/dto/public-list-dto.ts | 34 + .../venues/dto/public-venues-filter.dto.ts | 35 + .../src/venues/dto/update-venue-info.dto.ts | 8 + .../venues/entities/venue-document.entity.ts | 52 + .../src/venues/entities/venue-image.entity.ts | 47 + .../venue-verification-request.entity.ts | 79 + .../src/venues/entities/venue.entity.ts | 129 + .../src/venues/public-venues.controller.ts | 26 + bmvBackend/src/venues/venues.controller.ts | 227 + bmvBackend/src/venues/venues.module.ts | 27 + bmvBackend/src/venues/venues.service.ts | 509 + bmvBackend/test/app.e2e-spec.ts | 29 + bmvBackend/test/jest-e2e.json | 9 + bmvBackend/tsconfig.build.json | 4 + bmvBackend/tsconfig.json | 23 + 80 files changed, 15159 insertions(+) create mode 100644 bmvBackend/.env.example create mode 100644 bmvBackend/.gitignore create mode 100644 bmvBackend/.prettierrc create mode 100644 bmvBackend/CONTRIBUTING.md create mode 100644 bmvBackend/LICENSE create mode 100644 bmvBackend/README.md create mode 100644 bmvBackend/eslint.config.mjs create mode 100644 bmvBackend/nest-cli.json create mode 100644 bmvBackend/package-lock.json create mode 100644 bmvBackend/package.json create mode 100644 bmvBackend/src/app.controller.spec.ts create mode 100644 bmvBackend/src/app.controller.ts create mode 100644 bmvBackend/src/app.module.ts create mode 100644 bmvBackend/src/app.service.ts create mode 100644 bmvBackend/src/auth/auth.controller.spec.ts create mode 100644 bmvBackend/src/auth/auth.controller.ts create mode 100644 bmvBackend/src/auth/auth.module.ts create mode 100644 bmvBackend/src/auth/auth.service.spec.ts create mode 100644 bmvBackend/src/auth/auth.service.ts create mode 100644 bmvBackend/src/auth/dto/login.dto.ts create mode 100644 bmvBackend/src/auth/dto/refresh-token.dto.ts create mode 100644 bmvBackend/src/auth/dto/send-otp.dto.ts create mode 100644 bmvBackend/src/auth/dto/verify-otp.dto.ts create mode 100644 bmvBackend/src/auth/entities/phone-otp.entity.ts create mode 100644 bmvBackend/src/auth/entities/refresh-token.entity.ts create mode 100644 bmvBackend/src/auth/otp.util.ts create mode 100644 bmvBackend/src/cloudinary/cloudinary.module.ts create mode 100644 bmvBackend/src/cloudinary/cloudinary.provider.ts create mode 100644 bmvBackend/src/cloudinary/cloudinary.service.ts create mode 100644 bmvBackend/src/common/decorators/current-user.decorator.ts create mode 100644 bmvBackend/src/common/decorators/roles.decorator.ts create mode 100644 bmvBackend/src/common/enums/booking-type.enum.ts create mode 100644 bmvBackend/src/common/enums/document-status.enum.ts create mode 100644 bmvBackend/src/common/enums/document-type.enum.ts create mode 100644 bmvBackend/src/common/enums/image-type.enum.ts create mode 100644 bmvBackend/src/common/enums/user-role.enum.ts create mode 100644 bmvBackend/src/common/enums/venue-status.enum.ts create mode 100644 bmvBackend/src/common/enums/venue-type.enum.ts create mode 100644 bmvBackend/src/common/enums/verification-status.enum.ts create mode 100644 bmvBackend/src/common/guards/jwt-auth.guard.ts create mode 100644 bmvBackend/src/common/guards/roles.guard.ts create mode 100644 bmvBackend/src/common/guards/venue-owner.guard.ts create mode 100644 bmvBackend/src/common/strategies/jwt.strategy.ts create mode 100644 bmvBackend/src/config/config.module.ts create mode 100644 bmvBackend/src/config/env.config.ts create mode 100644 bmvBackend/src/db/db.module.ts create mode 100644 bmvBackend/src/db/seeds/facilities.sql create mode 100644 bmvBackend/src/main.ts create mode 100644 bmvBackend/src/users/dto/onboard-customer.dto.ts create mode 100644 bmvBackend/src/users/dto/register-user.dto.ts create mode 100644 bmvBackend/src/users/entities/customer-profile.entity.ts create mode 100644 bmvBackend/src/users/entities/user.entity.ts create mode 100644 bmvBackend/src/users/users.controller.spec.ts create mode 100644 bmvBackend/src/users/users.controller.ts create mode 100644 bmvBackend/src/users/users.module.ts create mode 100644 bmvBackend/src/users/users.service.spec.ts create mode 100644 bmvBackend/src/users/users.service.ts create mode 100644 bmvBackend/src/venues/admin-venue/admin-venue.controller.spec.ts create mode 100644 bmvBackend/src/venues/admin-venue/admin-venue.controller.ts create mode 100644 bmvBackend/src/venues/admin-venue/admin-venue.module.ts create mode 100644 bmvBackend/src/venues/admin-venue/admin-venue.service.spec.ts create mode 100644 bmvBackend/src/venues/admin-venue/admin-venue.service.ts create mode 100644 bmvBackend/src/venues/dto/add-venue-document.dto.ts create mode 100644 bmvBackend/src/venues/dto/add-venue-image.dto.ts create mode 100644 bmvBackend/src/venues/dto/create-venue.dto.ts create mode 100644 bmvBackend/src/venues/dto/public-list-dto.ts create mode 100644 bmvBackend/src/venues/dto/public-venues-filter.dto.ts create mode 100644 bmvBackend/src/venues/dto/update-venue-info.dto.ts create mode 100644 bmvBackend/src/venues/entities/venue-document.entity.ts create mode 100644 bmvBackend/src/venues/entities/venue-image.entity.ts create mode 100644 bmvBackend/src/venues/entities/venue-verification-request.entity.ts create mode 100644 bmvBackend/src/venues/entities/venue.entity.ts create mode 100644 bmvBackend/src/venues/public-venues.controller.ts create mode 100644 bmvBackend/src/venues/venues.controller.ts create mode 100644 bmvBackend/src/venues/venues.module.ts create mode 100644 bmvBackend/src/venues/venues.service.ts create mode 100644 bmvBackend/test/app.e2e-spec.ts create mode 100644 bmvBackend/test/jest-e2e.json create mode 100644 bmvBackend/tsconfig.build.json create mode 100644 bmvBackend/tsconfig.json diff --git a/bmvBackend/.env.example b/bmvBackend/.env.example new file mode 100644 index 000000000..15ba67df6 --- /dev/null +++ b/bmvBackend/.env.example @@ -0,0 +1,10 @@ +# ─── Database ─────────────────────────────────────────────── +DB_HOST=localhost +DB_PORT=5432 +DB_USER=postgres +DB_PASS= +DB_NAME=bookmyvenue + +# ─── App ──────────────────────────────────────────────────── +PORT=3000 +NODE_ENV=development diff --git a/bmvBackend/.gitignore b/bmvBackend/.gitignore new file mode 100644 index 000000000..4b56acfbe --- /dev/null +++ b/bmvBackend/.gitignore @@ -0,0 +1,56 @@ +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/bmvBackend/.prettierrc b/bmvBackend/.prettierrc new file mode 100644 index 000000000..a20502b7f --- /dev/null +++ b/bmvBackend/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/bmvBackend/CONTRIBUTING.md b/bmvBackend/CONTRIBUTING.md new file mode 100644 index 000000000..777d2f758 --- /dev/null +++ b/bmvBackend/CONTRIBUTING.md @@ -0,0 +1,64 @@ +# 🤝 Contribution Guidelines + +We are building BookMyVenue in **4 Phases** to ensure a structured and community-driven approach: + +### 🚀 Phase 1: The MVP (Current Phase) +Our immediate goal is to explore different approaches to building the Minimum Viable Product (MVP). +- **Open Stack:** During this phase, **anyone can contribute a base MVP using any tech stack**. +- You aren't just submitting a single feature or component—you are submitting the foundational base for the project! We encourage using a **Monorepo structure** (where your frontend and backend microservices are organized in folders within the single repository). It doesn't need to be perfect or fully featured, but it should cover the basic core flow end-to-end. We will review all submissions and merge the best one(s) to serve as our starting point. + +### 🧩 Phase 2 & 3: Feature Implementation & Modularization +Once the MVP is solid, we will shift focus to expanding features and refining the architecture. +- Implementing features module by module. +- Refactoring and modularizing code to ensure it is clean, maintainable, and reusable. +- Standardizing the tech stack based on community consensus from Phase 1. + +### 🌐 Phase 4: Fully Scalable Solution +The final phase focuses on scale and performance. +- Transitioning to a fully scalable product. +- Optimizing databases, adding caching layers, and handling high concurrent traffic. + +## 🛠️ How to Contribute + +We follow the standard open-source **Fork & Pull Request** workflow. Just follow these steps: + +### Step 1: Fork and Clone the Repository +1. Click the **Fork** button at the top right of this page to create a copy of this repository in your GitHub account. +2. Clone your forked repository to your local machine: + ```bash + git clone https://github.com//BookMyVenue.git + cd BookMyVenue + ``` + +### Step 2: Create a Branch +Always create a new branch for your work. Do not make changes directly on the `main` branch. +Give your branch a descriptive name so we know what you are working on. +```bash +git checkout -b feat/ +``` +*(Other prefixes you can use: `fix/`, `docs/`, `chore/`)* + +### Step 3: Work and Commit +1. Make your code changes or additions. +2. Test your changes locally. +3. Commit your changes with a clear and concise commit message: + ```bash + git add . + git commit -m "Add: " + ``` + +### Step 4: Push and Create a Pull Request (PR) +1. Push your branch to your forked repository on GitHub: + ```bash + git push origin feat/ + ``` +2. Go to the original BookMyVenue repository. You should see a prompt to **Compare & pull request**. Click it! +3. Provide a clear description in your PR of what you changed, why, and which phase it belongs to. +4. Submit the PR. + +--- + +### Need Help? +If you're stuck or have questions about where to start, feel free to open an Issue and ask the community. We're here to help! + +Happy Coding! 🎉 diff --git a/bmvBackend/LICENSE b/bmvBackend/LICENSE new file mode 100644 index 000000000..1389c12c1 --- /dev/null +++ b/bmvBackend/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 WeCode Org + +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. diff --git a/bmvBackend/README.md b/bmvBackend/README.md new file mode 100644 index 000000000..0161641a6 --- /dev/null +++ b/bmvBackend/README.md @@ -0,0 +1,48 @@ +# BookMyVenue (by WeCode) + +BookMyVenue is a 100% open-source initiative built by the **WeCode community**, designed to simplify the process of finding and booking venues in local areas. This is a platform built for the community, by the community. Whether you're looking for a cozy cafe for a quick meetup, a spacious auditorium for a community event, or a scenic outdoor space for a personal celebration, BookMyVenue aims to connect people with the perfect space—without the commercial overhead. + +## 🚧 The Problem + +Organizing an event—big or small—often comes with the hassle of finding a suitable location. Traditionally, people struggle with: + +- **Fragmented Information:** Venue details, availability, and pricing are scattered across various websites or require time-consuming phone calls. +- **Lack of Transparency:** Hidden costs, unclear amenities, and outdated photos make it hard to trust what you are booking. +- **Time Inefficiency:** Manually comparing options, negotiating, and finalizing bookings is a tedious and frustrating process for organizers. +- **Underutilized Spaces:** Owners of small or unique spaces (like local cafes, art studios, or boutique halls) often lack the marketing reach to showcase their venues to a wider audience. + +## 💡 The Solution + +BookMyVenue acts as a completely free and open bridge between space owners and the community, offering a seamless booking experience for everyone: + +- **Centralized Marketplace:** A single platform to discover a wide variety of venues, ranging from conventional banquet halls and auditoriums to unconventional spaces like cafes, studios, and mall pop-up spaces. +- **Real-Time Availability & Pricing:** Transparent access to schedules and upfront pricing so you can make informed decisions quickly without the back-and-forth. +- **Detailed Listings:** High-quality photos, comprehensive amenity lists (e.g., Wi-Fi, parking, AV equipment), and verified user reviews. +- **Simplified Booking Process:** Easy, hassle-free online reservations in just a few clicks. +- **Empowering Local Communities:** Providing small venue owners, community centers, and local spaces an open platform to manage their underutilized spaces and share them with the public. + +## 🗺️ Project Roadmap + +We are building BookMyVenue iteratively through **4 Phases**: +- **Phase 1: MVP (Current):** Anyone can contribute in *any stack*. If it's good, we merge it! +- **Phase 2 & 3: Feature & Modularization:** Implementing features module by module and refining the architecture. +- **Phase 4: Scalability:** Building a fully scalable, cloud-native solution for high traffic. + +## 🤝 How to Contribute + +This is a collective effort, and everyone in the WeCode community shares the benefits of what we build! Whether you're a beginner or a pro, we would love your help. +Please check out our full [Contribution Guidelines](CONTRIBUTING.md) to learn how to: +- Fork and clone the repository. +- Create your feature branch (`feat/`). +- Submit a Pull Request. + +### 📝 Pull Request Format +To maintain a high standard of code, all Pull Requests must use our [standard template](.github/PULL_REQUEST_TEMPLATE.md). When you open a PR, you will be prompted to: +1. **Select the Phase Category** (e.g., Phase 1 MVP). +2. **Document your Tech Stack** (Frontend, Backend, Database). +3. **Complete the Review Checklist** (Self-review, community standards). +4. **Sign the AI Disclosure** (Confirming you have reviewed any AI-generated code). +5. **Attach Screenshots** (If your PR includes UI changes). + +**BookMyVenue belongs to all of us. Join WeCode today and let's build something amazing together!** +love to contribute diff --git a/bmvBackend/eslint.config.mjs b/bmvBackend/eslint.config.mjs new file mode 100644 index 000000000..4e9f8271c --- /dev/null +++ b/bmvBackend/eslint.config.mjs @@ -0,0 +1,35 @@ +// @ts-check +import eslint from '@eslint/js'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['eslint.config.mjs'], + }, + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + eslintPluginPrettierRecommended, + { + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + sourceType: 'commonjs', + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', + "prettier/prettier": ["error", { endOfLine: "auto" }], + }, + }, +); diff --git a/bmvBackend/nest-cli.json b/bmvBackend/nest-cli.json new file mode 100644 index 000000000..f9aa683b1 --- /dev/null +++ b/bmvBackend/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/bmvBackend/package-lock.json b/bmvBackend/package-lock.json new file mode 100644 index 000000000..d02a1c49b --- /dev/null +++ b/bmvBackend/package-lock.json @@ -0,0 +1,11675 @@ +{ + "name": "bookmyvenue", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bookmyvenue", + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@aws-sdk/client-s3": "^3.1062.0", + "@aws-sdk/s3-request-presigner": "^3.1062.0", + "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.4", + "@nestjs/core": "^11.0.1", + "@nestjs/jwt": "^11.0.2", + "@nestjs/mapped-types": "^2.1.1", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.1.24", + "@nestjs/throttler": "^6.5.0", + "@nestjs/typeorm": "^11.0.1", + "@types/bcrypt": "^6.0.0", + "@types/passport-jwt": "^4.0.1", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "cloudinary": "^2.10.0", + "multer": "^2.1.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "pg": "^8.21.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "typeorm": "^0.3.21" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.0.1", + "@types/express": "^5.0.0", + "@types/jest": "^30.0.0", + "@types/multer": "^2.1.0", + "@types/node": "^24.0.0", + "@types/supertest": "^7.0.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^17.0.0", + "jest": "^30.0.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.24.tgz", + "integrity": "sha512-Kd49warf6U/EyWe5BszF/eebN3zQ3bk7tgfEljAw8q/rX95UUtriJubWvp6pgzHfzBA4jwq8f+QiNZB8eBEXPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.24.tgz", + "integrity": "sha512-lnw+ZM1Io+cJAkReC0NPDjqObL8NtKzKIkdgEEKC8CUmkhurYhedbicN8Y8NYHgG1uLd2GozW3+/QqPRZaN+Lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.24", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.24", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.24.tgz", + "integrity": "sha512-bsStZQG67J1HBqTmWxtIcobvgrn32L4UOdL7hGyOru5VxDWPNA8pRnDYavT3hnJeBkJYPoQIw8u7Dm0ecoQprw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/crc32c": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha1-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", + "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/checksums": { + "version": "3.1000.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/checksums/-/checksums-3.1000.1.tgz", + "integrity": "sha512-DFCtlisEuWzw7rESV65jHK7De1QsJZRZgUNJ8ovpmdVaayPrxvmlsAlW8hka9E7f9B31d1T7lHG9oozZf6Bp6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@aws-crypto/crc32c": "5.2.0", + "@aws-crypto/util": "5.2.0", + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/client-s3": { + "version": "3.1062.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1062.0.tgz", + "integrity": "sha512-fb+qr6Jql36rJwlOEgkzaC85PGZ1sQ1pTd5YlpwPbOoN54gpVBaSLsDp8GXMToymJzpDHd8GeSDalUak1W9gLw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha1-browser": "5.2.0", + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/credential-provider-node": "^3.972.51", + "@aws-sdk/middleware-flexible-checksums": "^3.974.26", + "@aws-sdk/middleware-sdk-s3": "^3.972.47", + "@aws-sdk/signature-v4-multi-region": "^3.996.31", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.974.17", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.17.tgz", + "integrity": "sha512-r8o4h2K7j6P9ngno+8ei0aK0U/4JwDb7A2fMMxGVoSqDN8AFlIzSDeZHME9LcVLR2codyhtr1WAAg+/nmkeeMA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.10", + "@aws-sdk/xml-builder": "^3.972.27", + "@aws/lambda-invoke-store": "^0.2.2", + "@smithy/core": "^3.24.6", + "@smithy/signature-v4": "^5.4.6", + "@smithy/types": "^4.14.3", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.972.43", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.43.tgz", + "integrity": "sha512-g0XVQKzaA/4cq1vz1IvCQwYM+1Pkv01J9yHDpCTXekVuGZRDEz0wqBQ1AuYTq7FM6uik4uBGH8Tb5d9YvgeA7g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.972.45", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.45.tgz", + "integrity": "sha512-w9PuOoKCt6+xoESvY+zlV0u3PKQ0mVL259PcsVR6a3S/uYJJHnIi4r1NxdJHEcNldUVRIciltWnFMGBR4YEm3g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.972.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.49.tgz", + "integrity": "sha512-83r5MK+PERv9irzky1o5aNbXiLuaLfeB7N8MrktB9USpoebdNtuG0Ek9ieIxpGH1aZ9a0nIaDaLjEr3EmOV3Ng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/credential-provider-env": "^3.972.43", + "@aws-sdk/credential-provider-http": "^3.972.45", + "@aws-sdk/credential-provider-login": "^3.972.48", + "@aws-sdk/credential-provider-process": "^3.972.43", + "@aws-sdk/credential-provider-sso": "^3.972.48", + "@aws-sdk/credential-provider-web-identity": "^3.972.48", + "@aws-sdk/nested-clients": "^3.997.16", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/credential-provider-imds": "^4.3.7", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.972.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.48.tgz", + "integrity": "sha512-amPGeF6fcvLInK4Pu2k2Y2jHFR6MpaIKrZrbaf0QUnV3tjzjWh442eifZ2+KcmzFdsqyvyjBqAhq2JNLt1C5gA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/nested-clients": "^3.997.16", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.972.51", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.51.tgz", + "integrity": "sha512-mbhSY3ytXIGMuBoJsWCivk+63dtVlenT6wstUra07Lar4Ln2MVL8/j5zCTIOog+ig5/FlFJ8gcFU4nQZV+Jh4Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "^3.972.43", + "@aws-sdk/credential-provider-http": "^3.972.45", + "@aws-sdk/credential-provider-ini": "^3.972.49", + "@aws-sdk/credential-provider-process": "^3.972.43", + "@aws-sdk/credential-provider-sso": "^3.972.48", + "@aws-sdk/credential-provider-web-identity": "^3.972.48", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/credential-provider-imds": "^4.3.7", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.972.43", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.43.tgz", + "integrity": "sha512-GPokLNyvTfCmuaHk+v3GKVs4ZT3cMu5kgS2a+NPkOMt96cq6fSIK0g+mZHpGS6Cd4QGrPKesANEaLUKgOskTzg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.972.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.48.tgz", + "integrity": "sha512-tf0sD47SeTgCDfOWYssctzGgwAuk8/ECjb7bom4wZ7P1om0qE8i2yjniUdvysmANm5haARr35O8vZnTe/UEtpQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/nested-clients": "^3.997.16", + "@aws-sdk/token-providers": "3.1062.0", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.972.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.48.tgz", + "integrity": "sha512-YYsumc2oe09gl4l+fjfmR64JDn6+0o4Ql5HMBkMuhFazO1tZlE5NjSnZM3oXHwenPjh2qow0TFgSIVjfWfsojg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/nested-clients": "^3.997.16", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-flexible-checksums": { + "version": "3.974.26", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.26.tgz", + "integrity": "sha512-WndRXQV8wAU/bW3GH8THumEOSV7FpS0AtoluT2M7lYaaDUyG0gOCD+DppB+IWQ4TPmzuTtFcCedh9xCzM4Zv4g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/checksums": "^3.1000.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.47", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.47.tgz", + "integrity": "sha512-fzVBvGib8P1G6RFV3qVTPlXy9bMFAy5nxhdhA7LwyhWjRkJufNfJIPiloZq2mt36YAXSlLsEa4s3Kgcw6cv3+g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/signature-v4-multi-region": "^3.996.31", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.16.tgz", + "integrity": "sha512-bGvfDgC2KQePjEmZdltScPPLKFoyjPElAXeZcLfvZ58J1AO283//WGtvp9GdnryLHTi7gis0UoCezqh0vl/nig==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/signature-v4-multi-region": "^3.996.31", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/fetch-http-handler": "^5.4.6", + "@smithy/node-http-handler": "^4.7.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.1062.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.1062.0.tgz", + "integrity": "sha512-AE/LWvZWNBDbtx/29KdIAs3NKOvMi3xm22icENTwTDSlaRzsckTCb3xjaVB8ahKNylOhNbSLFSqgc0HtAax45A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/signature-v4-multi-region": "^3.996.31", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.31", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.31.tgz", + "integrity": "sha512-Kn2up9SlG1KC6wRtwf0d7waTGF6rvp9DxYqB54x6UCKdQ6kyaXCqHL4WGb5vUJga5kS8FxnjhY0LqM28aMvnNQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.10", + "@smithy/signature-v4": "^5.4.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1062.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1062.0.tgz", + "integrity": "sha512-fvHh53zSm2FoQPgkw9thH5D7sd13bC0nPyuZb+mQJ85l5v7lQnsZ97u6e6YkJJN/LU1Mxm1/DLGrIIRR2L7tZw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.17", + "@aws-sdk/nested-clients": "^3.997.16", + "@aws-sdk/types": "^3.973.10", + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.973.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.10.tgz", + "integrity": "sha512-992QrTO7G9qCvKD0fx1rMlqcL14plUcRAbwmqqYVsuF3GrqcvlAL9qxR+baMafarEZ+l7DUQ5lCMmt5mbMhF7g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.972.27", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.27.tgz", + "integrity": "sha512-hpsCXCOI436kxWpjtRuIHVvuPP81MOw8f18jzfZeg+UOiiOvlqWcmWChzEhJEu16cOC6+ku4ncBN+7rdt+DZ9g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.3", + "fast-xml-parser": "5.7.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.21", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", + "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", + "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.3.2", + "@inquirer/confirm": "^5.1.21", + "@inquirer/editor": "^4.2.23", + "@inquirer/expand": "^4.0.23", + "@inquirer/input": "^4.3.1", + "@inquirer/number": "^3.0.23", + "@inquirer/password": "^4.0.23", + "@inquirer/rawlist": "^4.1.11", + "@inquirer/search": "^3.2.2", + "@inquirer/select": "^4.4.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.4.1.tgz", + "integrity": "sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.4.2.tgz", + "integrity": "sha512-TZJA6cPJUFxoWhxaLo8t0VX/MZX2wPWr0uIDvLSHIvN4gu9h02vSzqI2kBADG1ExqQlC+cY09xKMSreivvrChQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/pattern": "30.4.0", + "@jest/reporters": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.4.1", + "jest-config": "30.4.2", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-resolve-dependencies": "30.4.2", + "jest-runner": "30.4.2", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "jest-watcher": "30.4.1", + "pretty-format": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", + "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.4.1.tgz", + "integrity": "sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.4.1", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", + "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.4.1.tgz", + "integrity": "sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@sinonjs/fake-timers": "^15.4.0", + "@types/node": "*", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.4.1.tgz", + "integrity": "sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/types": "30.4.1", + "jest-mock": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", + "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.4.1.tgz", + "integrity": "sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/schemas": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", + "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.4.1.tgz", + "integrity": "sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.4.1.tgz", + "integrity": "sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/types": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.4.1.tgz", + "integrity": "sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.4.1.tgz", + "integrity": "sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.4.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.1", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", + "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.4.0", + "@jest/schemas": "30.4.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@nestjs/cli": { + "version": "11.0.21", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.21.tgz", + "integrity": "sha512-F8mV0Sj/zVEouzR3NxBuJy08YHTUOmC5Xdcx3qIIaJWzrm8Vw86CHkhkaPBJ5ewRMHPDCShPmhsfwhpCcjts3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "@angular-devkit/schematics-cli": "19.2.24", + "@inquirer/prompts": "7.10.1", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.2.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "13.0.6", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.9.3", + "webpack": "5.106.0", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 20.11" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nestjs/cli/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/@nestjs/cli/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nestjs/cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nestjs/cli/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nestjs/cli/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/cli/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@nestjs/cli/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.106.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.0.tgz", + "integrity": "sha512-Pkx5joZ9RrdgO5LBkyX1L2ZAJeK/Taz3vqZ9CbcP0wS5LEMx5QkKsEwLl29QJfihZ+DKRBFldzy1O30pJ1MDpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.3.1", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.17", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.4" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.24.tgz", + "integrity": "sha512-9zHxaDDM+oXW9As6UsP5yYB+UqczBmpeSCIFWdPEtEukMnZhxODG1BBjaUcdBB8Sc1uzojSJSJlp3yFp853t1g==", + "license": "MIT", + "dependencies": { + "file-type": "21.3.4", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/config": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.4.tgz", + "integrity": "sha512-CJPjNitr0bAufSEnRe2N+JbnVmMmDoo6hvKCPzXgZoGwJSmp/dZPk9f/RMbuD/+Q1ZJPjwsRpq0vxna++Knwow==", + "license": "MIT", + "dependencies": { + "dotenv": "17.4.1", + "dotenv-expand": "12.0.3", + "lodash": "4.18.1" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/core": { + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.24.tgz", + "integrity": "sha512-K4bzT+lEdd0Hhcsw3jtk56QAW6s6skK3ViN7hIROSN0kUf4ROwWEAKopJID6yhPQxB45kDtP2wEcjzE8171J3g==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/jwt": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.2.tgz", + "integrity": "sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "9.0.10", + "jsonwebtoken": "9.0.3" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" + } + }, + "node_modules/@nestjs/mapped-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.1.tgz", + "integrity": "sha512-SCCoMEJ6jdeI5h/N+KCVF1+pmg/hmEkNA5nHTS8Gvww7T/LCl4o1gFLinw2iQ60w7slFkszHcGLKGdazVI4F8A==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0 || ^0.15.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/passport": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz", + "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "passport": "^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, + "node_modules/@nestjs/platform-express": { + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.24.tgz", + "integrity": "sha512-CeMKbRBm05aOBiWhIHWO2xDeHbxynBF9ySQv3gRjObz2N5+uJnYriAYkHvVqvC4JIydmMPmT5VdICFNlNz3qyA==", + "license": "MIT", + "dependencies": { + "cors": "2.8.6", + "express": "5.2.1", + "multer": "2.1.1", + "path-to-regexp": "8.4.2", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.1.0.tgz", + "integrity": "sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.24", + "@angular-devkit/schematics": "19.2.24", + "comment-json": "5.0.0", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "typescript": ">=4.8.2" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "node_modules/@nestjs/testing": { + "version": "11.1.24", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.24.tgz", + "integrity": "sha512-+4M4UAnhtprBQN0J2uI6IP0wDqhy9aH8XCMu5SO8oCi0oB04YXA4a4PAEkxmsPn7gHW4dj1u4GFteNQOWgvTJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + } + } + }, + "node_modules/@nestjs/throttler": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.5.0.tgz", + "integrity": "sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0" + } + }, + "node_modules/@nestjs/typeorm": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.1.tgz", + "integrity": "sha512-8rw/nKT0S+L+MkzgE9F2/mox7mAgsPlwfzmW9gsESN1lmQtIrVEfiiBwC2O8+guS1jBfQehJIdcdUj2OAp4VUQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0", + "rxjs": "^7.2.0", + "typeorm": "^0.3.0 || ^1.0.0-dev" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodable/entities": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.1.tgz", + "integrity": "sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.6.tgz", + "integrity": "sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@smithy/core": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.6.tgz", + "integrity": "sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.8.tgz", + "integrity": "sha512-5cAM+KZC02sTqDt6NaLXyu50M/GNMd1eTzDVR8Lb0BBsVtu7RWHo47VPPEEv1vt3Yub6uzr+M5FHC+GtoT0USg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.6.tgz", + "integrity": "sha512-FEwEYJ1jlBKdhe9TPzfghEi1bP55ZeEImlDkEa62bBBYzUcnB6RUCyuiS2mqKt6ZVjUbBgcNhzfIctH+Hevx9g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.7.tgz", + "integrity": "sha512-ZAFvHXrEk6K180EVhmZVg8GU5pUH5BSFqRs27JW3j1qEFx9YyYwWFx17x/MHcjALYimGAji7qEOlF1++be+G5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.6.tgz", + "integrity": "sha512-Ojg4B6oIDlIr1R86xCDJt1zJWnYa0VINmqdjfe9qxWjdRivHalZ3iSlQgVqYbW0MdpFOC5XfHEWsnbmdnpIILQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.24.6", + "@smithy/types": "^4.14.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.3.tgz", + "integrity": "sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/multer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.1.0.tgz", + "integrity": "sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/passport": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/superagent": { + "version": "8.1.10", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.10.tgz", + "integrity": "sha512-nbt4IWXABhW0jGmmpRzCFNlbmwCTzZ2gTUsNIr+X+ItdqPms+PAJZbWsNzpS2USqXjcoNLQcO6nXo60zcPQiIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/supertest": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-7.2.0.tgz", + "integrity": "sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz", + "integrity": "sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/type-utils": "8.60.0", + "@typescript-eslint/utils": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.60.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.0.tgz", + "integrity": "sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.0.tgz", + "integrity": "sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.0", + "@typescript-eslint/types": "^8.60.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz", + "integrity": "sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz", + "integrity": "sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz", + "integrity": "sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.0.tgz", + "integrity": "sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz", + "integrity": "sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.60.0", + "@typescript-eslint/tsconfig-utils": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/visitor-keys": "8.60.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.0.tgz", + "integrity": "sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.0", + "@typescript-eslint/types": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz", + "integrity": "sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "devOptional": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", + "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.4.1.tgz", + "integrity": "sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.4.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.4.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.4.0.tgz", + "integrity": "sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.4.0.tgz", + "integrity": "sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.4.0", + "babel-preset-current-node-syntax": "^1.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-beta.1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bowser": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", + "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.15.1.tgz", + "integrity": "sha512-LqoS80HBBSCVhz/3KloUly0ovokxpdOLR++Al3J3+dHXWt9sTKlKd4eYtoxhxyUjoe5+UcIM+5k9MIxyBWnRTw==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.22" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cloudinary": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.10.0.tgz", + "integrity": "sha512-sY09kYg7wprkndAOjZBAYqFZqwL+SxnEGcAvksOvFA+5upnFn949UjkEkHKNSwkBtW/xRDd0p6NgbSXZcxkI3w==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.23" + }, + "engines": { + "node": ">=9" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", + "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "17.4.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz", + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz", + "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.6.tgz", + "integrity": "sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.13" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", + "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.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.0", + "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.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-builder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", + "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.7", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "21.3.4", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.4.tgz", + "integrity": "sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "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.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.4.2.tgz", + "integrity": "sha512-Yi1jqNC/Oq0N4hBgNH/YvBpP1P57QqundgytzYqy3yqAa7NZPNjSoi4SGbRAXDMdBzNE6xBCi5U7RgfrvMEUVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/types": "30.4.1", + "import-local": "^3.2.0", + "jest-cli": "30.4.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.4.1.tgz", + "integrity": "sha512-IuctmYrxi21iOSOaIXpJWalHyPAsVv0GeBHKDn8C1CA4W5htHn7INL+wdnL4Bo0+olEndvAFkmb++tIQJG+vvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.4.2.tgz", + "integrity": "sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/expect": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-runtime": "30.4.2", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "p-limit": "^3.1.0", + "pretty-format": "30.4.1", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.4.2.tgz", + "integrity": "sha512-jfA2ocvVHMXS2QijrJ0d31ektP+d/W0T5RpcTX2Pq+3sVqHlsXVCM2+FmwpL+bdY8OfHpIg9xMxLF17Zg0U49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.4.2", + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.4.2.tgz", + "integrity": "sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.4.0", + "@jest/test-sequencer": "30.4.1", + "@jest/types": "30.4.1", + "babel-jest": "30.4.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-circus": "30.4.2", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-runner": "30.4.2", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "parse-json": "^5.2.0", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-diff": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", + "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.4.0", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.4.0.tgz", + "integrity": "sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.4.1.tgz", + "integrity": "sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "chalk": "^4.1.2", + "jest-util": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", + "integrity": "sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1", + "jest-validate": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.4.1.tgz", + "integrity": "sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.4.0", + "jest-util": "30.4.1", + "jest-worker": "30.4.1", + "picomatch": "^4.0.3", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.4.1.tgz", + "integrity": "sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", + "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.4.1", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", + "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.4.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-util": "30.4.1", + "picomatch": "^4.0.3", + "pretty-format": "30.4.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", + "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.4.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", + "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.4.1.tgz", + "integrity": "sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.4.1", + "jest-validate": "30.4.1", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.4.2.tgz", + "integrity": "sha512-gDiVh1I+GxYzz9oXlyw+1wv6VOYX1WYxMOfjsA3iGKePV2oxmbHhwxfkALxNxYy1ciw6APWwkW2zZONwP97aEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.4.0", + "jest-snapshot": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.4.2.tgz", + "integrity": "sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.4.1", + "@jest/environment": "30.4.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.4.0", + "jest-environment-node": "30.4.1", + "jest-haste-map": "30.4.1", + "jest-leak-detector": "30.4.1", + "jest-message-util": "30.4.1", + "jest-resolve": "30.4.1", + "jest-runtime": "30.4.2", + "jest-util": "30.4.1", + "jest-watcher": "30.4.1", + "jest-worker": "30.4.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.4.2", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.4.2.tgz", + "integrity": "sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/globals": "30.4.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.5.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.4.1", + "jest-message-util": "30.4.1", + "jest-mock": "30.4.1", + "jest-regex-util": "30.4.0", + "jest-resolve": "30.4.1", + "jest-snapshot": "30.4.1", + "jest-util": "30.4.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-snapshot": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.4.1.tgz", + "integrity": "sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.4.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.4.1", + "@jest/transform": "30.4.1", + "@jest/types": "30.4.1", + "babel-preset-current-node-syntax": "^1.2.0", + "chalk": "^4.1.2", + "expect": "30.4.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.4.1", + "jest-matcher-utils": "30.4.1", + "jest-message-util": "30.4.1", + "jest-util": "30.4.1", + "pretty-format": "30.4.1", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", + "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.4.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.4.1.tgz", + "integrity": "sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.4.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.4.1.tgz", + "integrity": "sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.4.1", + "@jest/types": "30.4.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.4.1", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.4.1.tgz", + "integrity": "sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.4.1", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.4.tgz", + "integrity": "sha512-/lhWr7vq8foWN9Apksnd9v8/cfwzW6g6qKOCo25XBGkNaVCHucXO57hLy4CWHGvytvLz6Nt3J5Gs8p3jlCGFXA==", + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz", + "integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "type-is": "^1.6.18" + }, + "engines": { + "node": ">= 10.16.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.8.0.tgz", + "integrity": "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "license": "MIT", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/pg": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.21.0.tgz", + "integrity": "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.13.0.tgz", + "integrity": "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.14.0.tgz", + "integrity": "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", + "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.4.1", + "ansi-styles": "^5.2.0", + "react-is-18": "npm:react-is@^18.3.1", + "react-is-19": "npm:react-is@^19.2.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-is-19": { + "name": "react-is", + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.6.tgz", + "integrity": "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", + "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.5", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.14.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/synckit": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.13.tgz", + "integrity": "sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.3.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-jest": { + "version": "29.4.11", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", + "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.8.0", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-loader": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.0.tgz", + "integrity": "sha512-dsJO0S+T7grTDWTc4a0nTygXGjKncVUpx8Y+af8EvI/D5WgTJby5UEk5eoMCB9EcLQmnvitqh99MqtjtHgAwFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "loader-utils": "*", + "typescript": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "loader-utils": { + "optional": true + } + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typeorm": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.30.tgz", + "integrity": "sha512-8T35PzjefOdqc2ZR9mwLQj0pUGp6lQhMbK2EvVMwJVJWlaoHm0v/Q6dThNOZkFchD+0yMg8gwjKM28ePiLSXSQ==", + "license": "MIT", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "ansis": "^4.2.0", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "dayjs": "^1.11.20", + "debug": "^4.4.3", + "dedent": "^1.7.2", + "dotenv": "^16.6.1", + "glob": "^10.5.0", + "reflect-metadata": "^0.2.2", + "sha.js": "^2.4.12", + "sql-highlight": "^6.1.0", + "tslib": "^2.8.1", + "uuid": "^11.1.1", + "yargs": "^17.7.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@sap/hana-client": "^2.14.22", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/typeorm/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.60.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.0.tgz", + "integrity": "sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.60.0", + "@typescript-eslint/parser": "8.60.0", + "@typescript-eslint/typescript-estree": "8.60.0", + "@typescript-eslint/utils": "8.60.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webpack": { + "version": "5.107.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", + "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.22.0", + "es-module-lexer": "^2.1.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "loader-runner": "^4.3.2", + "mime-db": "^1.54.0", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.5.0", + "watchpack": "^2.5.1", + "webpack-sources": "^3.5.0" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", + "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.21.tgz", + "integrity": "sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw==", + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/bmvBackend/package.json b/bmvBackend/package.json new file mode 100644 index 000000000..9159733dc --- /dev/null +++ b/bmvBackend/package.json @@ -0,0 +1,91 @@ +{ + "name": "bookmyvenue", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.1062.0", + "@aws-sdk/s3-request-presigner": "^3.1062.0", + "@nestjs/common": "^11.0.1", + "@nestjs/config": "^4.0.4", + "@nestjs/core": "^11.0.1", + "@nestjs/jwt": "^11.0.2", + "@nestjs/mapped-types": "^2.1.1", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.1.24", + "@nestjs/throttler": "^6.5.0", + "@nestjs/typeorm": "^11.0.1", + "@types/bcrypt": "^6.0.0", + "@types/passport-jwt": "^4.0.1", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.15.1", + "cloudinary": "^2.10.0", + "multer": "^2.1.1", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "pg": "^8.21.0", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "typeorm": "^0.3.21" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.0.1", + "@types/express": "^5.0.0", + "@types/jest": "^30.0.0", + "@types/multer": "^2.1.0", + "@types/node": "^24.0.0", + "@types/supertest": "^7.0.0", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^17.0.0", + "jest": "^30.0.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" + } +} diff --git a/bmvBackend/src/app.controller.spec.ts b/bmvBackend/src/app.controller.spec.ts new file mode 100644 index 000000000..d22f3890a --- /dev/null +++ b/bmvBackend/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/bmvBackend/src/app.controller.ts b/bmvBackend/src/app.controller.ts new file mode 100644 index 000000000..8b8c7b6a8 --- /dev/null +++ b/bmvBackend/src/app.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) { } + + @Get() + getHello(): string { + return this.appService.getHello(); + + } +} diff --git a/bmvBackend/src/app.module.ts b/bmvBackend/src/app.module.ts new file mode 100644 index 000000000..f58943fd3 --- /dev/null +++ b/bmvBackend/src/app.module.ts @@ -0,0 +1,29 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { ConfigModule } from '@nestjs/config'; +import { ThrottlerModule } from '@nestjs/throttler'; +import { AuthModule } from './auth/auth.module'; +import { DbModule } from './db/db.module'; +import { UsersModule } from './users/users.module'; +import { VenuesModule } from './venues/venues.module'; +import { CloudinaryModule } from './cloudinary/cloudinary.module'; + +@Module({ + imports: [ + ConfigModule.forRoot({ isGlobal: true }), + ThrottlerModule.forRoot([ + { name: 'otp', ttl: 60_000, limit: 3 }, + { name: 'login', ttl: 60_000, limit: 10 }, + ]), + CloudinaryModule, + AuthModule, + DbModule, + UsersModule, + VenuesModule, + ], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} + diff --git a/bmvBackend/src/app.service.ts b/bmvBackend/src/app.service.ts new file mode 100644 index 000000000..927d7cca0 --- /dev/null +++ b/bmvBackend/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/bmvBackend/src/auth/auth.controller.spec.ts b/bmvBackend/src/auth/auth.controller.spec.ts new file mode 100644 index 000000000..27a31e618 --- /dev/null +++ b/bmvBackend/src/auth/auth.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthController } from './auth.controller'; + +describe('AuthController', () => { + let controller: AuthController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AuthController], + }).compile(); + + controller = module.get(AuthController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/bmvBackend/src/auth/auth.controller.ts b/bmvBackend/src/auth/auth.controller.ts new file mode 100644 index 000000000..b43ed78ad --- /dev/null +++ b/bmvBackend/src/auth/auth.controller.ts @@ -0,0 +1,80 @@ +import { + Body, + Controller, + HttpCode, + HttpStatus, + Post, + UseGuards, +} from '@nestjs/common'; +import { Throttle, ThrottlerGuard } from '@nestjs/throttler'; +import { AuthService } from './auth.service'; +import { SendOtpDto } from './dto/send-otp.dto'; +import { VerifyOtpDto } from './dto/verify-otp.dto'; +import { LoginDto } from './dto/login.dto'; +import { RefreshTokenDto } from './dto/refresh-token.dto'; +@Controller('auth') +@UseGuards(ThrottlerGuard) // All auth routes are rate-limited +export class AuthController { + constructor(private readonly authService: AuthService) { } + + /** + * POST /auth/send-otp + * Step 1: Request an OTP for the given phone number. + * Rate-limit: 3 req / min — prevents SMS cost abuse & OTP brute-force. + */ + @Post('send-otp') + @HttpCode(HttpStatus.OK) + @Throttle({ otp: { limit: 3, ttl: 60_000 } }) + sendOtp(@Body() dto: SendOtpDto) { + return this.authService.sendOtp(dto); + } + + /** + * POST /auth/verify-otp + * Step 2: Verify the OTP. Returns a short-lived phoneVerifiedToken on success. + * Rate-limit: 3 req / min — same strict pool to prevent brute-force. + */ + @Post('verify-otp') + @HttpCode(HttpStatus.OK) + @Throttle({ otp: { limit: 3, ttl: 60_000 } }) + verifyOtp(@Body() dto: VerifyOtpDto) { + return this.authService.verifyOtp(dto); + } + + /** + * POST /auth/login + * Sign in with email + password. + * Returns: { accessToken (15 min), refreshToken (7 days), expiresIn, user } + * Rate-limit: 10 req / min — protects against credential stuffing. + */ + @Post('login') + @HttpCode(HttpStatus.OK) + + @Throttle({ login: { limit: 10, ttl: 60_000 } }) + login(@Body() dto: LoginDto) { + return this.authService.login(dto); + } + /** + * POST /auth/refresh + * Rotate tokens. Send the current refreshToken to get a new token pair. + * The old refresh token is invalidated immediately (rotation). + * Rate-limit: 10 req / min. + */ + @Post('refresh') + @HttpCode(HttpStatus.OK) + @Throttle({ login: { limit: 10, ttl: 60_000 } }) + refresh(@Body() dto: RefreshTokenDto) { + return this.authService.refresh(dto); + } + + /** + * POST /auth/logout + * Invalidate the current session by deleting the refresh token from DB. + * The access token will expire naturally within 15 minutes. + */ + @Post('logout') + @HttpCode(HttpStatus.OK) + logout(@Body() dto: RefreshTokenDto) { + return this.authService.logout(dto); + } +} diff --git a/bmvBackend/src/auth/auth.module.ts b/bmvBackend/src/auth/auth.module.ts new file mode 100644 index 000000000..f11820eb8 --- /dev/null +++ b/bmvBackend/src/auth/auth.module.ts @@ -0,0 +1,38 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { PhoneOtp } from './entities/phone-otp.entity'; +import { RefreshToken } from './entities/refresh-token.entity'; +import { User } from '../users/entities/user.entity'; +import { JwtStrategy } from '../common/strategies/jwt.strategy'; + +@Module({ + imports: [ + PassportModule.register({ defaultStrategy: 'jwt' }), + // PhoneOtp — OTP flow + // RefreshToken — refresh token rotation & revocation + // User — login credential lookup + TypeOrmModule.forFeature([PhoneOtp, RefreshToken, User]), + JwtModule.registerAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (config: ConfigService) => ({ + secret: config.get('JWT_SECRET'), + // No global default expiry — set per-token in the service + }), + }), + ], + controllers: [AuthController], + providers: [AuthService, JwtStrategy], + exports: [ + JwtModule, // UsersModule needs JwtService to verify phoneVerifiedToken + AuthService, // UsersModule needs AuthService.issueTokenPair() after registration + PassportModule, // exports the passport strategy for JwtAuthGuard + ], +}) +export class AuthModule {} + diff --git a/bmvBackend/src/auth/auth.service.spec.ts b/bmvBackend/src/auth/auth.service.spec.ts new file mode 100644 index 000000000..800ab6626 --- /dev/null +++ b/bmvBackend/src/auth/auth.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AuthService], + }).compile(); + + service = module.get(AuthService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/bmvBackend/src/auth/auth.service.ts b/bmvBackend/src/auth/auth.service.ts new file mode 100644 index 000000000..c01e7d4bb --- /dev/null +++ b/bmvBackend/src/auth/auth.service.ts @@ -0,0 +1,273 @@ +import { + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { MoreThan, Repository } from 'typeorm'; +import { JwtService } from '@nestjs/jwt'; +import { createHash, randomBytes } from 'crypto'; +import * as bcrypt from 'bcrypt'; +import { PhoneOtp } from './entities/phone-otp.entity'; +import { RefreshToken } from './entities/refresh-token.entity'; +import { User } from '../users/entities/user.entity'; +import { UserRole } from '../common/enums/user-role.enum'; +import { generateOtp } from './otp.util'; +import { SendOtpDto } from './dto/send-otp.dto'; +import { VerifyOtpDto } from './dto/verify-otp.dto'; +import { LoginDto } from './dto/login.dto'; +import { RefreshTokenDto } from './dto/refresh-token.dto'; + +const OTP_TTL_MINUTES = 5; + +/** Short-lived access token — stateless, fast to verify. */ +const ACCESS_TOKEN_TTL = '15m'; + +/** Long-lived refresh token — stored hashed in DB, fully revocable. */ +const REFRESH_TOKEN_TTL_DAYS = 7; + +/** How long the phone-verified token is valid (must complete registration in this window). */ +const PHONE_TOKEN_TTL = '10m'; + +/** Shared token-pair type returned by login, register, and refresh. */ +export type TokenPair = { + accessToken: string; + refreshToken: string; + expiresIn: number; // access token TTL in seconds (for the client timer) +}; + +@Injectable() +export class AuthService { + constructor( + @InjectRepository(PhoneOtp) + private readonly phoneOtpRepo: Repository, + @InjectRepository(RefreshToken) + private readonly refreshTokenRepo: Repository, + @InjectRepository(User) + private readonly userRepo: Repository, + private readonly jwtService: JwtService, + ) { } + + // ───────────────────────────────────────────────────────────────────────── + // Internal helpers + // ───────────────────────────────────────────────────────────────────────── + + /** + * Hashes a raw token with SHA-256. + * Only the hash is stored in the DB — the raw token lives only in memory. + */ + private hashToken(raw: string): string { + return createHash('sha256').update(raw).digest('hex'); + } + + /** + * Issues a short-lived access token + a long-lived refresh token. + * + * The raw refresh token is returned to the caller ONCE and never persisted. + * Only its SHA-256 hash is saved to `refresh_tokens`. + * Rotation: every call to this method creates a NEW DB row. + */ + async issueTokenPair(userId: string, role: UserRole): Promise { + // Access token — signed JWT, valid for 15 minutes + const accessToken = this.jwtService.sign( + { sub: userId, role, purpose: 'access' }, + { expiresIn: ACCESS_TOKEN_TTL }, + ); + + // Refresh token — 256-bit cryptographically random value (64 hex chars) + const rawRefreshToken = randomBytes(32).toString('hex'); + const tokenHash = this.hashToken(rawRefreshToken); + const expiresAt = new Date( + Date.now() + REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000, + ); + + await this.refreshTokenRepo.save( + this.refreshTokenRepo.create({ userId, tokenHash, expiresAt }), + ); + + return { + accessToken, + refreshToken: rawRefreshToken, + expiresIn: 15 * 60, // 900 seconds + }; + } + + // ───────────────────────────────────────────────────────────────────────── + // OTP Flow (Steps 1 & 2 of signup) + // ───────────────────────────────────────────────────────────────────────── + + /** + * Step 1 — Generate a 6-digit OTP, persist it, and send via SMS. + * Any previous OTP for this phone is deleted first. + */ + async sendOtp(dto: SendOtpDto): Promise<{ message: string }> { + const { phone } = dto; + + await this.phoneOtpRepo.delete({ phone }); + + const otp = generateOtp(); + const expiresAt = new Date(Date.now() + OTP_TTL_MINUTES * 60 * 1000); + + await this.phoneOtpRepo.save( + this.phoneOtpRepo.create({ phone, otpCode: otp, expiresAt }), + ); + + // TODO: Replace with real SMS provider (e.g. Twilio / Fast2SMS) + console.log(`[OTP] Phone: ${phone} Code: ${otp} Expires: ${expiresAt.toISOString()}`); + + return { message: 'OTP sent successfully. It expires in 5 minutes.' }; + } + + /** + * Step 2 — Verify the OTP. + * On success the OTP record is deleted and a 10-minute phoneVerifiedToken is returned. + * The frontend must include this token in the /users/register body. + */ + async verifyOtp(dto: VerifyOtpDto): Promise<{ + verified: boolean; + phone: string; + phoneVerifiedToken: string; + }> { + const { phone, otp } = dto; + + const record = await this.phoneOtpRepo.findOne({ + where: { phone, expiresAt: MoreThan(new Date()) }, + order: { createdAt: 'DESC' }, + }); + + if (!record) { + throw new UnauthorizedException( + 'OTP not found or has expired. Please request a new OTP.', + ); + } + + if (record.otpCode !== otp) { + throw new UnauthorizedException('Invalid OTP. Please try again.'); + } + + await this.phoneOtpRepo.delete(record.id); + + const phoneVerifiedToken = this.jwtService.sign( + { phone, purpose: 'phone_verification' }, + { expiresIn: PHONE_TOKEN_TTL }, + ); + + return { verified: true, phone, phoneVerifiedToken }; + } + + // ───────────────────────────────────────────────────────────────────────── + // Signin Flow + // ───────────────────────────────────────────────────────────────────────── + + /** + * POST /auth/login — Sign in with email + password. + * + * Returns a token pair: + * - accessToken (15 min) — use in Authorization: Bearer header + * - refreshToken (7 days) — store securely, use to get new accessToken + * + * Generic error message prevents user enumeration. + */ + async login(dto: LoginDto): Promise< + TokenPair & { + user: { + id: string; + name: string; + email: string; + phone: string; + role: UserRole; + isProfileCompleted: boolean; + }; + } + > { + const { email, password } = dto; + const user = await this.userRepo.findOne({ where: { email } }); + + if (!user) { + throw new UnauthorizedException('Invalid email or password.'); + } + + if (!user.isActive) { + throw new UnauthorizedException( + 'Your account has been deactivated. Please contact support.', + ); + } + + const passwordMatch = await bcrypt.compare(password, user.passwordHash); + if (!passwordMatch) { + throw new UnauthorizedException('Invalid email or password.'); + } + + const tokens = await this.issueTokenPair(user.id, user.role); + + return { + ...tokens, + user: { + id: user.id, + name: user.name, + email: user.email, + phone: user.phone, + role: user.role, + isProfileCompleted: user.isProfileCompleted, + }, + }; + } + + // ───────────────────────────────────────────────────────────────────────── + // Token Management + // ───────────────────────────────────────────────────────────────────────── + + /** + * POST /auth/refresh — Rotate tokens. + * + * Accepts the current refresh token and: + * 1. Validates it exists in DB and is not expired. + * 2. Deletes the old token (rotation — prevents token reuse). + * 3. Issues and returns a fresh token pair. + * + * If the refresh token has already been used (not in DB), it likely + * indicates a stolen token. Throw immediately. + */ + async refresh(dto: RefreshTokenDto): Promise { + const tokenHash = this.hashToken(dto.refreshToken); + + const record = await this.refreshTokenRepo.findOne({ + where: { tokenHash }, + relations: ['user'], + }); + + if (!record) { + throw new UnauthorizedException( + 'Refresh token is invalid. Please log in again.', + ); + } + + if (record.expiresAt < new Date()) { + // Clean up the expired record + await this.refreshTokenRepo.delete(record.id); + throw new UnauthorizedException( + 'Refresh token has expired. Please log in again.', + ); + } + + if (!record.user.isActive) { + throw new UnauthorizedException('Your account has been deactivated.'); + } + + // Rotate: delete the old token, issue a new pair + await this.refreshTokenRepo.delete(record.id); + return this.issueTokenPair(record.userId, record.user.role); + } + + /** + * POST /auth/logout — Invalidate a specific session. + * + * Deletes the refresh token from DB — the client's access token will + * expire on its own within 15 minutes (acceptable window for stateless JWTs). + * For immediate access token revocation, a token blacklist (Redis) is needed. + */ + async logout(dto: RefreshTokenDto): Promise<{ message: string }> { + const tokenHash = this.hashToken(dto.refreshToken); + await this.refreshTokenRepo.delete({ tokenHash }); + return { message: 'Logged out successfully.' }; + } +} diff --git a/bmvBackend/src/auth/dto/login.dto.ts b/bmvBackend/src/auth/dto/login.dto.ts new file mode 100644 index 000000000..8956da784 --- /dev/null +++ b/bmvBackend/src/auth/dto/login.dto.ts @@ -0,0 +1,12 @@ +import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator'; + +export class LoginDto { + @IsEmail({}, { message: 'email must be a valid email address' }) + @IsNotEmpty() + email: string; + + @IsString() + @IsNotEmpty() + @MinLength(8, { message: 'password must be at least 8 characters' }) + password: string; +} diff --git a/bmvBackend/src/auth/dto/refresh-token.dto.ts b/bmvBackend/src/auth/dto/refresh-token.dto.ts new file mode 100644 index 000000000..2f0584001 --- /dev/null +++ b/bmvBackend/src/auth/dto/refresh-token.dto.ts @@ -0,0 +1,7 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class RefreshTokenDto { + @IsString() + @IsNotEmpty({ message: 'refreshToken is required.' }) + refreshToken: string; +} diff --git a/bmvBackend/src/auth/dto/send-otp.dto.ts b/bmvBackend/src/auth/dto/send-otp.dto.ts new file mode 100644 index 000000000..743497487 --- /dev/null +++ b/bmvBackend/src/auth/dto/send-otp.dto.ts @@ -0,0 +1,10 @@ +import { IsNotEmpty, IsString, Matches } from 'class-validator'; + +export class SendOtpDto { + @IsString() + @IsNotEmpty() + @Matches(/^\+?[1-9]\d{6,14}$/, { + message: 'phone must be a valid mobile number (7–15 digits, optional leading +)', + }) + phone: string; +} diff --git a/bmvBackend/src/auth/dto/verify-otp.dto.ts b/bmvBackend/src/auth/dto/verify-otp.dto.ts new file mode 100644 index 000000000..6893b4348 --- /dev/null +++ b/bmvBackend/src/auth/dto/verify-otp.dto.ts @@ -0,0 +1,16 @@ +import { IsNotEmpty, IsString, Length, Matches } from 'class-validator'; + +export class VerifyOtpDto { + @IsString() + @IsNotEmpty() + @Matches(/^\+?[1-9]\d{6,14}$/, { + message: 'phone must be a valid mobile number', + }) + phone: string; + + @IsString() + @IsNotEmpty() + @Length(6, 6, { message: 'otp must be exactly 6 digits' }) + @Matches(/^\d{6}$/, { message: 'otp must contain only digits' }) + otp: string; +} diff --git a/bmvBackend/src/auth/entities/phone-otp.entity.ts b/bmvBackend/src/auth/entities/phone-otp.entity.ts new file mode 100644 index 000000000..42fb219d2 --- /dev/null +++ b/bmvBackend/src/auth/entities/phone-otp.entity.ts @@ -0,0 +1,27 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, +} from 'typeorm'; + +@Entity('phone_otps') +export class PhoneOtp { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ type: 'varchar', length: 15 }) + phone: string; + + @Column({ name: 'otp_code', type: 'varchar', length: 6 }) + otpCode: string; + + @Column({ name: 'expires_at', type: 'timestamp' }) + expiresAt: Date; + + @Column({ name: 'is_used', type: 'boolean', default: false }) + isUsed: boolean; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; +} diff --git a/bmvBackend/src/auth/entities/refresh-token.entity.ts b/bmvBackend/src/auth/entities/refresh-token.entity.ts new file mode 100644 index 000000000..84d3c237e --- /dev/null +++ b/bmvBackend/src/auth/entities/refresh-token.entity.ts @@ -0,0 +1,37 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + ManyToOne, + JoinColumn, +} from 'typeorm'; +import { User } from '../../users/entities/user.entity'; + +@Entity('refresh_tokens') +export class RefreshToken { + @PrimaryGeneratedColumn('uuid') + id: string; + + /** + * SHA-256 hash of the raw 256-bit token. + * We NEVER store the plaintext token — only its hash. + * If the DB is compromised, stolen hashes are useless without the raw values. + */ + @Column({ name: 'token_hash', type: 'varchar', length: 64, unique: true }) + tokenHash: string; + + @Column({ name: 'user_id', type: 'uuid' }) + userId: string; + + /** Cascade-delete all tokens when the user is removed. */ + @ManyToOne(() => User, { onDelete: 'CASCADE', eager: false }) + @JoinColumn({ name: 'user_id' }) + user: User; + + @Column({ name: 'expires_at', type: 'timestamp' }) + expiresAt: Date; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; +} diff --git a/bmvBackend/src/auth/otp.util.ts b/bmvBackend/src/auth/otp.util.ts new file mode 100644 index 000000000..1136b9c0e --- /dev/null +++ b/bmvBackend/src/auth/otp.util.ts @@ -0,0 +1,7 @@ +/** + * Generates a cryptographically random 6-digit OTP string. + */ +import { randomInt } from 'crypto'; +export function generateOtp(): string { + return randomInt(100000, 1000000).toString(); +} diff --git a/bmvBackend/src/cloudinary/cloudinary.module.ts b/bmvBackend/src/cloudinary/cloudinary.module.ts new file mode 100644 index 000000000..1d5eca498 --- /dev/null +++ b/bmvBackend/src/cloudinary/cloudinary.module.ts @@ -0,0 +1,12 @@ +import { Global, Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { CloudinaryProvider } from './cloudinary.provider'; +import { CloudinaryService } from './cloudinary.service'; + +@Global() +@Module({ + imports: [ConfigModule], + providers: [CloudinaryProvider, CloudinaryService], + exports: [CloudinaryService], +}) +export class CloudinaryModule {} diff --git a/bmvBackend/src/cloudinary/cloudinary.provider.ts b/bmvBackend/src/cloudinary/cloudinary.provider.ts new file mode 100644 index 000000000..2975ad668 --- /dev/null +++ b/bmvBackend/src/cloudinary/cloudinary.provider.ts @@ -0,0 +1,16 @@ +import { v2 as cloudinary } from 'cloudinary'; +import { ConfigService } from '@nestjs/config'; + +export const CLOUDINARY = 'CLOUDINARY'; + +export const CloudinaryProvider = { + provide: CLOUDINARY, + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + return cloudinary.config({ + cloud_name: configService.get('CLOUDINARY_CLOUD_NAME'), + api_key: configService.get('CLOUDINARY_API_KEY'), + api_secret: configService.get('CLOUDINARY_API_SECRET'), + }); + }, +}; diff --git a/bmvBackend/src/cloudinary/cloudinary.service.ts b/bmvBackend/src/cloudinary/cloudinary.service.ts new file mode 100644 index 000000000..7e1f1c13e --- /dev/null +++ b/bmvBackend/src/cloudinary/cloudinary.service.ts @@ -0,0 +1,85 @@ +import { Injectable, BadRequestException } from '@nestjs/common'; +import { v2 as cloudinary, UploadApiResponse } from 'cloudinary'; +import { Readable } from 'stream'; + +export interface CloudinaryUploadResult { + /** Cloudinary public_id — store this as the "fileKey" for later deletion. */ + publicId: string; + /** Fully-qualified secure URL to the asset. */ + secureUrl: string; +} + +@Injectable() +export class CloudinaryService { + /** + * Uploads an image buffer to Cloudinary under + * `venues/{venueId}/images` and transforms it to WebP. + */ + async uploadImage( + buffer: Buffer, + venueId: string, + ): Promise { + return this.uploadBuffer(buffer, { + folder: `venues/${venueId}/images`, + resource_type: 'image', + format: 'webp', + quality: 'auto:good', + }); + } + + /** + * Uploads a document buffer (PDF, etc.) to Cloudinary under + * `venues/{venueId}/documents` as a raw resource. + */ + async uploadDocument( + buffer: Buffer, + venueId: string, + ): Promise { + return this.uploadBuffer(buffer, { + folder: `venues/${venueId}/documents`, + resource_type: 'raw', + }); + } + + /** + * Deletes an image asset from Cloudinary by its public_id. + */ + async deleteImage(publicId: string): Promise { + await cloudinary.uploader.destroy(publicId, { resource_type: 'image' }); + } + + /** + * Deletes a raw (document) asset from Cloudinary by its public_id. + */ + async deleteDocument(publicId: string): Promise { + await cloudinary.uploader.destroy(publicId, { resource_type: 'raw' }); + } + + // ─── Private Helpers ─────────────────────────────────────────────────────── + + private uploadBuffer( + buffer: Buffer, + options: Record, + ): Promise { + return new Promise((resolve, reject) => { + const uploadStream = cloudinary.uploader.upload_stream( + options, + (error, result) => { + if (error || !result) { + return reject( + new BadRequestException( + error?.message ?? 'Cloudinary upload failed.', + ), + ); + } + resolve({ + publicId: result.public_id, + secureUrl: result.secure_url, + }); + }, + ); + + Readable.from(buffer).pipe(uploadStream); + }); + } +} diff --git a/bmvBackend/src/common/decorators/current-user.decorator.ts b/bmvBackend/src/common/decorators/current-user.decorator.ts new file mode 100644 index 000000000..7c5279086 --- /dev/null +++ b/bmvBackend/src/common/decorators/current-user.decorator.ts @@ -0,0 +1,25 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; +import { UserRole } from '../enums/user-role.enum'; + +export interface CurrentUserPayload { + sub: string; // user UUID + role: UserRole; +} + +/** + * Extracts the authenticated user payload from the JWT token. + * Requires JwtAuthGuard to be applied first. + * + * @example + * @Get('/me') + * @UseGuards(JwtAuthGuard) + * getProfile(@CurrentUser() user: CurrentUserPayload) { + * return user.sub; + * } + */ +export const CurrentUser = createParamDecorator( + (_data: unknown, ctx: ExecutionContext): CurrentUserPayload => { + const request = ctx.switchToHttp().getRequest(); + return request.user as CurrentUserPayload; + }, +); diff --git a/bmvBackend/src/common/decorators/roles.decorator.ts b/bmvBackend/src/common/decorators/roles.decorator.ts new file mode 100644 index 000000000..11112d3bb --- /dev/null +++ b/bmvBackend/src/common/decorators/roles.decorator.ts @@ -0,0 +1,14 @@ +import { SetMetadata } from '@nestjs/common'; +import { UserRole } from '../enums/user-role.enum'; + +export const ROLES_KEY = 'roles'; + +/** + * Attach allowed roles to a route handler. + * Used together with RolesGuard. + * + * @example + * @Roles(UserRole.VENUE_OWNER) + * @Post('/venues') + */ +export const Roles = (...roles: UserRole[]) => SetMetadata(ROLES_KEY, roles); diff --git a/bmvBackend/src/common/enums/booking-type.enum.ts b/bmvBackend/src/common/enums/booking-type.enum.ts new file mode 100644 index 000000000..c5c64f5e1 --- /dev/null +++ b/bmvBackend/src/common/enums/booking-type.enum.ts @@ -0,0 +1,9 @@ +/** + * How the venue accepts bookings. + * FULL_DAY — entire day is booked as one unit + * TIME_SLOT — owner defines specific time slots (morning / evening etc.) + */ +export enum BookingType { + FULL_DAY = 'FULL_DAY', + TIME_SLOT = 'TIME_SLOT', +} diff --git a/bmvBackend/src/common/enums/document-status.enum.ts b/bmvBackend/src/common/enums/document-status.enum.ts new file mode 100644 index 000000000..a16a5c725 --- /dev/null +++ b/bmvBackend/src/common/enums/document-status.enum.ts @@ -0,0 +1,8 @@ +/** + * Admin verification status of an individual document. + */ +export enum DocumentStatus { + PENDING = 'PENDING', + APPROVED = 'APPROVED', + REJECTED = 'REJECTED', +} diff --git a/bmvBackend/src/common/enums/document-type.enum.ts b/bmvBackend/src/common/enums/document-type.enum.ts new file mode 100644 index 000000000..31614e8ee --- /dev/null +++ b/bmvBackend/src/common/enums/document-type.enum.ts @@ -0,0 +1,11 @@ +/** + * Type of verification document uploaded by a venue owner. + */ +export enum DocumentType { + AADHAAR = 'AADHAAR', + PAN = 'PAN', + BUSINESS_LICENSE = 'BUSINESS_LICENSE', + PROPERTY_PROOF = 'PROPERTY_PROOF', + RENTAL_AGREEMENT = 'RENTAL_AGREEMENT', + OTHER = 'OTHER', +} diff --git a/bmvBackend/src/common/enums/image-type.enum.ts b/bmvBackend/src/common/enums/image-type.enum.ts new file mode 100644 index 000000000..dac966e22 --- /dev/null +++ b/bmvBackend/src/common/enums/image-type.enum.ts @@ -0,0 +1,11 @@ +/** + * Category of a venue image. + * Used to enforce minimum required photo coverage per venue. + */ +export enum ImageType { + COVER = 'COVER', + ENTRANCE = 'ENTRANCE', + HALL = 'HALL', + PARKING = 'PARKING', + OTHER = 'OTHER', +} diff --git a/bmvBackend/src/common/enums/user-role.enum.ts b/bmvBackend/src/common/enums/user-role.enum.ts new file mode 100644 index 000000000..28abaeec5 --- /dev/null +++ b/bmvBackend/src/common/enums/user-role.enum.ts @@ -0,0 +1,12 @@ +/** + * All possible roles a user can have in BookMyVenue. + * + * - customer → books venues + * - venue_owner → lists and manages venues + * - admin → platform administrator (created manually, no self-registration) + */ +export enum UserRole { + CUSTOMER = 'customer', + VENUE_OWNER = 'venue_owner', + ADMIN = 'admin', +} diff --git a/bmvBackend/src/common/enums/venue-status.enum.ts b/bmvBackend/src/common/enums/venue-status.enum.ts new file mode 100644 index 000000000..7f5d512d8 --- /dev/null +++ b/bmvBackend/src/common/enums/venue-status.enum.ts @@ -0,0 +1,17 @@ +/** + * Lifecycle status of a venue. + * + * State machine: + * DRAFT → PENDING_REVIEW → APPROVED + * → CHANGES_REQUESTED → (owner edits) → PENDING_REVIEW + * → REJECTED (terminal — contact support) + * APPROVED → SUSPENDED (admin action) + */ +export enum VenueStatus { + DRAFT = 'DRAFT', + PENDING_REVIEW = 'PENDING_REVIEW', + CHANGES_REQUESTED = 'CHANGES_REQUESTED', + APPROVED = 'APPROVED', + REJECTED = 'REJECTED', + SUSPENDED = 'SUSPENDED', +} diff --git a/bmvBackend/src/common/enums/venue-type.enum.ts b/bmvBackend/src/common/enums/venue-type.enum.ts new file mode 100644 index 000000000..5b6226a19 --- /dev/null +++ b/bmvBackend/src/common/enums/venue-type.enum.ts @@ -0,0 +1,15 @@ +/** + * All venue types supported on BookMyVenue. + * Values are normalized to UPPER_SNAKE_CASE for consistent DB storage. + */ +export enum VenueType { + WEDDING_HALL = 'WEDDING_HALL', + AUDITORIUM = 'AUDITORIUM', + RESORT = 'RESORT', + CONVENTION_CENTER = 'CONVENTION_CENTER', + CAFE = 'CAFE', + PARTY_HALL = 'PARTY_HALL', + MEETUP_SPACE = 'MEETUP_SPACE', + MALL = 'MALL', + HOTEL = 'HOTEL', +} diff --git a/bmvBackend/src/common/enums/verification-status.enum.ts b/bmvBackend/src/common/enums/verification-status.enum.ts new file mode 100644 index 000000000..61ff65dc0 --- /dev/null +++ b/bmvBackend/src/common/enums/verification-status.enum.ts @@ -0,0 +1,10 @@ +/** + * Status of a venue verification request submission. + * Each submission is a separate row — history is fully preserved. + */ +export enum VerificationStatus { + PENDING = 'PENDING', + APPROVED = 'APPROVED', + REJECTED = 'REJECTED', + CHANGES_REQUESTED = 'CHANGES_REQUESTED', +} diff --git a/bmvBackend/src/common/guards/jwt-auth.guard.ts b/bmvBackend/src/common/guards/jwt-auth.guard.ts new file mode 100644 index 000000000..905db8976 --- /dev/null +++ b/bmvBackend/src/common/guards/jwt-auth.guard.ts @@ -0,0 +1,30 @@ +import { + Injectable, + ExecutionContext, + UnauthorizedException, +} from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +/** + * Validates the JWT Bearer token from the Authorization header. + * On success, attaches { sub, role } to req.user. + * On failure, throws 401 Unauthorized. + * + * Usage: @UseGuards(JwtAuthGuard) + */ +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') { + handleRequest(err: Error, user: T): T { + if (err || !user) { + throw new UnauthorizedException( + 'Access denied. Please log in to continue.', + ); + } + return user; + } + + // Make context available for logging / testing + canActivate(context: ExecutionContext) { + return super.canActivate(context); + } +} diff --git a/bmvBackend/src/common/guards/roles.guard.ts b/bmvBackend/src/common/guards/roles.guard.ts new file mode 100644 index 000000000..adf3867a3 --- /dev/null +++ b/bmvBackend/src/common/guards/roles.guard.ts @@ -0,0 +1,40 @@ +import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { ROLES_KEY } from '../decorators/roles.decorator'; +import { UserRole } from '../enums/user-role.enum'; +import { CurrentUserPayload } from '../decorators/current-user.decorator'; + +/** + * Checks that the authenticated user's role matches one of the roles + * specified by the @Roles() decorator on the route handler. + * + * Must be used after JwtAuthGuard (requires req.user to be populated). + * + * If no @Roles() decorator is present, the route is allowed for all authenticated users. + */ +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private readonly reflector: Reflector) { } + + canActivate(context: ExecutionContext): boolean { + const requiredRoles = this.reflector.getAllAndOverride( + ROLES_KEY, + [context.getHandler(), context.getClass()], + ); + + // No @Roles() decorator — allow all authenticated users + if (!requiredRoles || requiredRoles.length === 0) { + return true; + } + + const user = context.switchToHttp().getRequest().user as CurrentUserPayload; + // if (user.role === UserRole.ADMIN) return true; + if (!requiredRoles.includes(user.role)) { + throw new ForbiddenException( + `This action requires one of the following roles: ${requiredRoles.join(', ')}.`, + ); + } + + return true; + } +} diff --git a/bmvBackend/src/common/guards/venue-owner.guard.ts b/bmvBackend/src/common/guards/venue-owner.guard.ts new file mode 100644 index 000000000..69c138bcd --- /dev/null +++ b/bmvBackend/src/common/guards/venue-owner.guard.ts @@ -0,0 +1,62 @@ +import { + Injectable, + CanActivate, + ExecutionContext, + ForbiddenException, + NotFoundException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Venue } from '../../venues/entities/venue.entity'; +import { CurrentUserPayload } from '../decorators/current-user.decorator'; +import { UserRole } from '../enums/user-role.enum'; + +/** + * Verifies that the authenticated user owns the venue identified by :venueId + * in the route params. + * + * Must be applied after JwtAuthGuard. + * Attaches the loaded venue to req.venue for downstream use in the controller. + */ +@Injectable() +export class VenueOwnerGuard implements CanActivate { + constructor( + @InjectRepository(Venue) + private readonly venueRepo: Repository, + ) { } + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const user = request.user as CurrentUserPayload; + console.log("venueOwner", user.sub); + const venueId: string = request.params.venueId; + + if (!venueId) { + throw new ForbiddenException('Venue ID is required.'); + } + + const venue = await this.venueRepo.findOne({ where: { id: venueId } }); + + if (!venue) { + throw new NotFoundException(`Venue with id ${venueId} not found.`); + } + // if (user.role === UserRole.ADMIN) { + // request.venue = venue; + // return true; + // } + if (venue.ownerId !== user.sub) { + console.log(venue.ownerId); + console.log(user.sub); + + + throw new ForbiddenException( + 'You do not have permission to access this venue.', + ); + } + + // Attach to request so controllers can access it without re-fetching + request.venue = venue; + + return true; + } +} diff --git a/bmvBackend/src/common/strategies/jwt.strategy.ts b/bmvBackend/src/common/strategies/jwt.strategy.ts new file mode 100644 index 000000000..2e6efe75d --- /dev/null +++ b/bmvBackend/src/common/strategies/jwt.strategy.ts @@ -0,0 +1,39 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { ConfigService } from '@nestjs/config'; +import { UserRole } from '../enums/user-role.enum'; + +interface JwtPayload { + sub: string; + role: UserRole; + purpose: string; +} + +/** + * Passport JWT strategy — validates the Bearer token in every guarded request. + * Extracts the JWT from Authorization: Bearer header. + * Returns { sub, role } which is attached to req.user by Passport. + * + * Rejects tokens whose `purpose` is not 'access' (e.g. phoneVerifiedToken). + */ +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(private readonly configService: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configService.get('JWT_SECRET')!, + }); + } + + validate(payload: JwtPayload): { sub: string; role: UserRole } { + if (payload.purpose !== 'access') { + throw new UnauthorizedException( + 'Invalid token type. Please use an access token.', + ); + } + + return { sub: payload.sub, role: payload.role }; + } +} diff --git a/bmvBackend/src/config/config.module.ts b/bmvBackend/src/config/config.module.ts new file mode 100644 index 000000000..3142f949a --- /dev/null +++ b/bmvBackend/src/config/config.module.ts @@ -0,0 +1,2 @@ +// ConfigModule is registered globally in AppModule via ConfigModule.forRoot({ isGlobal: true }). +// No custom wrapper needed — ConfigService can be injected anywhere without re-importing ConfigModule. diff --git a/bmvBackend/src/config/env.config.ts b/bmvBackend/src/config/env.config.ts new file mode 100644 index 000000000..3142f949a --- /dev/null +++ b/bmvBackend/src/config/env.config.ts @@ -0,0 +1,2 @@ +// ConfigModule is registered globally in AppModule via ConfigModule.forRoot({ isGlobal: true }). +// No custom wrapper needed — ConfigService can be injected anywhere without re-importing ConfigModule. diff --git a/bmvBackend/src/db/db.module.ts b/bmvBackend/src/db/db.module.ts new file mode 100644 index 000000000..9c8e69011 --- /dev/null +++ b/bmvBackend/src/db/db.module.ts @@ -0,0 +1,30 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ConfigModule, ConfigService } from '@nestjs/config'; + +@Module({ + imports: [ + TypeOrmModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + + useFactory: (config: ConfigService) => ({ + type: 'postgres', + host: config.get('DB_HOST'), + port: config.get('DB_PORT'), + username: config.get('DB_USER'), + password: 'postgres@123', + + // config.get('DB_PASS'), + database: config.get('DB_NAME'), + + autoLoadEntities: true, + synchronize: config.get('NODE_ENV') !== 'production', // auto-sync in dev, off in prod + logging: config.get('NODE_ENV') === 'development', + + + }), + }), + ], +}) +export class DbModule { } \ No newline at end of file diff --git a/bmvBackend/src/db/seeds/facilities.sql b/bmvBackend/src/db/seeds/facilities.sql new file mode 100644 index 000000000..81366da82 --- /dev/null +++ b/bmvBackend/src/db/seeds/facilities.sql @@ -0,0 +1,21 @@ +-- ============================================================ +-- BookMyVenue — Facilities Seed +-- ============================================================ +-- Run ONCE after database creation: +-- psql -d bookmyvenue -f src/db/seeds/facilities.sql +-- +-- Safe to re-run — ON CONFLICT DO NOTHING prevents duplicates. +-- ============================================================ + +INSERT INTO facilities (id, name, icon, created_at) VALUES + (gen_random_uuid(), 'AC', 'snowflake', NOW()), + (gen_random_uuid(), 'WiFi', 'wifi', NOW()), + (gen_random_uuid(), 'Generator Backup', 'zap', NOW()), + (gen_random_uuid(), 'Stage', 'theater', NOW()), + (gen_random_uuid(), 'Projector', 'monitor', NOW()), + (gen_random_uuid(), 'Catering', 'utensils', NOW()), + (gen_random_uuid(), 'Sound System', 'speaker', NOW()), + (gen_random_uuid(), 'Bridal Room', 'heart', NOW()), + (gen_random_uuid(), 'Accommodation', 'bed', NOW()), + (gen_random_uuid(), 'Wheelchair Access', 'accessibility', NOW()) +ON CONFLICT (name) DO NOTHING; diff --git a/bmvBackend/src/main.ts b/bmvBackend/src/main.ts new file mode 100644 index 000000000..fcf05d336 --- /dev/null +++ b/bmvBackend/src/main.ts @@ -0,0 +1,19 @@ +import { NestFactory } from '@nestjs/core'; +import { ValidationPipe } from '@nestjs/common'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + // Enforce DTO validation globally across all endpoints + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, // Strip properties not in the DTO + // forbidNonWhitelisted: true, // Throw 400 if unknown properties are sent + transform: true, // Auto-transform payloads to DTO class instances + }), + ); + + await app.listen(process.env.PORT ?? 3000); +} +bootstrap(); diff --git a/bmvBackend/src/users/dto/onboard-customer.dto.ts b/bmvBackend/src/users/dto/onboard-customer.dto.ts new file mode 100644 index 000000000..074611b5e --- /dev/null +++ b/bmvBackend/src/users/dto/onboard-customer.dto.ts @@ -0,0 +1,32 @@ +import { IsNotEmpty, IsOptional, IsString, Length, IsUrl } from 'class-validator'; + +export class OnboardCustomerDto { + @IsNotEmpty() + @IsString() + @Length(1, 255) + addressLine1: string; + + @IsOptional() + @IsString() + @Length(1, 255) + addressLine2?: string; + + @IsNotEmpty() + @IsString() + @Length(1, 100) + city: string; + + @IsNotEmpty() + @IsString() + @Length(1, 100) + state: string; + + @IsNotEmpty() + @IsString() + @Length(1, 20) + pincode: string; + + @IsString() + @IsUrl({}, { message: 'googleLocationUrl must be a valid URL' }) + googleLocationUrl?: string; +} diff --git a/bmvBackend/src/users/dto/register-user.dto.ts b/bmvBackend/src/users/dto/register-user.dto.ts new file mode 100644 index 000000000..929bab32f --- /dev/null +++ b/bmvBackend/src/users/dto/register-user.dto.ts @@ -0,0 +1,42 @@ +import { + IsEmail, + IsEnum, + IsNotEmpty, + IsString, + Matches, + MaxLength, + MinLength, +} from 'class-validator'; +import { UserRole } from '../../common/enums/user-role.enum'; + +export class RegisterUserDto { + + @MaxLength(255) + @IsString() + @IsNotEmpty() + name: string; + + @IsString() + @IsNotEmpty() + @Matches(/^\+?[1-9]\d{6,14}$/, { + message: 'phone must be a valid mobile number', + }) + phone: string; + + @IsEmail({}, { message: 'email must be a valid email address' }) + @IsNotEmpty() + email: string; + + @IsString() + @IsNotEmpty() + @MinLength(8, { message: 'password must be at least 8 characters' }) + password: string; + + @IsString() + @IsNotEmpty({ message: 'Please verify your phone number first.' }) + phoneVerifiedToken: string; + + @IsEnum(UserRole, { message: 'role must be customer or venue_owner' }) + @IsNotEmpty() + role: UserRole; +} diff --git a/bmvBackend/src/users/entities/customer-profile.entity.ts b/bmvBackend/src/users/entities/customer-profile.entity.ts new file mode 100644 index 000000000..0655416b3 --- /dev/null +++ b/bmvBackend/src/users/entities/customer-profile.entity.ts @@ -0,0 +1,47 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + OneToOne, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { User } from './user.entity'; + +@Entity('customer_profiles') +export class CustomerProfile { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'user_id', type: 'uuid', unique: true }) + userId: string; + + @OneToOne(() => User, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'user_id' }) + user: User; + + @Column({ name: 'address_line_1', type: 'varchar', length: 255 }) + addressLine1: string; + + @Column({ name: 'address_line_2', type: 'varchar', length: 255, nullable: true }) + addressLine2?: string; + + @Column({ type: 'varchar', length: 100 }) + city: string; + + @Column({ type: 'varchar', length: 100 }) + state: string; + + @Column({ type: 'varchar', length: 20 }) + pincode: string; + + @Column({ name: 'google_location_url', type: 'text', nullable: true }) + googleLocationUrl?: string; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; +} diff --git a/bmvBackend/src/users/entities/user.entity.ts b/bmvBackend/src/users/entities/user.entity.ts new file mode 100644 index 000000000..e77450dc2 --- /dev/null +++ b/bmvBackend/src/users/entities/user.entity.ts @@ -0,0 +1,59 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { UserRole } from '../../common/enums/user-role.enum'; + +@Entity('users') +export class User { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ type: 'varchar', length: 255 }) + name: string; + + @Column({ type: 'varchar', length: 15, unique: true }) + phone: string; + + @Column({ name: 'phone_verified', type: 'boolean', default: false }) + phoneVerified: boolean; + + @Column({ type: 'varchar', length: 255, unique: true }) + email: string; + + @Column({ name: 'password_hash', type: 'text' }) + passwordHash: string; + + /** + * Role determines which dashboard & onboarding the user sees. + * - customer → books venues + * - venue_owner → lists and manages venues + * - admin → platform administrator (assigned manually) + */ + @Column({ + type: 'enum', + enum: UserRole, + default: UserRole.CUSTOMER, + }) + role: UserRole; + + /** + * Set to true once the user completes the role-specific onboarding + * (customer profile or venue owner profile). + * Frontend uses this to redirect to the onboarding screen if false. + */ + @Column({ name: 'is_profile_completed', type: 'boolean', default: false }) + isProfileCompleted: boolean; + + @Column({ name: 'is_active', type: 'boolean', default: true }) + isActive: boolean; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; +} diff --git a/bmvBackend/src/users/users.controller.spec.ts b/bmvBackend/src/users/users.controller.spec.ts new file mode 100644 index 000000000..3e27c395d --- /dev/null +++ b/bmvBackend/src/users/users.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersController } from './users.controller'; + +describe('UsersController', () => { + let controller: UsersController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [UsersController], + }).compile(); + + controller = module.get(UsersController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/bmvBackend/src/users/users.controller.ts b/bmvBackend/src/users/users.controller.ts new file mode 100644 index 000000000..a92dc8779 --- /dev/null +++ b/bmvBackend/src/users/users.controller.ts @@ -0,0 +1,39 @@ +import { Body, Controller, HttpCode, HttpStatus, Post, UseGuards } from '@nestjs/common'; +import { UsersService } from './users.service'; +import { RegisterUserDto } from './dto/register-user.dto'; +import { OnboardCustomerDto } from './dto/onboard-customer.dto'; +import { JwtAuthGuard } from '../common/guards/jwt-auth.guard'; +import { RolesGuard } from '../common/guards/roles.guard'; +import { Roles } from '../common/decorators/roles.decorator'; +import { UserRole } from '../common/enums/user-role.enum'; +import { CurrentUser, CurrentUserPayload } from '../common/decorators/current-user.decorator'; + +@Controller('users') +export class UsersController { + constructor(private readonly usersService: UsersService) { } + + /** + * POST /users/register + * Step 3: Register a new user after OTP verification. + */ + @Post('register') + @HttpCode(HttpStatus.CREATED) + register(@Body() dto: RegisterUserDto) { + return this.usersService.register(dto); + } + + /** + * POST /users/customer/onboard + * Onboard a registered customer with location details. + */ + @Post('customer/onboard') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(UserRole.CUSTOMER) + @HttpCode(HttpStatus.OK) + onboardCustomer( + @Body() dto: OnboardCustomerDto, + @CurrentUser() user: CurrentUserPayload, + ) { + return this.usersService.onboardCustomer(user.sub, dto); + } +} diff --git a/bmvBackend/src/users/users.module.ts b/bmvBackend/src/users/users.module.ts new file mode 100644 index 000000000..4709b9acb --- /dev/null +++ b/bmvBackend/src/users/users.module.ts @@ -0,0 +1,17 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; +import { User } from './entities/user.entity'; +import { CustomerProfile } from './entities/customer-profile.entity'; +import { AuthModule } from '../auth/auth.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([User, CustomerProfile]), + AuthModule, // brings in JwtModule so UsersService can verify the phoneVerifiedToken + ], + controllers: [UsersController], + providers: [UsersService], +}) +export class UsersModule {} diff --git a/bmvBackend/src/users/users.service.spec.ts b/bmvBackend/src/users/users.service.spec.ts new file mode 100644 index 000000000..62815ba64 --- /dev/null +++ b/bmvBackend/src/users/users.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { UsersService } from './users.service'; + +describe('UsersService', () => { + let service: UsersService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [UsersService], + }).compile(); + + service = module.get(UsersService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/bmvBackend/src/users/users.service.ts b/bmvBackend/src/users/users.service.ts new file mode 100644 index 000000000..ab2c06b21 --- /dev/null +++ b/bmvBackend/src/users/users.service.ts @@ -0,0 +1,176 @@ +import { + ConflictException, + Injectable, + UnauthorizedException, + BadRequestException, + NotFoundException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import * as bcrypt from 'bcrypt'; +import { JwtService } from '@nestjs/jwt'; +import { User } from './entities/user.entity'; +import { CustomerProfile } from './entities/customer-profile.entity'; +import { RegisterUserDto } from './dto/register-user.dto'; +import { OnboardCustomerDto } from './dto/onboard-customer.dto'; +import { UserRole } from '../common/enums/user-role.enum'; +import { AuthService, TokenPair } from '../auth/auth.service'; + +const SALT_ROUNDS = 12; + +@Injectable() +export class UsersService { + constructor( + @InjectRepository(User) + private readonly userRepo: Repository, + @InjectRepository(CustomerProfile) + private readonly customerProfileRepo: Repository, + private readonly jwtService: JwtService, // used to verify phoneVerifiedToken + private readonly authService: AuthService, // used to issue token pair after registration + ) { } + + /** + * Step 3 — Register a new user after their phone has been OTP-verified. + * + * The caller must supply the `phoneVerifiedToken` returned by POST /auth/verify-otp. + * We decode it to confirm: + * 1. The token is valid & not expired (within 10-minute window). + * 2. The phone in the token matches the phone in the registration body. + * + * On success returns a full token pair (accessToken + refreshToken) so the + * user is immediately authenticated — no extra login round-trip needed. + * + * Throws 401 if the token is invalid/expired or phone mismatch. + * Throws 401 if someone tries to self-register as admin. + * Throws 409 if the phone or email is already registered. + */ + async register( + dto: RegisterUserDto, + ): Promise< + TokenPair & { + user: { + id: string; + name: string; + phone: string; + email: string; + role: UserRole; + isProfileCompleted: boolean; + }; + } + > { + const { name, phone, email, password, phoneVerifiedToken, role } = dto; + + // ── Step 1: Block admin self-registration ───────────────────────────── + if (role === UserRole.ADMIN) { + throw new UnauthorizedException( + 'Admin accounts cannot be self-registered.', + ); + } + + // ── Step 2: Verify the phoneVerifiedToken from the frontend ─────────── + let tokenPayload: { phone: string; purpose: string }; + try { + tokenPayload = this.jwtService.verify<{ phone: string; purpose: string }>( + phoneVerifiedToken, + ); + } catch { + throw new UnauthorizedException( + 'Phone verification token is invalid or has expired. Please verify your phone number again.', + ); + } + + if ( + tokenPayload.purpose !== 'phone_verification' || + tokenPayload.phone !== phone + ) { + throw new UnauthorizedException( + 'Phone verification token does not match the provided phone number.', + ); + } + + // ── Step 3: Check for duplicate accounts ───────────────────────────── + const existingPhone = await this.userRepo.findOne({ where: { phone } }); + if (existingPhone) { + throw new ConflictException( + 'An account with this phone number already exists.', + ); + } + + const existingEmail = await this.userRepo.findOne({ where: { email } }); + if (existingEmail) { + throw new ConflictException( + 'An account with this email address already exists.', + ); + } + + // ── Step 4: Create the user ─────────────────────────────────────────── + const passwordHash = await bcrypt.hash(password, SALT_ROUNDS); + + const user = this.userRepo.create({ + name, + phone, + email, + passwordHash, + role, // 'customer' or 'venue_owner' + phoneVerified: true, // Confirmed by the phoneVerifiedToken above + isProfileCompleted: false, + isActive: true, + }); + + const saved = await this.userRepo.save(user); + + // ── Step 5: Issue token pair — user is authenticated immediately ────── + const tokens = await this.authService.issueTokenPair(saved.id, saved.role); + + return { + ...tokens, + user: { + id: saved.id, + name: saved.name, + phone: saved.phone, + email: saved.email, + role: saved.role, + isProfileCompleted: saved.isProfileCompleted, + }, + }; + } + + /** + * Onboard a customer by saving their location details and setting `isProfileCompleted` to true. + */ + async onboardCustomer( + userId: string, + dto: OnboardCustomerDto, + ): Promise<{ message: string; isProfileCompleted: boolean; profile: CustomerProfile }> { + const user = await this.userRepo.findOne({ where: { id: userId } }); + if (!user) { + throw new NotFoundException('User not found.'); + } + if (user.role !== UserRole.CUSTOMER) { + throw new BadRequestException('Only users with role CUSTOMER can complete customer onboarding.'); + } + + let profile = await this.customerProfileRepo.findOne({ where: { userId } }); + if (!profile) { + profile = this.customerProfileRepo.create({ userId }); + } + + profile.addressLine1 = dto.addressLine1; + profile.addressLine2 = dto.addressLine2; + profile.city = dto.city; + profile.state = dto.state; + profile.pincode = dto.pincode; + profile.googleLocationUrl = dto.googleLocationUrl; + + const savedProfile = await this.customerProfileRepo.save(profile); + + user.isProfileCompleted = true; + await this.userRepo.save(user); + + return { + message: 'Customer onboarding completed successfully.', + isProfileCompleted: true, + profile: savedProfile, + }; + } +} diff --git a/bmvBackend/src/venues/admin-venue/admin-venue.controller.spec.ts b/bmvBackend/src/venues/admin-venue/admin-venue.controller.spec.ts new file mode 100644 index 000000000..c18f694ab --- /dev/null +++ b/bmvBackend/src/venues/admin-venue/admin-venue.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AdminVenueController } from './admin-venue.controller'; + +describe('AdminVenueController', () => { + let controller: AdminVenueController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [AdminVenueController], + }).compile(); + + controller = module.get(AdminVenueController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/bmvBackend/src/venues/admin-venue/admin-venue.controller.ts b/bmvBackend/src/venues/admin-venue/admin-venue.controller.ts new file mode 100644 index 000000000..973d25874 --- /dev/null +++ b/bmvBackend/src/venues/admin-venue/admin-venue.controller.ts @@ -0,0 +1,37 @@ +import { Controller, HttpStatus, Post, UseGuards, Get, Body } from '@nestjs/common'; +import { RolesGuard } from 'src/common/guards/roles.guard'; +import { JwtAuthGuard } from 'src/common/guards/jwt-auth.guard'; +import { HttpCode } from '@nestjs/common'; +import { AdminVenueService } from './admin-venue.service'; +import { Roles } from 'src/common/decorators/roles.decorator'; +import { UserRole } from 'src/common/enums/user-role.enum'; +import { CurrentUser, CurrentUserPayload } from 'src/common/decorators/current-user.decorator'; +import { Param } from '@nestjs/common'; +import { ParseUUIDPipe } from '@nestjs/common'; +@Controller() +@UseGuards(JwtAuthGuard, RolesGuard) +export class AdminVenueController { + constructor(private readonly adminVenue: AdminVenueService) { } + @Get('venues/pending') + + @Roles(UserRole.ADMIN) + getPendingRequest(@CurrentUser() user: CurrentUserPayload) { + return this.adminVenue.PendingVenues(); + } + @Get('venues/:venueId') + @Roles(UserRole.ADMIN) + getPendingDetail( + @Param('venueId', ParseUUIDPipe) venueId: string, + @CurrentUser() user: CurrentUserPayload, + ) { + return this.adminVenue.PendingVenueDetails(venueId); + } + + @Post('venues/:requestId/accept') + @Roles(UserRole.ADMIN) + AcceptVerification(@Param('requestId', ParseUUIDPipe) requesttId: string, @Body() reviewNotes: string, + @CurrentUser() user: CurrentUserPayload,) { + return this.adminVenue.AcceptVerification(requesttId, reviewNotes) + } + +} diff --git a/bmvBackend/src/venues/admin-venue/admin-venue.module.ts b/bmvBackend/src/venues/admin-venue/admin-venue.module.ts new file mode 100644 index 000000000..18f9158ef --- /dev/null +++ b/bmvBackend/src/venues/admin-venue/admin-venue.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { AdminVenueController } from './admin-venue.controller'; +import { AdminVenueService } from './admin-venue.service'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ConfigModule } from '@nestjs/config'; +import { Venue } from '../entities/venue.entity'; +import { VenueVerificationRequest } from '../entities/venue-verification-request.entity'; +@Module({ + imports: [ + ConfigModule, TypeOrmModule.forFeature([ + Venue, + VenueVerificationRequest, + ]), + ], + controllers: [AdminVenueController], + providers: [AdminVenueService] +}) +export class AdminVenueModule { } diff --git a/bmvBackend/src/venues/admin-venue/admin-venue.service.spec.ts b/bmvBackend/src/venues/admin-venue/admin-venue.service.spec.ts new file mode 100644 index 000000000..c99239052 --- /dev/null +++ b/bmvBackend/src/venues/admin-venue/admin-venue.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AdminVenueService } from './admin-venue.service'; + +describe('AdminVenueService', () => { + let service: AdminVenueService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AdminVenueService], + }).compile(); + + service = module.get(AdminVenueService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/bmvBackend/src/venues/admin-venue/admin-venue.service.ts b/bmvBackend/src/venues/admin-venue/admin-venue.service.ts new file mode 100644 index 000000000..0d36fe839 --- /dev/null +++ b/bmvBackend/src/venues/admin-venue/admin-venue.service.ts @@ -0,0 +1,134 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Venue } from '../entities/venue.entity'; +import { VenueType } from 'src/common/enums/venue-type.enum'; +import { VenueStatus } from 'src/common/enums/venue-status.enum'; +import { NotFoundException } from '@nestjs/common'; +import { VenueVerificationRequest } from '../entities/venue-verification-request.entity'; +import { VerificationStatus } from 'src/common/enums/verification-status.enum'; +@Injectable() +export class AdminVenueService { + constructor( + @InjectRepository(Venue) + private readonly venueRepo: Repository, + @InjectRepository(VenueVerificationRequest) + private readonly venueVerificationRepo: Repository + ) { } + + async PendingVenues(): Promise { + const pending = await this.venueRepo.find({ + where: { + + status: VenueStatus.PENDING_REVIEW + } + }) + + if (pending.length === 0) { + throw new NotFoundException('Pending verifications is not found'); + } + return pending + } + + async PendingVenueDetails(venueId: string): Promise { + const pendingDetails = await this.venueRepo.findOne({ + where: { + id: venueId, + status: VenueStatus.PENDING_REVIEW + }, + relations: [ + 'images', + 'documents', + + ], + }) + + if (!pendingDetails) { + throw new NotFoundException("Venue Details Not Found") + } + return pendingDetails; + } + + async AcceptVerification(requestId: string, review_notes: string): Promise<{ + message: string, status: string, review_notes: string + }> { + + const request = await this.venueVerificationRepo.findOne({ + where: { + id: requestId + } + }) + + if (!request) { + throw new NotFoundException("Request is not found"); + } + + if (request.status != VerificationStatus.PENDING) { + throw new BadRequestException( + 'This request has already been reviewed.', + ); + } + request.status = VerificationStatus.APPROVED; + request.reviewNotes = review_notes + const venue = await this.venueRepo.findOne({ + where: { + id: request.venueId + } + }) + if (!venue) { + throw new NotFoundException('Venue not found'); + } + venue.status = VenueStatus.APPROVED + + await this.venueVerificationRepo.save(request) + await this.venueRepo.save(venue); + return { + message: `Venue ${venue.venueName} has been verified successfully.`, + status: venue.status, + review_notes: request.reviewNotes + } + } + + async RejectVerification(requestId: string, review_notes: string): Promise<{ + message: string, status: string, review_notes: string + }> { + + const request = await this.venueVerificationRepo.findOne({ + where: { + id: requestId + } + }) + + if (!request) { + throw new NotFoundException("Request is not found"); + } + if (request.status != VerificationStatus.PENDING) { + throw new BadRequestException('This request has already been reviewed.'); + } + + request.status = VerificationStatus.REJECTED; + request.reviewNotes = review_notes + const venue = await this.venueRepo.findOne({ + where: { + id: request.venueId + } + }) + if (!venue) { + throw new NotFoundException('Venue not found'); + } + venue.status = VenueStatus.REJECTED + + await this.venueVerificationRepo.save(request) + await this.venueRepo.save(venue); + + return { + message: `Venue ${venue.venueName} has been Rejected.`, + status: venue.status, + review_notes: request.reviewNotes + } + } + + + + +} diff --git a/bmvBackend/src/venues/dto/add-venue-document.dto.ts b/bmvBackend/src/venues/dto/add-venue-document.dto.ts new file mode 100644 index 000000000..2a932a8e4 --- /dev/null +++ b/bmvBackend/src/venues/dto/add-venue-document.dto.ts @@ -0,0 +1,14 @@ +import { IsEnum } from 'class-validator'; +import { DocumentType } from '../../common/enums/document-type.enum'; + +/** + * Used for multipart/form-data document upload. + * The actual file comes via the `file` field (FileInterceptor). + * This DTO covers the additional form fields. + */ +export class AddVenueDocumentDto { + @IsEnum(DocumentType, { + message: `documentType must be one of: ${Object.values(DocumentType).join(', ')}`, + }) + documentType: DocumentType; +} diff --git a/bmvBackend/src/venues/dto/add-venue-image.dto.ts b/bmvBackend/src/venues/dto/add-venue-image.dto.ts new file mode 100644 index 000000000..ae9531f46 --- /dev/null +++ b/bmvBackend/src/venues/dto/add-venue-image.dto.ts @@ -0,0 +1,22 @@ +import { IsEnum, IsInt, IsOptional, Min } from 'class-validator'; +import { Type } from 'class-transformer'; +import { ImageType } from '../../common/enums/image-type.enum'; + +/** + * Used for multipart/form-data image upload. + * The actual file comes via the `file` field (FileInterceptor). + * This DTO covers the additional form fields. + */ +export class AddVenueImageDto { + @IsEnum(ImageType, { + message: `imageType must be one of: ${Object.values(ImageType).join(', ')}`, + }) + imageType: ImageType; + + /** Optional display order — lower numbers appear first. */ + @IsOptional() + @IsInt() + @Min(0) + @Type(() => Number) + displayOrder?: number; +} diff --git a/bmvBackend/src/venues/dto/create-venue.dto.ts b/bmvBackend/src/venues/dto/create-venue.dto.ts new file mode 100644 index 000000000..302e4febc --- /dev/null +++ b/bmvBackend/src/venues/dto/create-venue.dto.ts @@ -0,0 +1,95 @@ +import { + IsBoolean, + IsDecimal, + IsEnum, + IsInt, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, + Max, + MaxLength, + Min, +} from 'class-validator'; +import { Type } from 'class-transformer'; +import { VenueType } from '../../common/enums/venue-type.enum'; +import { BookingType } from '../../common/enums/booking-type.enum'; + +export class CreateVenueDto { + // ─── Basic Info ────────────────────────────────────────────────────────── + + @IsString() + @IsNotEmpty() + @MaxLength(255) + venueName: string; + + @IsEnum(VenueType, { + message: `venueType must be one of: ${Object.values(VenueType).join(', ')}`, + }) + venueType: VenueType; + + @IsString() + @IsNotEmpty() + description: string; + + // ─── Location ──────────────────────────────────────────────────────────── + + @IsString() + @IsNotEmpty() + address: string; + + @IsString() + @IsNotEmpty() + @MaxLength(100) + city: string; + + @IsString() + @IsNotEmpty() + @MaxLength(100) + district: string; + + @IsString() + @IsNotEmpty() + @MaxLength(100) + state: string; + + @IsString() + @IsNotEmpty() + @MaxLength(20) + pincode: string; + + // ─── Capacity ──────────────────────────────────────────────────────────── + + @IsInt() + @Min(1) + @Type(() => Number) + maxCapacity: number; + + @IsInt() + @Min(1) + @Type(() => Number) + squareFeet: number; + + // ─── Parking ───────────────────────────────────────────────────────────── + + @IsBoolean() + hasParking: boolean; + + @IsOptional() + @IsInt() + @Min(1) + @Type(() => Number) + parkingCapacity?: number; + + // ─── Pricing & Booking ─────────────────────────────────────────────────── + + @IsNumber({ maxDecimalPlaces: 2 }, { message: 'startingPrice must be a valid price' }) + @Min(0) + @Type(() => Number) + startingPrice: number; + + @IsEnum(BookingType, { + message: `bookingType must be one of: ${Object.values(BookingType).join(', ')}`, + }) + bookingType: BookingType; +} diff --git a/bmvBackend/src/venues/dto/public-list-dto.ts b/bmvBackend/src/venues/dto/public-list-dto.ts new file mode 100644 index 000000000..926f80384 --- /dev/null +++ b/bmvBackend/src/venues/dto/public-list-dto.ts @@ -0,0 +1,34 @@ +import { VenueType } from "src/common/enums/venue-type.enum"; +import { BookingType } from "src/common/enums/booking-type.enum"; +import { VenueImage } from "../entities/venue-image.entity"; +import { VenueStatus } from "src/common/enums/venue-status.enum"; +export class PublicVenueResponseDto { + id: string; + venueName: string; + city: string; + venueType: VenueType; + maxCapacity: number; + startingPrice: number; + thumbnailImage: string; +} + +export class PublicVenueDetailResponseDto { + id: string; + venueName: string; + venueType: VenueType; + description: string; + address: string; + city: string; + district: string; + state: string; + pincode: string; + maxCapacity: number; + squareFeet: number; + hasParking: boolean; + parkingCapacity: number | null; + startingPrice: number; + bookingType: BookingType; + // status: VenueStatus + images: VenueImage[]; + +} \ No newline at end of file diff --git a/bmvBackend/src/venues/dto/public-venues-filter.dto.ts b/bmvBackend/src/venues/dto/public-venues-filter.dto.ts new file mode 100644 index 000000000..3506ecae6 --- /dev/null +++ b/bmvBackend/src/venues/dto/public-venues-filter.dto.ts @@ -0,0 +1,35 @@ +import { IsEnum, IsInt, IsOptional, IsString, Min } from 'class-validator'; +import { Type } from 'class-transformer'; +import { VenueType } from '../../common/enums/venue-type.enum'; + +export class PublicVenuesFilterDto { + @IsOptional() + @IsString() + city?: string; + + @IsOptional() + @IsEnum(VenueType) + venueType?: VenueType; + + @IsOptional() + @IsInt() + @Min(1) + @Type(() => Number) + maxCapacity?: number; + + @IsOptional() + @IsString() + search?: string; + + @IsOptional() + @IsInt() + @Min(1) + @Type(() => Number) + page?: number; + + @IsOptional() + @IsInt() + @Min(1) + @Type(() => Number) + limit?: number; +} diff --git a/bmvBackend/src/venues/dto/update-venue-info.dto.ts b/bmvBackend/src/venues/dto/update-venue-info.dto.ts new file mode 100644 index 000000000..70e912b63 --- /dev/null +++ b/bmvBackend/src/venues/dto/update-venue-info.dto.ts @@ -0,0 +1,8 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateVenueDto } from './create-venue.dto'; + +/** + * All fields from CreateVenueDto become optional. + * Used for PUT /venues/:venueId/info to allow partial updates. + */ +export class UpdateVenueInfoDto extends PartialType(CreateVenueDto) {} diff --git a/bmvBackend/src/venues/entities/venue-document.entity.ts b/bmvBackend/src/venues/entities/venue-document.entity.ts new file mode 100644 index 000000000..bb35ce82f --- /dev/null +++ b/bmvBackend/src/venues/entities/venue-document.entity.ts @@ -0,0 +1,52 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + CreateDateColumn, +} from 'typeorm'; +import { Venue } from './venue.entity'; +import { DocumentType } from '../../common/enums/document-type.enum'; +import { DocumentStatus } from '../../common/enums/document-status.enum'; + +@Entity('venue_documents') +export class VenueDocument { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'venue_id', type: 'uuid' }) + venueId: string; + + @ManyToOne(() => Venue, (venue) => venue.documents, { + onDelete: 'CASCADE', + eager: false, + }) + @JoinColumn({ name: 'venue_id' }) + venue: Venue; + + @Column({ name: 'document_type', type: 'enum', enum: DocumentType }) + documentType: DocumentType; + + /** Full Cloudinary secure URL — restricted, only shown to admin and the owner. */ + @Column({ name: 'document_url', type: 'text' }) + documentUrl: string; + + /** + * Cloudinary public_id — used for deletion. + * e.g. venues/{venueId}/documents/{cloudinary-id} + */ + @Column({ name: 'file_key', type: 'text' }) + fileKey: string; + + @Column({ + name: 'verification_status', + type: 'enum', + enum: DocumentStatus, + default: DocumentStatus.PENDING, + }) + verificationStatus: DocumentStatus; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; +} diff --git a/bmvBackend/src/venues/entities/venue-image.entity.ts b/bmvBackend/src/venues/entities/venue-image.entity.ts new file mode 100644 index 000000000..480502ae1 --- /dev/null +++ b/bmvBackend/src/venues/entities/venue-image.entity.ts @@ -0,0 +1,47 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + CreateDateColumn, +} from 'typeorm'; +import { Venue } from './venue.entity'; +import { ImageType } from '../../common/enums/image-type.enum'; + +@Entity('venue_images') +export class VenueImage { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'venue_id', type: 'uuid' }) + venueId: string; + + @ManyToOne(() => Venue, (venue) => venue.images, { + onDelete: 'CASCADE', + eager: false, + }) + @JoinColumn({ name: 'venue_id' }) + venue: Venue; + + /** Full Cloudinary secure URL returned to clients. */ + @Column({ name: 'image_url', type: 'text' }) + imageUrl: string; + + /** + * Cloudinary public_id — used for deletion. + * e.g. venues/{venueId}/images/{cloudinary-id} + */ + @Column({ name: 'file_key', type: 'text' }) + fileKey: string; + + @Column({ name: 'image_type', type: 'enum', enum: ImageType }) + imageType: ImageType; + + /** Controls display order on the frontend (lower = first). */ + @Column({ name: 'display_order', type: 'int', default: 0 }) + displayOrder: number; + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; +} diff --git a/bmvBackend/src/venues/entities/venue-verification-request.entity.ts b/bmvBackend/src/venues/entities/venue-verification-request.entity.ts new file mode 100644 index 000000000..467335f59 --- /dev/null +++ b/bmvBackend/src/venues/entities/venue-verification-request.entity.ts @@ -0,0 +1,79 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + JoinColumn, + CreateDateColumn, + Index, +} from 'typeorm'; +import { Venue } from './venue.entity'; +import { User } from '../../users/entities/user.entity'; +import { VerificationStatus } from '../../common/enums/verification-status.enum'; + +/** + * Each submission by the venue owner creates a new row. + * Full audit history is preserved across re-submissions. + * + * Current status = row with highest submission_number for a given venue_id. + * + * Partial unique constraint (enforced at DB level via migration / sync): + * UNIQUE (venue_id) WHERE status = 'PENDING' + * → Only one open submission allowed per venue at a time. + * + * REJECTED is terminal — owner cannot re-submit after rejection. + */ +@Entity('venue_verification_requests') +@Index(['venueId', 'submissionNumber']) +export class VenueVerificationRequest { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column({ name: 'venue_id', type: 'uuid' }) + venueId: string; + + @ManyToOne(() => Venue, (venue) => venue.verificationRequests, { + onDelete: 'CASCADE', + eager: false, + }) + @JoinColumn({ name: 'venue_id' }) + venue: Venue; + + @Column({ name: 'submitted_by', type: 'uuid' }) + submittedBy: string; + + @ManyToOne(() => User, { eager: false, onDelete: 'SET NULL' }) + @JoinColumn({ name: 'submitted_by' }) + submitter: User; + + /** + * Increments with each submission attempt. + * First submission → 1, after CHANGES_REQUESTED → 2, etc. + */ + @Column({ name: 'submission_number', type: 'int', default: 1 }) + submissionNumber: number; + + @Column({ + type: 'enum', + enum: VerificationStatus, + default: VerificationStatus.PENDING, + }) + status: VerificationStatus; + + /** Admin notes — populated when status changes to CHANGES_REQUESTED or REJECTED. */ + @Column({ name: 'review_notes', type: 'text', nullable: true }) + reviewNotes: string | null; + + @Column({ name: 'reviewed_by', type: 'uuid', nullable: true }) + reviewedBy: string | null; + + @ManyToOne(() => User, { eager: false, nullable: true, onDelete: 'SET NULL' }) + @JoinColumn({ name: 'reviewed_by' }) + reviewer: User | null; + + @CreateDateColumn({ name: 'submitted_at' }) + submittedAt: Date; + + @Column({ name: 'reviewed_at', type: 'timestamp', nullable: true }) + reviewedAt: Date | null; +} diff --git a/bmvBackend/src/venues/entities/venue.entity.ts b/bmvBackend/src/venues/entities/venue.entity.ts new file mode 100644 index 000000000..b9a114f16 --- /dev/null +++ b/bmvBackend/src/venues/entities/venue.entity.ts @@ -0,0 +1,129 @@ +import { + Entity, + PrimaryGeneratedColumn, + Column, + ManyToOne, + OneToMany, + JoinColumn, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm'; +import { User } from '../../users/entities/user.entity'; +import { VenueType } from '../../common/enums/venue-type.enum'; +import { VenueStatus } from '../../common/enums/venue-status.enum'; +import { BookingType } from '../../common/enums/booking-type.enum'; +import { VenueImage } from './venue-image.entity'; + +import { VenueDocument } from './venue-document.entity'; +import { VenueVerificationRequest } from './venue-verification-request.entity'; + +@Entity('venues') +export class Venue { + @PrimaryGeneratedColumn('uuid') + id: string; + + // ─── Owner ───────────────────────────────────────────────────────────────── + + @Column({ name: 'owner_id', type: 'uuid' }) + ownerId: string; + + @ManyToOne(() => User, { onDelete: 'CASCADE', eager: false }) + @JoinColumn({ name: 'owner_id' }) + owner: User; + + // ─── Basic Info ──────────────────────────────────────────────────────────── + + @Column({ name: 'venue_name', type: 'varchar', length: 255 }) + venueName: string; + + @Column({ type: 'enum', enum: VenueType }) + venueType: VenueType; + + @Column({ type: 'text' }) + description: string; + + // ─── Location ────────────────────────────────────────────────────────────── + + @Column({ type: 'text' }) + address: string; + + @Column({ type: 'varchar', length: 100 }) + city: string; + + @Column({ type: 'varchar', length: 100 }) + district: string; + + @Column({ type: 'varchar', length: 100 }) + state: string; + + @Column({ type: 'varchar', length: 20 }) + pincode: string; + // ─── Capacity ────────────────────────────────────────────────────────────── + + @Column({ name: 'max_capacity', type: 'int' }) + maxCapacity: number; + + @Column({ name: 'square_feet', type: 'int' }) + squareFeet: number; + + // ─── Parking ─────────────────────────────────────────────────────────────── + + @Column({ name: 'has_parking', type: 'boolean', default: false }) + hasParking: boolean; + + @Column({ name: 'parking_capacity', type: 'int', nullable: true }) + parkingCapacity: number | null; + + // ─── Pricing & Booking ───────────────────────────────────────────────────── + + @Column({ name: 'starting_price', type: 'decimal', precision: 12, scale: 2 }) + startingPrice: number; + + @Column({ name: 'booking_type', type: 'enum', enum: BookingType }) + bookingType: BookingType; + + // ─── Status ──────────────────────────────────────────────────────────────── + + @Column({ + type: 'enum', + enum: VenueStatus, + default: VenueStatus.DRAFT, + }) + status: VenueStatus; + + // ─── Onboarding Step Tracking ────────────────────────────────────────────── + // Each boolean is set true by the service when minimum requirements are met. + // canSubmit = all four are true AND status IN ['DRAFT', 'CHANGES_REQUESTED'] + + @Column({ name: 'step_venue_info_done', type: 'boolean', default: false }) + stepVenueInfoDone: boolean; + + @Column({ name: 'step_photos_done', type: 'boolean', default: false }) + stepPhotosDone: boolean; + + @Column({ name: 'step_facilities_done', type: 'boolean', default: false }) + stepFacilitiesDone: boolean; + + @Column({ name: 'step_documents_done', type: 'boolean', default: false }) + stepDocumentsDone: boolean; + + // ─── Relations ───────────────────────────────────────────────────────────── + + @OneToMany(() => VenueImage, (image) => image.venue, { cascade: true }) + images: VenueImage[]; + + + @OneToMany(() => VenueDocument, (doc) => doc.venue, { cascade: true }) + documents: VenueDocument[]; + + @OneToMany(() => VenueVerificationRequest, (req) => req.venue) + verificationRequests: VenueVerificationRequest[]; + + // ─── Timestamps ──────────────────────────────────────────────────────────── + + @CreateDateColumn({ name: 'created_at' }) + createdAt: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updatedAt: Date; +} diff --git a/bmvBackend/src/venues/public-venues.controller.ts b/bmvBackend/src/venues/public-venues.controller.ts new file mode 100644 index 000000000..f4065af58 --- /dev/null +++ b/bmvBackend/src/venues/public-venues.controller.ts @@ -0,0 +1,26 @@ +import { Controller, Get, Param, ParseUUIDPipe, Query } from '@nestjs/common'; +import { VenuesService } from './venues.service'; +import { PublicVenuesFilterDto } from './dto/public-venues-filter.dto'; + +@Controller('venues/public') +export class PublicVenuesController { + constructor(private readonly venuesService: VenuesService) { } + + /** + * GET /venues/public + * List all approved venues with optional filters and pagination. + */ + @Get() + getPublicVenues(@Query() filters: PublicVenuesFilterDto) { + return this.venuesService.findPublicVenues(filters); + } + + /** + * GET /venues/public/:id + * Get full details of a specific approved venue. + */ + @Get(':id') + getPublicVenueDetail(@Param('id', ParseUUIDPipe) id: string) { + return this.venuesService.findPublicVenueDetail(id); + } +} diff --git a/bmvBackend/src/venues/venues.controller.ts b/bmvBackend/src/venues/venues.controller.ts new file mode 100644 index 000000000..c6fe1d14f --- /dev/null +++ b/bmvBackend/src/venues/venues.controller.ts @@ -0,0 +1,227 @@ +import { + Body, + Controller, + Delete, + Get, + HttpCode, + HttpStatus, + Param, + ParseUUIDPipe, + Post, + Patch, + Req, + UploadedFile, + UseGuards, + UseInterceptors, +} from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { JwtAuthGuard } from '../common/guards/jwt-auth.guard'; +import { RolesGuard } from '../common/guards/roles.guard'; +import { VenueOwnerGuard } from '../common/guards/venue-owner.guard'; +import { Roles } from '../common/decorators/roles.decorator'; +import { CurrentUser } from '../common/decorators/current-user.decorator'; +import type { CurrentUserPayload } from '../common/decorators/current-user.decorator'; + +import { UserRole } from '../common/enums/user-role.enum'; +import { VenuesService } from './venues.service'; +import { Venue } from './entities/venue.entity'; +import { CreateVenueDto } from './dto/create-venue.dto'; +import { UpdateVenueInfoDto } from './dto/update-venue-info.dto'; +import { ImageType } from '../common/enums/image-type.enum'; +import { DocumentType } from '../common/enums/document-type.enum'; + +/** Helper to get the pre-loaded venue from request (attached by VenueOwnerGuard). */ +function getVenue(req: any): Venue { + return req.venue as Venue; +} + +@Controller() +@UseGuards(JwtAuthGuard, RolesGuard) +export class VenuesController { + constructor(private readonly venuesService: VenuesService) { } + + // ─── Step 2: Venue Basic Info ────────────────────────────────────────────── + + /** + * POST /venues + * Create a new venue. Owner can only have one venue. + */ + @Post('venues') + @HttpCode(HttpStatus.CREATED) + @Roles(UserRole.VENUE_OWNER) + createVenue( + @Body() dto: CreateVenueDto, + @CurrentUser() user: CurrentUserPayload, + ) { + return this.venuesService.createVenue(dto, user.sub); + } + + /** + * GET /venues/my-venue + * Get the authenticated owner's venue. + */ + @Get('venues/my-venue') + @Roles(UserRole.VENUE_OWNER) + getMyVenue(@CurrentUser() user: CurrentUserPayload) { + return this.venuesService.getMyVenue(user.sub); + } + + /** + * PATCH /venues/:venueId/info + * Update venue basic info. Blocked while PENDING_REVIEW. + */ + @Patch('venues/:venueId/info') + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + updateVenueInfo(@Req() req: any, @Body() dto: UpdateVenueInfoDto) { + return this.venuesService.updateVenueInfo(getVenue(req), dto); + } + + // ─── Step 3: Photos ──────────────────────────────────────────────────────── + + /** + * GET /venues/:venueId/images + * List all images for a venue. + */ + @Get('venues/:venueId/images') + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + getImages(@Param('venueId', ParseUUIDPipe) venueId: string) { + return this.venuesService.getImages(venueId); + } + + /** + * POST /venues/:venueId/images + * Upload an image file directly — multipart/form-data. + * Fields: file (required), imageType (required), displayOrder (optional). + */ + @Post('venues/:venueId/images') + @HttpCode(HttpStatus.CREATED) + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + @UseInterceptors(FileInterceptor('file')) + addImage( + @Req() req: any, + @UploadedFile() file: Express.Multer.File, + @Body('imageType') imageType: ImageType, + @Body('displayOrder') displayOrder: string, + @CurrentUser() user: CurrentUserPayload, + ) { + return this.venuesService.addImage( + getVenue(req), + file, + imageType, + displayOrder ? parseInt(displayOrder, 10) : undefined, + user.sub, + ); + } + + /** + * DELETE /venues/:venueId/images/:imageId + * Remove an image from the venue and delete it from Cloudinary. + */ + @Delete('venues/:venueId/images/:imageId') + @HttpCode(HttpStatus.OK) + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + deleteImage( + @Req() req: any, + @Param('imageId', ParseUUIDPipe) imageId: string, + ) { + return this.venuesService.deleteImage(getVenue(req), imageId); + } + + // ─── Step 4: Facilities ──────────────────────────────────────────────────── + + /** + * GET /facilities + * Public list of all available facilities (for the checklist UI). + */ + + + /** + * GET /venues/:venueId/facilities + * Get the facilities selected for a specific venue. + */ + /** + * PATCH /venues/:venueId/facilities + * Replace the entire facility selection. Min 1 required. + */ + + + // ─── Step 5: Documents ───────────────────────────────────────────────────── + + /** + * GET /venues/:venueId/documents + * List documents (URL hidden from listing response). + */ + @Get('venues/:venueId/documents') + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + getDocuments(@Param('venueId', ParseUUIDPipe) venueId: string) { + return this.venuesService.getDocuments(venueId); + } + + /** + * POST /venues/:venueId/documents + * Upload a document file directly — multipart/form-data. + * Fields: file (required), documentType (required). + */ + @Post('venues/:venueId/documents') + @HttpCode(HttpStatus.CREATED) + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + @UseInterceptors(FileInterceptor('file')) + addDocument( + @Req() req: any, + @UploadedFile() file: Express.Multer.File, + @Body('documentType') documentType: DocumentType, + @CurrentUser() user: CurrentUserPayload, + ) { + return this.venuesService.addDocument(getVenue(req), file, documentType, user.sub); + } + + /** + * DELETE /venues/:venueId/documents/:documentId + * Remove a document and delete it from Cloudinary. + */ + @Delete('venues/:venueId/documents/:documentId') + @HttpCode(HttpStatus.OK) + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + deleteDocument( + @Req() req: any, + @Param('documentId', ParseUUIDPipe) documentId: string, + ) { + return this.venuesService.deleteDocument(getVenue(req), documentId); + } + + // ─── Step 6: Onboarding Status & Submit ─────────────────────────────────── + + /** + * GET /venues/:venueId/onboarding-status + * Full step progress report + submission history. + */ + @Get('venues/:venueId/onboarding-status') + @Roles(UserRole.VENUE_OWNER) + @UseGuards(VenueOwnerGuard) + getOnboardingStatus(@Req() req: any) { + return this.venuesService.getOnboardingStatus(getVenue(req)); + } + + /** + * POST /venues/:venueId/submit + * Submit the venue for admin verification. + * Requires all 4 steps complete. REJECTED venues are blocked (terminal). + */ + @Post('venues/:venueId/submit') + @HttpCode(HttpStatus.OK) + @Roles(UserRole.VENUE_OWNER, UserRole.ADMIN) + @UseGuards(VenueOwnerGuard) + submitForVerification( + @Req() req: any, + @CurrentUser() user: CurrentUserPayload, + ) { + return this.venuesService.submitForVerification(getVenue(req), user.sub); + } +} diff --git a/bmvBackend/src/venues/venues.module.ts b/bmvBackend/src/venues/venues.module.ts new file mode 100644 index 000000000..6de51c91f --- /dev/null +++ b/bmvBackend/src/venues/venues.module.ts @@ -0,0 +1,27 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ConfigModule } from '@nestjs/config'; +import { VenuesController } from './venues.controller'; +import { PublicVenuesController } from './public-venues.controller'; +import { VenuesService } from './venues.service'; +import { Venue } from './entities/venue.entity'; +import { VenueImage } from './entities/venue-image.entity'; +import { VenueDocument } from './entities/venue-document.entity'; +import { VenueVerificationRequest } from './entities/venue-verification-request.entity'; +import { AdminVenueModule } from './admin-venue/admin-venue.module'; + +@Module({ + imports: [ + ConfigModule, + TypeOrmModule.forFeature([ + Venue, + VenueImage, + VenueDocument, + VenueVerificationRequest + ]), + AdminVenueModule, + ], + controllers: [VenuesController, PublicVenuesController], + providers: [VenuesService], +}) +export class VenuesModule { } diff --git a/bmvBackend/src/venues/venues.service.ts b/bmvBackend/src/venues/venues.service.ts new file mode 100644 index 000000000..ee9af6798 --- /dev/null +++ b/bmvBackend/src/venues/venues.service.ts @@ -0,0 +1,509 @@ +import { + BadRequestException, + ConflictException, + ForbiddenException, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository, DataSource, In } from 'typeorm'; +import { Venue } from './entities/venue.entity'; +import { VenueImage } from './entities/venue-image.entity'; +import { VenueDocument } from './entities/venue-document.entity'; +import { VenueVerificationRequest } from './entities/venue-verification-request.entity'; +import { VenueStatus } from '../common/enums/venue-status.enum'; +import { VerificationStatus } from '../common/enums/verification-status.enum'; +import { ImageType } from '../common/enums/image-type.enum'; +import { DocumentType } from '../common/enums/document-type.enum'; +import { CloudinaryService } from '../cloudinary/cloudinary.service'; +import { CreateVenueDto } from './dto/create-venue.dto'; +import { UpdateVenueInfoDto } from './dto/update-venue-info.dto'; +import { PublicVenuesFilterDto } from './dto/public-venues-filter.dto'; +import { PublicVenueResponseDto } from './dto/public-list-dto'; +import { PublicVenueDetailResponseDto } from './dto/public-list-dto'; + +/** Minimum / maximum photos required per venue. */ +const MIN_PHOTOS_TOTAL = 5; +const MAX_PHOTOS_TOTAL = 20; + +@Injectable() +export class VenuesService { + constructor( + @InjectRepository(Venue) + private readonly venueRepo: Repository, + @InjectRepository(VenueImage) + private readonly imageRepo: Repository, + @InjectRepository(VenueDocument) + private readonly documentRepo: Repository, + @InjectRepository(VenueVerificationRequest) + private readonly verificationRepo: Repository, + private readonly cloudinaryService: CloudinaryService, + private readonly dataSource: DataSource, + ) { } + + // ═══════════════════════════════════════════════════════════════════════════ + // Step 2 — Venue Basic Info + // ═══════════════════════════════════════════════════════════════════════════ + + /** + * Creates a new venue record for the authenticated owner. + * Sets step_venue_info_done = true immediately. + * An owner can have only one venue in DRAFT/PENDING at a time. + */ + async createVenue(dto: CreateVenueDto, ownerId: string): Promise { + // Prevent duplicate active venues per owner + const existing = await this.venueRepo.findOne({ where: { ownerId } }); + if (existing) { + throw new ConflictException( + 'You already have a venue registered. Please manage your existing venue.', + ); + } + + const venue = this.venueRepo.create({ + ...dto, + ownerId, + status: VenueStatus.DRAFT, + stepVenueInfoDone: true, + }); + + return this.venueRepo.save(venue); + } + + /** + * Updates venue basic info. + * Blocked when status = PENDING_REVIEW (must wait for admin decision). + */ + async updateVenueInfo(venue: Venue, dto: UpdateVenueInfoDto): Promise { + if (venue.status === VenueStatus.PENDING_REVIEW) { + throw new ForbiddenException( + 'Venue is currently under review. You cannot edit it until the admin review is complete.', + ); + } + + Object.assign(venue, dto); + venue.stepVenueInfoDone = true; + return this.venueRepo.save(venue); + } + + /** Returns the venue owned by the authenticated user. */ + async getMyVenue(ownerId: string): Promise { + const venue = await this.venueRepo.findOne({ where: { ownerId } }); + if (!venue) { + throw new NotFoundException('No venue found for your account.'); + } + return venue; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // Step 3 — Venue Photos + // ═══════════════════════════════════════════════════════════════════════════ + + async getImages(venueId: string): Promise { + return this.imageRepo.find({ + where: { venueId }, + order: { displayOrder: 'ASC', createdAt: 'ASC' }, + }); + } + + /** + * Uploads an image file to Cloudinary and saves the record. + * Accepts a Multer file buffer directly (multipart/form-data). + */ + async addImage( + venue: Venue, + file: Express.Multer.File, + imageType: ImageType, + displayOrder: number | undefined, + uploaderId: string, + ): Promise { + if (!file) { + throw new BadRequestException('No file provided. Please upload an image.'); + } + if (!imageType || !Object.values(ImageType).includes(imageType)) { + throw new BadRequestException( + `imageType must be one of: ${Object.values(ImageType).join(', ')}`, + ); + } + + // Total image cap + const currentCount = await this.imageRepo.count({ + where: { venueId: venue.id }, + }); + if (currentCount >= MAX_PHOTOS_TOTAL) { + throw new BadRequestException( + `Maximum ${MAX_PHOTOS_TOTAL} images allowed per venue.`, + ); + } + + // Upload to Cloudinary + const { publicId, secureUrl } = await this.cloudinaryService.uploadImage( + file.buffer, + venue.id, + ); + + // Persist record — fileKey holds the Cloudinary public_id for future deletion + const image = await this.imageRepo.save( + this.imageRepo.create({ + venueId: venue.id, + imageUrl: secureUrl, + fileKey: publicId, + imageType, + displayOrder: displayOrder ?? currentCount, + }), + ); + + // Recompute step completion + await this.recomputePhotoStep(venue); + + return image; + } + + async deleteImage( + venue: Venue, + imageId: string, + ): Promise<{ message: string }> { + const image = await this.imageRepo.findOne({ + where: { id: imageId, venueId: venue.id }, + }); + if (!image) { + throw new NotFoundException('Image not found.'); + } + + // Delete from Cloudinary + await this.cloudinaryService.deleteImage(image.fileKey); + + await this.imageRepo.delete(imageId); + + // Recompute step completion + await this.recomputePhotoStep(venue); + + return { message: 'Image deleted successfully.' }; + } + + /** + * Recomputes step_photos_done based on current image coverage. + * + * Rules: + * - At least 1 COVER image + * - At least 1 ENTRANCE image + * - At least 1 HALL image + * - At least 1 PARKING image IF venue.has_parking = true + * - Total >= 5 images + */ + private async recomputePhotoStep(venue: Venue): Promise { + const images = await this.imageRepo.find({ where: { venueId: venue.id } }); + + const byType = (type: ImageType) => + images.filter((i) => i.imageType === type).length; + + const hasMinimum = + byType(ImageType.COVER) >= 1 && + byType(ImageType.ENTRANCE) >= 1 && + byType(ImageType.HALL) >= 1 && + (!venue.hasParking || byType(ImageType.PARKING) >= 1) && + images.length >= MIN_PHOTOS_TOTAL; + + await this.venueRepo.update(venue.id, { stepPhotosDone: hasMinimum }); + } + + // ═══════════════════════════════════════════════════════════════════════════ + // Step 5 — Verification Documents + // ═══════════════════════════════════════════════════════════════════════════ + + async getDocuments( + venueId: string, + ): Promise[]> { + const docs = await this.documentRepo.find({ where: { venueId } }); + // Strip the actual URL — only show type + status to the owner for listing + return docs.map(({ documentUrl: _url, ...rest }) => rest); + } + + /** + * Uploads a document file to Cloudinary and saves the record. + * Accepts a Multer file buffer directly (multipart/form-data). + */ + async addDocument( + venue: Venue, + file: Express.Multer.File, + documentType: DocumentType, + uploaderId: string, + ): Promise> { + if (!file) { + throw new BadRequestException( + 'No file provided. Please upload a document.', + ); + } + if (!documentType || !Object.values(DocumentType).includes(documentType)) { + throw new BadRequestException( + `documentType must be one of: ${Object.values(DocumentType).join(', ')}`, + ); + } + + // Upload to Cloudinary (raw resource type for PDFs / non-image files) + const { publicId, secureUrl } = await this.cloudinaryService.uploadDocument( + file.buffer, + venue.id, + ); + + const doc = await this.documentRepo.save( + this.documentRepo.create({ + venueId: venue.id, + documentType, + documentUrl: secureUrl, + fileKey: publicId, // Cloudinary public_id stored for deletion + }), + ); + + // Any document uploaded = step done + await this.venueRepo.update(venue.id, { stepDocumentsDone: true }); + + const { documentUrl: _url, ...rest } = doc; + return rest; + } + + async deleteDocument( + venue: Venue, + documentId: string, + ): Promise<{ message: string }> { + const doc = await this.documentRepo.findOne({ + where: { id: documentId, venueId: venue.id }, + }); + if (!doc) { + throw new NotFoundException('Document not found.'); + } + + // Delete from Cloudinary + await this.cloudinaryService.deleteDocument(doc.fileKey); + + await this.documentRepo.delete(documentId); + + // Recompute: if no docs remain, step is incomplete + const remaining = await this.documentRepo.count({ + where: { venueId: venue.id }, + }); + await this.venueRepo.update(venue.id, { + stepDocumentsDone: remaining > 0, + }); + + return { message: 'Document deleted successfully.' }; + } + + // ═══════════════════════════════════════════════════════════════════════════ + // Step 6 — Onboarding Status & Submit + // ═══════════════════════════════════════════════════════════════════════════ + + /** Returns full onboarding progress + submission history. */ + async getOnboardingStatus(venue: Venue) { + const images = await this.imageRepo.find({ where: { venueId: venue.id } }); + + const documents = await this.documentRepo.count({ + where: { venueId: venue.id }, + }); + const history = await this.verificationRepo.find({ + where: { venueId: venue.id }, + order: { submissionNumber: 'DESC' }, + select: [ + 'id', + 'submissionNumber', + 'status', + 'reviewNotes', + 'submittedAt', + 'reviewedAt', + ], + }); + + // Reload fresh venue for up-to-date step flags + const fresh = await this.venueRepo.findOneOrFail({ + where: { id: venue.id }, + }); + + const canSubmit = + fresh.stepVenueInfoDone && + fresh.stepPhotosDone && + fresh.stepFacilitiesDone && + fresh.stepDocumentsDone && + (fresh.status === VenueStatus.DRAFT || + fresh.status === VenueStatus.CHANGES_REQUESTED); + + return { + venueId: fresh.id, + venueName: fresh.venueName, + status: fresh.status, + steps: { + venueInfo: { done: fresh.stepVenueInfoDone }, + photos: { + done: fresh.stepPhotosDone, + uploaded: images.length, + required: MIN_PHOTOS_TOTAL, + }, + documents: { done: fresh.stepDocumentsDone, count: documents }, + }, + canSubmit, + submissionHistory: history, + }; + } + + /** + * Submits the venue for admin verification. + * + * Rules: + * - All 4 onboarding steps must be complete. + * - Venue status must be DRAFT or CHANGES_REQUESTED. + * - No existing PENDING submission for this venue. + * - REJECTED venues cannot re-submit (terminal). + */ + async submitForVerification( + venue: Venue, + submittedBy: string, + ): Promise<{ message: string; submissionNumber: number; status: string }> { + // Block REJECTED venues + if (venue.status === VenueStatus.REJECTED) { + throw new ForbiddenException( + 'This venue has been rejected. Please contact support to appeal.', + ); + } + + // Must be in a submittable state + if ( + venue.status !== VenueStatus.DRAFT && + venue.status !== VenueStatus.CHANGES_REQUESTED + ) { + throw new BadRequestException( + `Cannot submit a venue with status "${venue.status}".`, + ); + } + + // All steps must be done + if ( + !venue.stepVenueInfoDone || + !venue.stepPhotosDone || + !venue.stepDocumentsDone + ) { + throw new BadRequestException( + 'Please complete all onboarding steps before submitting.', + ); + } + + // Check for duplicate pending submission + const pending = await this.verificationRepo.findOne({ + where: { venueId: venue.id, status: VerificationStatus.PENDING }, + }); + if (pending) { + throw new ConflictException( + 'A verification request is already pending for this venue.', + ); + } + + // Calculate next submission number + const lastRequest = await this.verificationRepo.findOne({ + where: { venueId: venue.id }, + order: { submissionNumber: 'DESC' }, + }); + const submissionNumber = (lastRequest?.submissionNumber ?? 0) + 1; + + // Update venue status + create new request row in a transaction + await this.dataSource.transaction(async (manager) => { + await manager.update(Venue, venue.id, { + status: VenueStatus.PENDING_REVIEW, + }); + await manager.save( + manager.create(VenueVerificationRequest, { + venueId: venue.id, + submittedBy, + submissionNumber, + status: VerificationStatus.PENDING, + }), + ); + }); + + return { + message: 'Venue submitted for verification successfully.', + submissionNumber, + status: VenueStatus.PENDING_REVIEW, + }; + } + + /** + * Find all public venues that are APPROVED. + * Supports pagination and filtering by city, type, capacity, and keyword search. + */ + async findPublicVenues( + filters: PublicVenuesFilterDto, + ): Promise<{ data: PublicVenueResponseDto[]; total: number; page: number; limit: number }> { + const { city, venueType, maxCapacity, search, page = 1, limit = 10 } = filters; + + const query = this.venueRepo.createQueryBuilder('venue') + .leftJoinAndSelect('venue.images', 'image') + .where('venue.status = :status', { status: VenueStatus.APPROVED }); + + + if (city) { + query.andWhere('LOWER(venue.city) = LOWER(:city)', { city }); + } + + if (venueType) { + query.andWhere('venue.venueType = :venueType', { venueType }); + } + + if (maxCapacity) { + query.andWhere('venue.maxCapacity >= :maxCapacity', { maxCapacity }); + } + + if (search) { + query.andWhere( + '(LOWER(venue.venueName) LIKE LOWER(:search) OR LOWER(venue.description) LIKE LOWER(:search) OR LOWER(venue.city) LIKE LOWER(:search))', + { search: `%${search}%` }, + ); + } + + const skip = (page - 1) * limit; + query.skip(skip).take(limit); + query.orderBy('venue.createdAt', 'DESC'); + + const [venues, total] = await query.getManyAndCount(); + + const data: PublicVenueResponseDto[] = venues.map((venue) => ({ + id: venue.id, + venueName: venue.venueName, + city: venue.city, + venueType: venue.venueType, + maxCapacity: venue.maxCapacity, + startingPrice: venue.startingPrice, + thumbnailImage: venue.images?.[0]?.imageUrl ?? null, + })); + + return { + data, + total, + page, + limit, + }; + } + + /** + * Find full details of a specific approved venue. + */ + async findPublicVenueDetail(id: string): Promise { + const venue = await this.venueRepo.findOne({ + where: { id, status: VenueStatus.APPROVED }, + relations: ['images'], + }); + + if (!venue) { + throw new NotFoundException('Venue not found or not approved.'); + } + const { + ownerId, + stepVenueInfoDone, + stepPhotosDone, + stepFacilitiesDone, + stepDocumentsDone, + ...venueData + } = venue; + + return venueData; + } + + + +} diff --git a/bmvBackend/test/app.e2e-spec.ts b/bmvBackend/test/app.e2e-spec.ts new file mode 100644 index 000000000..a767839c0 --- /dev/null +++ b/bmvBackend/test/app.e2e-spec.ts @@ -0,0 +1,29 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { App } from 'supertest/types'; +import { AppModule } from './../src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); + + afterEach(async () => { + await app.close(); + }); +}); diff --git a/bmvBackend/test/jest-e2e.json b/bmvBackend/test/jest-e2e.json new file mode 100644 index 000000000..e9d912f3e --- /dev/null +++ b/bmvBackend/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/bmvBackend/tsconfig.build.json b/bmvBackend/tsconfig.build.json new file mode 100644 index 000000000..64f86c6bd --- /dev/null +++ b/bmvBackend/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/bmvBackend/tsconfig.json b/bmvBackend/tsconfig.json new file mode 100644 index 000000000..cbf1e51e3 --- /dev/null +++ b/bmvBackend/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": false, + "strictBindCallApply": false, + "noFallthroughCasesInSwitch": false + } +} \ No newline at end of file From 553202c0bf14569355b2cf956d707f75ebe9af60 Mon Sep 17 00:00:00 2001 From: Gokul Shaji Date: Wed, 10 Jun 2026 13:07:11 +0530 Subject: [PATCH 02/16] initial commit --- bmvfrontend/.gitignore | 42 + bmvfrontend/README.md | 36 + bmvfrontend/app/booking/confirm/page.tsx | 5 + bmvfrontend/app/customer/page.tsx | 5 + bmvfrontend/app/customer/register/page.tsx | 14 + bmvfrontend/app/favicon.ico | Bin 0 -> 25931 bytes bmvfrontend/app/globals.css | 320 + bmvfrontend/app/layout.tsx | 32 + bmvfrontend/app/login/page.tsx | 14 + bmvfrontend/app/page.tsx | 5 + bmvfrontend/app/partner/dashboard/page.tsx | 5 + bmvfrontend/app/partner/login/page.tsx | 5 + bmvfrontend/app/partner/page.tsx | 5 + bmvfrontend/app/partner/register/page.tsx | 5 + bmvfrontend/app/register/page.tsx | 14 + bmvfrontend/app/venues/%5Bid%5D/page.tsx | 10 + bmvfrontend/app/venues/page.tsx | 14 + bmvfrontend/components.json | 25 + bmvfrontend/components/ui/accordion.tsx | 72 + bmvfrontend/components/ui/badge.tsx | 52 + bmvfrontend/components/ui/button.tsx | 58 + bmvfrontend/components/ui/calendar.tsx | 221 + bmvfrontend/components/ui/card.tsx | 103 + bmvfrontend/components/ui/checkbox.tsx | 29 + bmvfrontend/components/ui/input-otp.tsx | 87 + bmvfrontend/components/ui/input.tsx | 20 + bmvfrontend/components/ui/popover.tsx | 90 + bmvfrontend/components/ui/progress.tsx | 83 + bmvfrontend/components/ui/radio-group.tsx | 38 + bmvfrontend/components/ui/select.tsx | 201 + bmvfrontend/components/ui/sheet.tsx | 138 + bmvfrontend/components/ui/skeleton.tsx | 13 + bmvfrontend/components/ui/slider.tsx | 52 + bmvfrontend/components/ui/tabs.tsx | 82 + bmvfrontend/eslint.config.mjs | 18 + bmvfrontend/lib/utils.ts | 6 + bmvfrontend/next.config.ts | 15 + bmvfrontend/package-lock.json | 9453 +++++++++++++++++ bmvfrontend/package.json | 36 + bmvfrontend/postcss.config.mjs | 7 + bmvfrontend/public/file.svg | 1 + bmvfrontend/public/globe.svg | 1 + bmvfrontend/public/next.svg | 1 + bmvfrontend/public/vercel.svg | 1 + bmvfrontend/public/window.svg | 1 + bmvfrontend/src/customer/auth/Login.tsx | 200 + bmvfrontend/src/customer/auth/Register.tsx | 404 + .../src/customer/components/Footer.tsx | 139 + .../src/customer/components/Header.tsx | 188 + .../src/customer/pages/BookingConfirm.tsx | 280 + bmvfrontend/src/customer/pages/Home.tsx | 427 + .../src/customer/pages/VenueDetail.tsx | 629 ++ .../src/customer/pages/VenueListing.tsx | 695 ++ bmvfrontend/src/lib/authStore.ts | 77 + bmvfrontend/src/lib/mockData.ts | 193 + bmvfrontend/src/partner/auth/PartnerLogin.tsx | 149 + .../src/partner/auth/PartnerRegister.tsx | 460 + bmvfrontend/src/partner/pages/Dashboard.tsx | 300 + .../src/partner/pages/PartnerLanding.tsx | 357 + bmvfrontend/tsconfig.json | 34 + 60 files changed, 15967 insertions(+) create mode 100644 bmvfrontend/.gitignore create mode 100644 bmvfrontend/README.md create mode 100644 bmvfrontend/app/booking/confirm/page.tsx create mode 100644 bmvfrontend/app/customer/page.tsx create mode 100644 bmvfrontend/app/customer/register/page.tsx create mode 100644 bmvfrontend/app/favicon.ico create mode 100644 bmvfrontend/app/globals.css create mode 100644 bmvfrontend/app/layout.tsx create mode 100644 bmvfrontend/app/login/page.tsx create mode 100644 bmvfrontend/app/page.tsx create mode 100644 bmvfrontend/app/partner/dashboard/page.tsx create mode 100644 bmvfrontend/app/partner/login/page.tsx create mode 100644 bmvfrontend/app/partner/page.tsx create mode 100644 bmvfrontend/app/partner/register/page.tsx create mode 100644 bmvfrontend/app/register/page.tsx create mode 100644 bmvfrontend/app/venues/%5Bid%5D/page.tsx create mode 100644 bmvfrontend/app/venues/page.tsx create mode 100644 bmvfrontend/components.json create mode 100644 bmvfrontend/components/ui/accordion.tsx create mode 100644 bmvfrontend/components/ui/badge.tsx create mode 100644 bmvfrontend/components/ui/button.tsx create mode 100644 bmvfrontend/components/ui/calendar.tsx create mode 100644 bmvfrontend/components/ui/card.tsx create mode 100644 bmvfrontend/components/ui/checkbox.tsx create mode 100644 bmvfrontend/components/ui/input-otp.tsx create mode 100644 bmvfrontend/components/ui/input.tsx create mode 100644 bmvfrontend/components/ui/popover.tsx create mode 100644 bmvfrontend/components/ui/progress.tsx create mode 100644 bmvfrontend/components/ui/radio-group.tsx create mode 100644 bmvfrontend/components/ui/select.tsx create mode 100644 bmvfrontend/components/ui/sheet.tsx create mode 100644 bmvfrontend/components/ui/skeleton.tsx create mode 100644 bmvfrontend/components/ui/slider.tsx create mode 100644 bmvfrontend/components/ui/tabs.tsx create mode 100644 bmvfrontend/eslint.config.mjs create mode 100644 bmvfrontend/lib/utils.ts create mode 100644 bmvfrontend/next.config.ts create mode 100644 bmvfrontend/package-lock.json create mode 100644 bmvfrontend/package.json create mode 100644 bmvfrontend/postcss.config.mjs create mode 100644 bmvfrontend/public/file.svg create mode 100644 bmvfrontend/public/globe.svg create mode 100644 bmvfrontend/public/next.svg create mode 100644 bmvfrontend/public/vercel.svg create mode 100644 bmvfrontend/public/window.svg create mode 100644 bmvfrontend/src/customer/auth/Login.tsx create mode 100644 bmvfrontend/src/customer/auth/Register.tsx create mode 100644 bmvfrontend/src/customer/components/Footer.tsx create mode 100644 bmvfrontend/src/customer/components/Header.tsx create mode 100644 bmvfrontend/src/customer/pages/BookingConfirm.tsx create mode 100644 bmvfrontend/src/customer/pages/Home.tsx create mode 100644 bmvfrontend/src/customer/pages/VenueDetail.tsx create mode 100644 bmvfrontend/src/customer/pages/VenueListing.tsx create mode 100644 bmvfrontend/src/lib/authStore.ts create mode 100644 bmvfrontend/src/lib/mockData.ts create mode 100644 bmvfrontend/src/partner/auth/PartnerLogin.tsx create mode 100644 bmvfrontend/src/partner/auth/PartnerRegister.tsx create mode 100644 bmvfrontend/src/partner/pages/Dashboard.tsx create mode 100644 bmvfrontend/src/partner/pages/PartnerLanding.tsx create mode 100644 bmvfrontend/tsconfig.json diff --git a/bmvfrontend/.gitignore b/bmvfrontend/.gitignore new file mode 100644 index 000000000..b1afb661e --- /dev/null +++ b/bmvfrontend/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions +/AGENTS.MD +/CLAUDE.MD +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/bmvfrontend/README.md b/bmvfrontend/README.md new file mode 100644 index 000000000..e215bc4cc --- /dev/null +++ b/bmvfrontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/bmvfrontend/app/booking/confirm/page.tsx b/bmvfrontend/app/booking/confirm/page.tsx new file mode 100644 index 000000000..07986f3b5 --- /dev/null +++ b/bmvfrontend/app/booking/confirm/page.tsx @@ -0,0 +1,5 @@ +import BookingConfirm from "@/src/customer/pages/BookingConfirm"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/customer/page.tsx b/bmvfrontend/app/customer/page.tsx new file mode 100644 index 000000000..6610f680b --- /dev/null +++ b/bmvfrontend/app/customer/page.tsx @@ -0,0 +1,5 @@ +import Home from "@/src/customer/pages/Home"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/customer/register/page.tsx b/bmvfrontend/app/customer/register/page.tsx new file mode 100644 index 000000000..db589ad12 --- /dev/null +++ b/bmvfrontend/app/customer/register/page.tsx @@ -0,0 +1,14 @@ +import React, { Suspense } from "react"; +import Register from "@/src/customer/auth/Register"; + +export default function Page() { + return ( + + Loading... + + }> + + + ); +} diff --git a/bmvfrontend/app/favicon.ico b/bmvfrontend/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXIbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN%hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT=zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B@xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W<fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O?MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&#&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7?r!zQTPPSv}{so2e>Fjs1{gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw*>=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*Ay{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4UlIWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyTDrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5EajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7giLVkBOKL@^smUA!tZ1CKRK}#SSM)iQHk)*R~?M!qkCruaS!#oIL1c z?J;U~&FfH#*98^G?i}pA{ z9Jg36t4=%6mhY(quYq*vSxptes9qy|7xSlH?G=S@>u>Ebe;|LVhs~@+06N<4CViBk zUiY$thvX;>Tby6z9Y1edAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJwb z_^v8bbg`SAn{I*4bH$u(RZ6*xUhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=pC^S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk($?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvhCL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397*_cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111aH}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*IcmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU&68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-=A=yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v#ix45EVrcEhr>!NMhprl$InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~&^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7<4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}scZlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2`1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M=hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S(O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/bmvfrontend/app/globals.css b/bmvfrontend/app/globals.css new file mode 100644 index 000000000..cbb7e7e1e --- /dev/null +++ b/bmvfrontend/app/globals.css @@ -0,0 +1,320 @@ +@import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn/tailwind.css"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: #FAFAF8; + --foreground: #1A1A19; + + --card: #FFFFFF; + --card-foreground: #1A1A19; + + --popover: #FFFFFF; + --popover-foreground: #1A1A19; + + --primary: #0D7377; + --primary-foreground: #FAFAF8; + + --secondary: #F0F0EC; + --secondary-foreground: #1A1A19; + + --muted: #F0F0EC; + --muted-foreground: #70706e; + + --accent: #E6F1F1; + --accent-foreground: #0D7377; + + --destructive: #EF4444; + --border: #E2E2DE; + --input: #E2E2DE; + --ring: #0D7377; + + --primary-teal: #0D7377; + --primary-teal-hover: #0a5b5e; + --primary-teal-light: #e6f1f1; + + --cta-amber: #F4A261; + --cta-amber-hover: #e7904c; + --cta-amber-light: #fef5ed; + + --neutral-dark: #232321; + --neutral-muted: #70706e; + --neutral-light: #F0F0EC; + + --radius: 0.75rem; /* rounded-xl (12px) as specified */ + + --sidebar: #FFFFFF; + --sidebar-foreground: #1A1A19; + --sidebar-primary: #0D7377; + --sidebar-primary-foreground: #FAFAF8; + --sidebar-accent: #E6F1F1; + --sidebar-accent-foreground: #0D7377; + --sidebar-border: #E2E2DE; + --sidebar-ring: #0D7377; +} + +@theme { + --font-sans: var(--font-sans), sans-serif; + --font-serif: var(--font-serif), serif; + + --color-background: var(--background); + --color-foreground: var(--foreground); + + --color-teal-primary: var(--primary-teal); + --color-teal-hover: var(--primary-teal-hover); + --color-teal-light: var(--primary-teal-light); + + --color-amber-cta: var(--cta-amber); + --color-amber-hover: var(--cta-amber-hover); + --color-amber-light: var(--cta-amber-light); + + --color-neutral-dark: var(--neutral-dark); + --color-neutral-muted: var(--neutral-muted); + --color-neutral-light: var(--neutral-light); + + --radius-xl: 16px; + --radius-lg: 12px; + --radius-md: 8px; + --radius-sm: 4px; +} + +body { + background-color: var(--background); + font-family: var(--font-sans); + min-height: 100vh; + overflow-x: hidden; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Custom Scrollbar */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: #FAFAF8; +} + +::-webkit-scrollbar-thumb { + background: #C4C4C0; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--primary-teal); +} + +/* Custom Micro-Animations & Utility Classes */ +.animate-fade-in { + animation: fadeIn 0.4s ease-out forwards; +} + +.animate-slide-up { + animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.animate-staggered-entrance > * { + opacity: 0; + animation: slideUp 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.animate-staggered-entrance > *:nth-child(1) { animation-delay: 0.05s; } +.animate-staggered-entrance > *:nth-child(2) { animation-delay: 0.1s; } +.animate-staggered-entrance > *:nth-child(3) { animation-delay: 0.15s; } +.animate-staggered-entrance > *:nth-child(4) { animation-delay: 0.2s; } +.animate-staggered-entrance > *:nth-child(5) { animation-delay: 0.25s; } + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(12px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Glassmorphism utility */ +.glass { + background: rgba(250, 250, 248, 0.7); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.3); +} + +.glass-dark { + background: rgba(26, 26, 25, 0.8); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.05); +} + +@theme inline { + + --font-heading: var(--font-sans); + + --font-sans: var(--font-sans); + + --color-sidebar-ring: var(--sidebar-ring); + + --color-sidebar-border: var(--sidebar-border); + + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + + --color-sidebar-accent: var(--sidebar-accent); + + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + + --color-sidebar-primary: var(--sidebar-primary); + + --color-sidebar-foreground: var(--sidebar-foreground); + + --color-sidebar: var(--sidebar); + + --color-chart-5: var(--chart-5); + + --color-chart-4: var(--chart-4); + + --color-chart-3: var(--chart-3); + + --color-chart-2: var(--chart-2); + + --color-chart-1: var(--chart-1); + + --color-ring: var(--ring); + + --color-input: var(--input); + + --color-border: var(--border); + + --color-destructive: var(--destructive); + + --color-accent-foreground: var(--accent-foreground); + + --color-accent: var(--accent); + + --color-muted-foreground: var(--muted-foreground); + + --color-muted: var(--muted); + + --color-secondary-foreground: var(--secondary-foreground); + + --color-secondary: var(--secondary); + + --color-primary-foreground: var(--primary-foreground); + + --color-primary: var(--primary); + + --color-popover-foreground: var(--popover-foreground); + + --color-popover: var(--popover); + + --color-card-foreground: var(--card-foreground); + + --color-card: var(--card); + + --color-foreground: var(--foreground); + + --color-background: var(--background); + + --radius-sm: calc(var(--radius) * 0.6); + + --radius-md: calc(var(--radius) * 0.8); + + --radius-lg: var(--radius); + + --radius-xl: calc(var(--radius) * 1.4); + + --radius-2xl: calc(var(--radius) * 1.8); + + --radius-3xl: calc(var(--radius) * 2.2); + + --radius-4xl: calc(var(--radius) * 2.6); +} + +.dark { + + --background: oklch(0.145 0 0); + + --foreground: oklch(0.985 0 0); + + --card: oklch(0.205 0 0); + + --card-foreground: oklch(0.985 0 0); + + --popover: oklch(0.205 0 0); + + --popover-foreground: oklch(0.985 0 0); + + --primary: oklch(0.922 0 0); + + --primary-foreground: oklch(0.205 0 0); + + --secondary: oklch(0.269 0 0); + + --secondary-foreground: oklch(0.985 0 0); + + --muted: oklch(0.269 0 0); + + --muted-foreground: oklch(0.708 0 0); + + --accent: oklch(0.269 0 0); + + --accent-foreground: oklch(0.985 0 0); + + --destructive: oklch(0.704 0.191 22.216); + + --border: oklch(1 0 0 / 10%); + + --input: oklch(1 0 0 / 15%); + + --ring: oklch(0.556 0 0); + + --chart-1: oklch(0.87 0 0); + + --chart-2: oklch(0.556 0 0); + + --chart-3: oklch(0.439 0 0); + + --chart-4: oklch(0.371 0 0); + + --chart-5: oklch(0.269 0 0); + + --sidebar: oklch(0.205 0 0); + + --sidebar-foreground: oklch(0.985 0 0); + + --sidebar-primary: oklch(0.488 0.243 264.376); + + --sidebar-primary-foreground: oklch(0.985 0 0); + + --sidebar-accent: oklch(0.269 0 0); + + --sidebar-accent-foreground: oklch(0.985 0 0); + + --sidebar-border: oklch(1 0 0 / 10%); + + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } + html { + @apply font-sans; + } +} \ No newline at end of file diff --git a/bmvfrontend/app/layout.tsx b/bmvfrontend/app/layout.tsx new file mode 100644 index 000000000..925ceeb38 --- /dev/null +++ b/bmvfrontend/app/layout.tsx @@ -0,0 +1,32 @@ +import type { Metadata } from "next"; +import { Inter, Playfair_Display, Geist } from "next/font/google"; +import "./globals.css"; +import { cn } from "@/lib/utils"; + +const geist = Geist({subsets:['latin'],variable:'--font-sans'}); + +const playfairDisplay = Playfair_Display({ + variable: "--font-serif", + subsets: ["latin"], + display: "swap", +}); + +export const metadata: Metadata = { + title: "BookMyVenue | Find the Perfect Venue for Every Occasion", + description: "Browse, filter, and book premium wedding halls, conference rooms, rooftops, and banquets across 18+ cities.", + keywords: ["venue booking", "wedding halls", "conference rooms", "banquet halls", "rooftop venues", "event venues"], +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + {children} + + + ); +} diff --git a/bmvfrontend/app/login/page.tsx b/bmvfrontend/app/login/page.tsx new file mode 100644 index 000000000..e5256a36f --- /dev/null +++ b/bmvfrontend/app/login/page.tsx @@ -0,0 +1,14 @@ +import React, { Suspense } from "react"; +import Login from "@/src/customer/auth/Login"; + +export default function Page() { + return ( + + Loading... + + }> + + + ); +} diff --git a/bmvfrontend/app/page.tsx b/bmvfrontend/app/page.tsx new file mode 100644 index 000000000..6610f680b --- /dev/null +++ b/bmvfrontend/app/page.tsx @@ -0,0 +1,5 @@ +import Home from "@/src/customer/pages/Home"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/partner/dashboard/page.tsx b/bmvfrontend/app/partner/dashboard/page.tsx new file mode 100644 index 000000000..5a2f16198 --- /dev/null +++ b/bmvfrontend/app/partner/dashboard/page.tsx @@ -0,0 +1,5 @@ +import Dashboard from "@/src/partner/pages/Dashboard"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/partner/login/page.tsx b/bmvfrontend/app/partner/login/page.tsx new file mode 100644 index 000000000..e545858f8 --- /dev/null +++ b/bmvfrontend/app/partner/login/page.tsx @@ -0,0 +1,5 @@ +import PartnerLogin from "@/src/partner/auth/PartnerLogin"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/partner/page.tsx b/bmvfrontend/app/partner/page.tsx new file mode 100644 index 000000000..2f4e1d793 --- /dev/null +++ b/bmvfrontend/app/partner/page.tsx @@ -0,0 +1,5 @@ +import PartnerLanding from "@/src/partner/pages/PartnerLanding"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/partner/register/page.tsx b/bmvfrontend/app/partner/register/page.tsx new file mode 100644 index 000000000..42877075d --- /dev/null +++ b/bmvfrontend/app/partner/register/page.tsx @@ -0,0 +1,5 @@ +import PartnerRegister from "@/src/partner/auth/PartnerRegister"; + +export default function Page() { + return ; +} diff --git a/bmvfrontend/app/register/page.tsx b/bmvfrontend/app/register/page.tsx new file mode 100644 index 000000000..db589ad12 --- /dev/null +++ b/bmvfrontend/app/register/page.tsx @@ -0,0 +1,14 @@ +import React, { Suspense } from "react"; +import Register from "@/src/customer/auth/Register"; + +export default function Page() { + return ( + + Loading... + + }> + + + ); +} diff --git a/bmvfrontend/app/venues/%5Bid%5D/page.tsx b/bmvfrontend/app/venues/%5Bid%5D/page.tsx new file mode 100644 index 000000000..8882647f1 --- /dev/null +++ b/bmvfrontend/app/venues/%5Bid%5D/page.tsx @@ -0,0 +1,10 @@ +import VenueDetail from "@/src/customer/pages/VenueDetail"; + +interface PageProps { + params: Promise<{ id: string }>; +} + +export default async function Page({ params }: PageProps) { + const resolvedParams = await params; + return ; +} diff --git a/bmvfrontend/app/venues/page.tsx b/bmvfrontend/app/venues/page.tsx new file mode 100644 index 000000000..80ec79773 --- /dev/null +++ b/bmvfrontend/app/venues/page.tsx @@ -0,0 +1,14 @@ +import React, { Suspense } from "react"; +import VenueListing from "@/src/customer/pages/VenueListing"; + +export default function Page() { + return ( + + Loading... + + }> + + + ); +} diff --git a/bmvfrontend/components.json b/bmvfrontend/components.json new file mode 100644 index 000000000..f382eb79e --- /dev/null +++ b/bmvfrontend/components.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-nova", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": {} +} diff --git a/bmvfrontend/components/ui/accordion.tsx b/bmvfrontend/components/ui/accordion.tsx new file mode 100644 index 000000000..597ef3cb9 --- /dev/null +++ b/bmvfrontend/components/ui/accordion.tsx @@ -0,0 +1,72 @@ +import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion" + +import { cn } from "@/lib/utils" +import { ChevronDownIcon, ChevronUpIcon } from "lucide-react" + +function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) { + return ( + + ) +} + +function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: AccordionPrimitive.Trigger.Props) { + return ( + + + {children} + + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: AccordionPrimitive.Panel.Props) { + return ( + +
+ {children} +
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/bmvfrontend/components/ui/badge.tsx b/bmvfrontend/components/ui/badge.tsx new file mode 100644 index 000000000..b20959dd7 --- /dev/null +++ b/bmvfrontend/components/ui/badge.tsx @@ -0,0 +1,52 @@ +import { mergeProps } from "@base-ui/react/merge-props" +import { useRender } from "@base-ui/react/use-render" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", + secondary: + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", + destructive: + "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20", + outline: + "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", + ghost: + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", + link: "text-primary underline-offset-4 hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant = "default", + render, + ...props +}: useRender.ComponentProps<"span"> & VariantProps) { + return useRender({ + defaultTagName: "span", + props: mergeProps<"span">( + { + className: cn(badgeVariants({ variant }), className), + }, + props + ), + render, + state: { + slot: "badge", + variant, + }, + }) +} + +export { Badge, badgeVariants } diff --git a/bmvfrontend/components/ui/button.tsx b/bmvfrontend/components/ui/button.tsx new file mode 100644 index 000000000..b0336017b --- /dev/null +++ b/bmvfrontend/components/ui/button.tsx @@ -0,0 +1,58 @@ +import { Button as ButtonPrimitive } from "@base-ui/react/button" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/80", + outline: + "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", + ghost: + "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50", + destructive: + "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: + "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + icon: "size-8", + "icon-xs": + "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", + "icon-sm": + "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", + "icon-lg": "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant = "default", + size = "default", + ...props +}: ButtonPrimitive.Props & VariantProps) { + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/bmvfrontend/components/ui/calendar.tsx b/bmvfrontend/components/ui/calendar.tsx new file mode 100644 index 000000000..352f20e48 --- /dev/null +++ b/bmvfrontend/components/ui/calendar.tsx @@ -0,0 +1,221 @@ +"use client" + +import * as React from "react" +import { + DayPicker, + getDefaultClassNames, + type DayButton, + type Locale, +} from "react-day-picker" + +import { cn } from "@/lib/utils" +import { Button, buttonVariants } from "@/components/ui/button" +import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from "lucide-react" + +function Calendar({ + className, + classNames, + showOutsideDays = true, + captionLayout = "label", + buttonVariant = "ghost", + locale, + formatters, + components, + ...props +}: React.ComponentProps & { + buttonVariant?: React.ComponentProps["variant"] +}) { + const defaultClassNames = getDefaultClassNames() + + return ( + svg]:rotate-180`, + String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, + className + )} + captionLayout={captionLayout} + locale={locale} + formatters={{ + formatMonthDropdown: (date) => + date.toLocaleString(locale?.code, { month: "short" }), + ...formatters, + }} + classNames={{ + root: cn("w-fit", defaultClassNames.root), + months: cn( + "relative flex flex-col gap-4 md:flex-row", + defaultClassNames.months + ), + month: cn("flex w-full flex-col gap-4", defaultClassNames.month), + nav: cn( + "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", + defaultClassNames.nav + ), + button_previous: cn( + buttonVariants({ variant: buttonVariant }), + "size-(--cell-size) p-0 select-none aria-disabled:opacity-50", + defaultClassNames.button_previous + ), + button_next: cn( + buttonVariants({ variant: buttonVariant }), + "size-(--cell-size) p-0 select-none aria-disabled:opacity-50", + defaultClassNames.button_next + ), + month_caption: cn( + "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)", + defaultClassNames.month_caption + ), + dropdowns: cn( + "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium", + defaultClassNames.dropdowns + ), + dropdown_root: cn( + "relative rounded-(--cell-radius)", + defaultClassNames.dropdown_root + ), + dropdown: cn( + "absolute inset-0 bg-popover opacity-0", + defaultClassNames.dropdown + ), + caption_label: cn( + "font-medium select-none", + captionLayout === "label" + ? "text-sm" + : "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground", + defaultClassNames.caption_label + ), + table: "w-full border-collapse", + weekdays: cn("flex", defaultClassNames.weekdays), + weekday: cn( + "flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none", + defaultClassNames.weekday + ), + week: cn("mt-2 flex w-full", defaultClassNames.week), + week_number_header: cn( + "w-(--cell-size) select-none", + defaultClassNames.week_number_header + ), + week_number: cn( + "text-[0.8rem] text-muted-foreground select-none", + defaultClassNames.week_number + ), + day: cn( + "group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)", + props.showWeekNumber + ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)" + : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)", + defaultClassNames.day + ), + range_start: cn( + "relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted", + defaultClassNames.range_start + ), + range_middle: cn("rounded-none", defaultClassNames.range_middle), + range_end: cn( + "relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted", + defaultClassNames.range_end + ), + today: cn( + "rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none", + defaultClassNames.today + ), + outside: cn( + "text-muted-foreground aria-selected:text-muted-foreground", + defaultClassNames.outside + ), + disabled: cn( + "text-muted-foreground opacity-50", + defaultClassNames.disabled + ), + hidden: cn("invisible", defaultClassNames.hidden), + ...classNames, + } as any} + components={{ + Root: ({ className, rootRef, ...props }) => { + return ( +
+ ) + }, + Chevron: ({ className, orientation, ...props }) => { + if (orientation === "left") { + return ( + + ) + } + + if (orientation === "right") { + return ( + + ) + } + + return ( + + ) + }, + DayButton: ({ ...props }) => ( + + ), + WeekNumber: ({ children, ...props }) => { + return ( + +
+ {children} +
+ + ) + }, + ...components, + }} + {...props} + /> + ) +} + +function CalendarDayButton({ + className, + day, + modifiers, + locale, + ...props +}: React.ComponentProps & { locale?: Partial }) { + const defaultClassNames = getDefaultClassNames() + + const ref = React.useRef(null) + React.useEffect(() => { + if (modifiers.focused) ref.current?.focus() + }, [modifiers.focused]) + + return ( + +
+ + + {/* Login Button */} + + + + {/* Divider */} +
+
+ Or continue with +
+
+ + {/* Google Login */} + + + {/* Register link */} +

+ Don't have an account?{" "} + + Register + +

+ + + + + + ); +} diff --git a/bmvfrontend/src/customer/auth/Register.tsx b/bmvfrontend/src/customer/auth/Register.tsx new file mode 100644 index 000000000..a4795b0dc --- /dev/null +++ b/bmvfrontend/src/customer/auth/Register.tsx @@ -0,0 +1,404 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; +import Link from "next/link"; +import { setSession } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Card, CardContent } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp"; +import { + Smartphone, + User, + Mail, + Lock, + Eye, + EyeOff, + CheckCircle, + ChevronLeft, + ShieldAlert, + Sparkles, + PartyPopper +} from "lucide-react"; + +export default function Register() { + const router = useRouter(); + const searchParams = useSearchParams(); + const returnUrl = searchParams.get("returnUrl") || "/"; + + // Step state: 1, 2, or 3 + const [step, setStep] = useState(1); + const [error, setError] = useState(""); + + // Step 1: Mobile verification + const [phone, setPhone] = useState(""); + const [otpSent, setOtpSent] = useState(false); + const [otpValue, setOtpValue] = useState(""); + const [timer, setTimer] = useState(30); + + // Step 2: Basic details + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + + // Step 3: Personalization + const [favOccasion, setFavOccasion] = useState(""); + + // OTP resend timer + useEffect(() => { + let interval: any; + if (otpSent && timer > 0) { + interval = setInterval(() => { + setTimer((prev) => prev - 1); + }, 1000); + } + return () => clearInterval(interval); + }, [otpSent, timer]); + + const handleSendOtp = (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + if (phone.length < 10) { + setError("Please enter a valid 10-digit mobile number."); + return; + } + setOtpSent(true); + setTimer(30); + }; + + const handleVerifyOtp = () => { + setError(""); + // Accept any 6 digit, but instruct to use 123456 + if (otpValue === "123456" || otpValue.length === 6) { + setStep(2); + } else { + setError("Incorrect OTP. Enter '123456' to pass."); + } + }; + + const handleResendOtp = () => { + setTimer(30); + setOtpValue(""); + setError(""); + }; + + const handleBasicDetailsSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + + if (!name.trim()) { + setError("Full Name is required."); + return; + } + if (!email.includes("@")) { + setError("Please enter a valid email address."); + return; + } + if (password.length < 6) { + setError("Password must be at least 6 characters."); + return; + } + if (password !== confirmPassword) { + setError("Passwords do not match."); + return; + } + + setStep(3); + }; + + const handleCompleteRegistration = (skipped = false) => { + // Save customer session + setSession({ + name: name, + email: email, + phone: phone, + role: "customer" + }); + + // Success redirect + router.push(returnUrl); + }; + + // Step Progress values + const progressPercent = step === 1 ? 33 : step === 2 ? 66 : 100; + + return ( +
+ + {/* Back button */} + {step > 1 && ( + + )} + +
+
+ + BookMyVenue + +

Create Account

+

+ Join BookMyVenue to discover and book premium venues. +

+
+
+ +
+ + {/* Progress Bar */} +
+
+ Step {step} of 3 + {step === 1 ? "Verification" : step === 2 ? "Basic Info" : "Personalize"} +
+ + {/* Standard nested components from UI progress if customized, otherwise default handles value */} + +
+ + + + + {error && ( +
+ + {error} +
+ )} + + {/* STEP 1: MOBILE VERIFICATION */} + {step === 1 && ( +
+ {!otpSent ? ( +
+

+ Enter your mobile number to receive a 6-digit OTP verification code. +

+
+ +
+ +
+ + + + setPhone(e.target.value.replace(/\D/g, ""))} + placeholder="9876543210" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+
+ +
+ ) : ( +
+
+ We've sent a 6-digit OTP to +91 {phone}. +

Demo Code: Enter 123456

+
+ +
+ + + + + + + + + + + + {timer > 0 ? ( + Resend OTP in {timer}s + ) : ( + + )} +
+ + +
+ )} +
+ )} + + {/* STEP 2: BASIC DETAILS */} + {step === 2 && ( +
+
+ +
+ + + + setName(e.target.value)} + placeholder="John Doe" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+ +
+ +
+ + + + setEmail(e.target.value)} + placeholder="john@example.com" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+ +
+ +
+ + + + setPassword(e.target.value)} + placeholder="Min 6 characters" + className="pl-9 pr-9 h-10 border-input rounded-xl bg-white" + /> + +
+
+ +
+ +
+ + + + setConfirmPassword(e.target.value)} + placeholder="Confirm your password" + className="pl-9 pr-9 h-10 border-input rounded-xl bg-white" + /> +
+
+ + +
+ )} + + {/* STEP 3: PERSONALIZATION QUESTIONS */} + {step === 3 && ( +
+
+ +
+ +
+

Almost Done!

+

+ Help us customize BookMyVenue to display locations and offers that matter most to you. +

+
+ +
+
+ +
+ {["Wedding", "Corporate", "Birthday", "Social"].map((occ) => ( + + ))} +
+
+
+ +
+ + +
+
+ )} + + {/* Back to Login link */} + {step < 3 && ( +

+ Already have an account?{" "} + + Login + +

+ )} + +
+
+
+
+ ); +} diff --git a/bmvfrontend/src/customer/components/Footer.tsx b/bmvfrontend/src/customer/components/Footer.tsx new file mode 100644 index 000000000..c995dd65e --- /dev/null +++ b/bmvfrontend/src/customer/components/Footer.tsx @@ -0,0 +1,139 @@ +"use client"; + +import React from "react"; +import Link from "next/link"; +import { Building2 } from "lucide-react"; + +export default function Footer() { + return ( +
+
+
+ {/* Logo and Tagline */} +
+ + + BookMyVenue + + +

+ Simplifying event planning. Find, compare, and book the perfect venue for your next wedding, meeting, or celebration. +

+ +
+ + {/* Quick Links */} +
+

For Customers

+
    +
  • + + Browse Venues + +
  • +
  • + + Customer Login + +
  • +
  • + + Sign Up + +
  • +
  • + + How It Works + +
  • +
+
+ + {/* For Partners */} +
+

For Venue Owners

+
    +
  • + + List Your Venue + +
  • +
  • + + Partner Login + +
  • +
  • + + Partner Registration + +
  • +
+
+ + {/* Cities */} +
+

Top Cities

+
    +
  • + + Kochi + +
  • +
  • + + Bangalore + +
  • +
  • + + Mumbai + +
  • +
  • + + Pune + +
  • +
+
+
+ +
+

© {new Date().getFullYear()} BookMyVenue Inc. All rights reserved.

+
+ + + Are you a Venue Partner? List your Venue + +
+
+
+
+ ); +} diff --git a/bmvfrontend/src/customer/components/Header.tsx b/bmvfrontend/src/customer/components/Header.tsx new file mode 100644 index 000000000..dd39e693b --- /dev/null +++ b/bmvfrontend/src/customer/components/Header.tsx @@ -0,0 +1,188 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import Link from "next/link"; +import { usePathname, useRouter } from "next/navigation"; +import { getSession, clearSession, UserSession } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { User, LogOut, Menu, X, Heart, Calendar } from "lucide-react"; + +export default function Header() { + const [session, setSessionState] = useState(null); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const pathname = usePathname(); + const router = useRouter(); + + // Load session on mount and when pathname changes (to reflect login status instantly) + useEffect(() => { + setSessionState(getSession()); + }, [pathname]); + + const handleLogout = () => { + clearSession(); + setSessionState(null); + router.push("/"); + }; + + const navLinks = [ + { label: "Find Venues", path: "/venues" }, + { label: "Partner Area", path: "/partner" }, + ]; + + return ( +
+
+ {/* Logo */} +
+ + + BookMyVenue + + + +
+ + {/* Desktop Actions */} +
+ {session ? ( +
+ {session.role === "partner" ? ( + + + + ) : ( +
+ Hello, {session.name} +
+
+ )} + +
+ ) : ( +
+ + + List Your Venue + + + + + + + + +
+ )} +
+ + {/* Mobile Menu Button */} +
+ {!session && ( + + + + )} + +
+
+ + {/* Mobile Drawer */} + {mobileMenuOpen && ( +
+ + +
+ + {session ? ( +
+
+

Logged in as

+

{session.name} ({session.role})

+
+ {session.role === "partner" && ( + setMobileMenuOpen(false)}> + + + )} + +
+ ) : ( +
+ setMobileMenuOpen(false)}> + + + setMobileMenuOpen(false)}> + + +
+ )} +
+ )} +
+ ); +} diff --git a/bmvfrontend/src/customer/pages/BookingConfirm.tsx b/bmvfrontend/src/customer/pages/BookingConfirm.tsx new file mode 100644 index 000000000..dc4fe742c --- /dev/null +++ b/bmvfrontend/src/customer/pages/BookingConfirm.tsx @@ -0,0 +1,280 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import Image from "next/image"; +import Header from "../components/Header"; +import Footer from "../components/Footer"; +import { getSession, addBooking } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { + Calendar, + Users, + Clock, + CheckCircle2, + Building, + ArrowRight, + ShieldCheck, + CreditCard +} from "lucide-react"; + +export default function BookingConfirm() { + const router = useRouter(); + + // Pending Booking details + const [pending, setPending] = useState(null); + const [session, setSession] = useState(null); + + // Form State + const [contactName, setContactName] = useState(""); + const [contactEmail, setContactEmail] = useState(""); + const [contactPhone, setContactPhone] = useState(""); + + // Confirmation state + const [isSuccess, setIsSuccess] = useState(false); + const [bookingId, setBookingId] = useState(""); + + useEffect(() => { + const activeSession = getSession(); + setSession(activeSession); + + const saved = localStorage.getItem("bmv_pending_booking"); + if (saved) { + const parsed = JSON.parse(saved); + setPending(parsed); + + // Auto fill if session is active + if (activeSession) { + setContactName(activeSession.name); + setContactEmail(activeSession.email); + setContactPhone(activeSession.phone || ""); + } + } else { + // If no pending booking, redirect to venues + router.push("/venues"); + } + }, [router]); + + const handleConfirmBooking = (e: React.FormEvent) => { + e.preventDefault(); + if (!pending) return; + + // Save actual booking to mock DB + const newBk = addBooking({ + venueId: pending.venueId, + venueName: pending.venueName, + venueImage: pending.venueImage, + date: pending.date, + slot: pending.slot, + guests: pending.guests, + totalPrice: pending.totalPrice + }); + + // Clear pending booking + localStorage.removeItem("bmv_pending_booking"); + + setBookingId(newBk.id); + setIsSuccess(true); + }; + + if (!pending) { + return ( +
+
+
+

Loading booking details...

+
+
+
+ ); + } + + return ( +
+
+ +
+ + {isSuccess ? ( + // GORGEOUS SUCCESS STATE +
+
+ +
+ +

Booking Confirmed!

+

+ Your venue reservation request has been processed and locked in. +

+ +
+
+ Booking ID + {bookingId} +
+
+ Venue + {pending.venueName} +
+
+ Date & Slot + {pending.date} ({pending.slot}) +
+
+ Expected Guests + {pending.guests} Guests +
+
+ Amount Paid + ₹{pending.totalPrice.toLocaleString("en-IN")} +
+
+ +
+ + + + + + +
+
+ ) : ( + // CONFIRMATION FORM & SUMMARY +
+

Review & Confirm Booking

+

+ Please double check your details and fill in the coordinator contact form. +

+ +
+ {/* Form panel */} +
+

Event Coordinator Details

+ +
+ + setContactName(e.target.value)} + placeholder="Enter coordinator name" + className="h-10 border-input rounded-xl" + /> +
+ +
+
+ + setContactEmail(e.target.value)} + placeholder="e.g. john@example.com" + className="h-10 border-input rounded-xl" + /> +
+ +
+ + setContactPhone(e.target.value)} + placeholder="e.g. 9876543210" + className="h-10 border-input rounded-xl" + /> +
+
+ +
+ +
+ Secure Transaction Guarantee + Your booking is covered by BookMyVenue's Venue Guarantee. If the partner fails to deliver the space in agreed conditions, you receive a 100% refund. +
+
+ + +
+ + {/* Summary panel */} +
+ +
+ {pending.venueName} +
+ +
+

{pending.venueName}

+

+ Venue Reservation +

+
+ +
+ +
+
+ + Date: {pending.date} +
+
+ + Slot: {pending.slot} +
+
+ + Guests: {pending.guests} Guests +
+
+ +
+ +
+
+ Base rate + ₹{pending.totalPrice ? Math.round(pending.totalPrice / 1.205).toLocaleString("en-IN") : "0"} +
+
+ Estimated Taxes + Included +
+
+ Total Price + ₹{pending.totalPrice.toLocaleString("en-IN")} +
+
+
+
+
+
+
+ )} +
+ +
+
+ ); +} diff --git a/bmvfrontend/src/customer/pages/Home.tsx b/bmvfrontend/src/customer/pages/Home.tsx new file mode 100644 index 000000000..24f53d089 --- /dev/null +++ b/bmvfrontend/src/customer/pages/Home.tsx @@ -0,0 +1,427 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import Image from "next/image"; +import Header from "../components/Header"; +import Footer from "../components/Footer"; +import { MOCK_VENUES, MOCK_CITIES, Venue } from "@/src/lib/mockData"; +import { getWishlist, toggleWishlist } from "@/src/lib/authStore"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { Badge } from "@/components/ui/badge"; +import { + MapPin, + Calendar as CalendarIcon, + Search, + Star, + Sparkles, + Building, + Users, + CreditCard, + Heart, + ChevronRight +} from "lucide-react"; + +export default function Home() { + const router = useRouter(); + + // Search Form State + const [location, setLocation] = useState(""); + const [showCities, setShowCities] = useState(false); + const [occasion, setOccasion] = useState(""); + const [date, setDate] = useState(undefined); + const [wishlist, setWishlistState] = useState([]); + + useEffect(() => { + setWishlistState(getWishlist()); + }, []); + + const handleCityClick = (city: string) => { + setLocation(city); + setShowCities(false); + }; + + const handleSearch = (e: React.FormEvent) => { + e.preventDefault(); + const dateStr = date ? date.toISOString().split("T")[0] : ""; + router.push(`/venues?location=${encodeURIComponent(location)}&occasion=${encodeURIComponent(occasion)}&date=${dateStr}`); + }; + + const handleWishlistToggle = (e: React.MouseEvent, id: string) => { + e.preventDefault(); + e.stopPropagation(); + const updated = toggleWishlist(id); + setWishlistState(updated); + }; + + // Get Top 3 rated venues for features + const featuredVenues = MOCK_VENUES.slice(0, 3); + + // Quick link categories + const categories = [ + { label: "Wedding Hall", type: "Hall", image: "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=150" }, + { label: "Conference Room", type: "Hall", image: "https://images.unsplash.com/photo-1431540015161-0bf868a2d407?auto=format&fit=crop&q=80&w=150" }, + { label: "Rooftop Lounge", type: "Rooftop", image: "https://images.unsplash.com/photo-1533105079780-92b9be482077?auto=format&fit=crop&q=80&w=150" }, + { label: "Resort Lawn", type: "Resort", image: "https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&q=80&w=150" }, + { label: "Creative Studio", type: "Studio", image: "https://images.unsplash.com/photo-1598488035139-bdbb2231ce04?auto=format&fit=crop&q=80&w=150" }, + { label: "Party Banquet", type: "Hall", image: "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=150&id2" }, + ]; + + return ( +
+
+ +
+ {/* HERO SECTION */} +
+ {/* Background image */} +
+ Luxury Wedding Hall Ambient Background + {/* Soft dark overlay */} +
+
+ +
+ {/* Accent badge */} +
+ Premium Venue Booking +
+ +

+ Find the Perfect Venue for Every Occasion +

+

+ Discover verified spaces, compare transparent pricing, and secure your booking in minutes. +

+ + {/* UNIFIED SEARCH BAR */} +
+ {/* Location Input with suggestions */} +
+ + Location + + { + setLocation(e.target.value); + setShowCities(true); + }} + onFocus={() => setShowCities(true)} + className="w-full bg-transparent border-0 outline-none text-sm font-semibold pt-1 focus:ring-0 placeholder:text-neutral-muted/70 placeholder:font-normal" + /> + + {/* Auto-suggest dropdown */} + {showCities && ( +
+
Popular Cities
+ {MOCK_CITIES.filter(c => c.toLowerCase().includes(location.toLowerCase())).map((city) => ( + + ))} + {MOCK_CITIES.filter(c => c.toLowerCase().includes(location.toLowerCase())).length === 0 && ( +
No cities matched
+ )} +
+ )} +
+ + {/* Occasion Selection */} +
+ + Occasion + + +
+ + {/* Date Popover Picker */} +
+ + Date + + + + {date ? date.toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" }) : "Pick a date"} + + + + + +
+ + {/* Search CTA */} +
+ +
+
+
+
+ + {/* TRUST STRIP */} +
+
+
+
+
+ +
+
+

2,400+ Venues

+

Verified & curated spaces

+
+
+ +
+ +
+
+ +
+
+

18 Cities

+

Across India & growing

+
+
+ +
+ +
+
+ +
+
+

50,000+ Bookings

+

Successfully hosted events

+
+
+
+
+
+ + {/* CATEGORY QUICK-LINKS */} +
+
+

Explore by Space Type

+ + {/* Scrollable container */} +
+ {categories.map((cat, idx) => ( + +
+ {cat.label} +
+
+ + {cat.label} + + + ))} +
+
+
+ + {/* FEATURED VENUES (3-col grid) */} +
+
+
+
+

Featured Selections

+

Top Picks Near You

+
+ + View All Venues + +
+ +
+ {featuredVenues.map((venue) => { + const isStarred = wishlist.includes(venue.id); + return ( +
+ {/* Venue Image */} +
+ {venue.name} + + {/* Badge Overlays */} + {venue.verified && ( + + Verified + + )} + + {/* Wishlist Heart */} + +
+ + {/* Venue Info */} +
+ {/* Badges */} +
+ {venue.occasions.map((occ) => ( + + {occ} + + ))} +
+ +

+ {venue.name} +

+

+ {venue.location} +

+ + {/* Guest and Price Meta */} +
+
+ Max {venue.capacity} guests +
+
+

Per Day

+

+ ₹{venue.pricePerDay.toLocaleString("en-IN")} +

+
+
+ + {/* View Details Link */} + + + +
+
+ ); + })} +
+ +
+ + View All Venues + +
+
+
+ + {/* HOW IT WORKS */} +
+
+

Simplicity First

+

How It Works

+ +
+
+
+ +
+

1. Search & Filter

+

+ Enter location, date, and occasion to explore local spaces. Filter by capacity, budget, and amenities. +

+
+ +
+
+ +
+

2. Explore Details

+

+ View high-resolution photo galleries, slots, exact pricing breakdowns, and venue booking policies. +

+
+ +
+
+ +
+

3. Book Securely

+

+ Select your slot and guest count, submit a booking request, and lock in your date instantly. +

+
+
+
+
+
+ +
+
+ ); +} diff --git a/bmvfrontend/src/customer/pages/VenueDetail.tsx b/bmvfrontend/src/customer/pages/VenueDetail.tsx new file mode 100644 index 000000000..dcfc28260 --- /dev/null +++ b/bmvfrontend/src/customer/pages/VenueDetail.tsx @@ -0,0 +1,629 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import Image from "next/image"; +import Header from "../components/Header"; +import Footer from "../components/Footer"; +import { MOCK_VENUES, Venue } from "@/src/lib/mockData"; +import { getSession, toggleWishlist, getWishlist } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { Badge } from "@/components/ui/badge"; +import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; +import { + MapPin, + Users, + Star, + CheckCircle2, + Calendar as CalendarIcon, + Heart, + ChevronLeft, + ChevronRight, + Maximize2, + Plus, + Minus, + Info, + Clock, + ShieldCheck, + PartyPopper, + X +} from "lucide-react"; + +interface VenueDetailProps { + id: string; +} + +export default function VenueDetail({ id }: VenueDetailProps) { + const router = useRouter(); + const venue = MOCK_VENUES.find((v) => v.id === id); + + // States + const [selectedDate, setSelectedDate] = useState(new Date()); + const [selectedSlot, setSelectedSlot] = useState<"Morning" | "Evening" | "Full Day">("Morning"); + const [guestsCount, setGuestsCount] = useState(50); + const [wishlist, setWishlistState] = useState([]); + const [lightboxOpen, setLightboxOpen] = useState(false); + const [activePhotoIdx, setActivePhotoIdx] = useState(0); + + useEffect(() => { + setWishlistState(getWishlist()); + }, []); + + if (!venue) { + return ( +
+
+
+

Venue Not Found

+

The venue you are looking for does not exist or has been removed.

+ + + +
+
+
+ ); + } + + const isStarred = wishlist.includes(venue.id); + + const handleWishlistToggle = () => { + const updated = toggleWishlist(venue.id); + setWishlistState(updated); + }; + + // Pricing calculation + const getBasePrice = () => { + if (selectedSlot === "Full Day") return venue.pricePerDay; + return venue.pricePerSlot; + }; + + const basePrice = getBasePrice(); + const gstTax = Math.round(basePrice * 0.18); // 18% GST + const serviceFee = Math.round(basePrice * 0.025); // 2.5% Service Fee + const totalPrice = basePrice + gstTax + serviceFee; + + const handleBookNow = () => { + const session = getSession(); + + // Save selection details to localStorage + const bookingParams = { + venueId: venue.id, + venueName: venue.name, + venueImage: venue.image, + date: selectedDate ? selectedDate.toLocaleDateString("en-IN") : new Date().toLocaleDateString("en-IN"), + slot: selectedSlot, + guests: guestsCount, + totalPrice: totalPrice, + }; + + localStorage.setItem("bmv_pending_booking", JSON.stringify(bookingParams)); + + if (!session) { + // Redirect to login with return path + router.push(`/login?returnUrl=${encodeURIComponent(`/booking/confirm`)}`); + } else { + router.push("/booking/confirm"); + } + }; + + const handleOpenLightbox = (index: number) => { + setActivePhotoIdx(index); + setLightboxOpen(true); + }; + + const handleNextPhoto = () => { + setActivePhotoIdx((prev) => (prev + 1) % venue.images.length); + }; + + const handlePrevPhoto = () => { + setActivePhotoIdx((prev) => (prev - 1 + venue.images.length) % venue.images.length); + }; + + // Emoji dictionary for amenities + const amenityEmojis: Record = { + Parking: "🅿", + AC: "❄", + Catering: "🍽", + "AV Equipment": "🔊", + Valet: "🚗", + WiFi: "📶", + Decoration: "✨" + }; + + return ( +
+
+ +
+ + {/* Back Link */} +
+ + Back to Venues + +
+ + {/* IMAGE GALLERY GRID */} +
+ + {/* Main Hero Photo */} +
handleOpenLightbox(0)}> + {`${venue.name} +
+
+ Tap to expand +
+
+ + {/* 2x2 Thumbnail Grid (hidden on mobile, shown as a column of 2 or grid on tablet/desktop) */} +
+ {venue.images.slice(1, 5).map((img, idx) => ( +
handleOpenLightbox(idx + 1)} + > + {`${venue.name} +
+ + {/* Overlay on last item to show remaining counts if there are more */} + {idx === 3 && venue.images.length > 5 && ( +
+ +{venue.images.length - 5} Photos +
+ )} +
+ ))} + {/* Fallback if less than 5 images */} + {Array.from({ length: Math.max(0, 4 - venue.images.slice(1, 5).length) }).map((_, i) => ( +
+ +
+ ))} +
+
+ + {/* DETAILS & STICKY BOOKING PANEL CONTAINER */} +
+ + {/* Left Side: Venue Details */} +
+ + {/* Header info */} +
+
+ + {venue.type} + + {venue.verified && ( + + Verified Venue + + )} + +
+ +

+ {venue.name} +

+ +
+

+ {venue.location} +

+
+
+ + {venue.rating.toFixed(1)} + ({venue.reviewsCount} reviews) +
+
+ +
+
+ Capacity + {venue.capacity} guests max +
+
+ Price Slot + ₹{venue.pricePerSlot.toLocaleString("en-IN")} +
+
+ Price Day + ₹{venue.pricePerDay.toLocaleString("en-IN")} +
+
+ City + {venue.city} +
+
+
+ + {/* Tab panel Overview | Amenities | Policies */} + + + Overview + Amenities + Policies + + + {/* Overview panel */} + +

About this Venue

+

+ {venue.description} +

+
+

Suitable Occasions

+
+ {venue.occasions.map((occ) => ( + + {occ} + + ))} +
+
+
+ + {/* Amenities Panel */} + +

Available Amenities

+
+ {venue.amenities.map((amenity) => ( +
+ + {amenityEmojis[amenity] || "🔹"} + + {amenity} +
+ ))} +
+
+ + {/* Policies Panel */} + +

Venue Policies & Guidelines

+
+
+
+ +
+
+

Booking Slot Times

+

Morning slots: 8:00 AM – 3:00 PM. Evening slots: 4:00 PM – 11:00 PM. Full day: 8:00 AM – 11:00 PM.

+
+
+ +
+
+ +
+
+

Cancellation Policy

+

Free cancellation up to 15 days before the event. 50% cancellation fee between 7-14 days. Non-refundable within 7 days of the booking.

+
+
+ +
+
+ +
+
+

Catering & External Vendors

+

In-house catering and decoration services are optional. External caterers and decorators are allowed with a minor vendor surcharge fee.

+
+
+
+
+
+
+ + {/* Right Side: Desktop Sticky Booking Panel */} +
+ + +
+ Booking Rate +
+ ₹{basePrice.toLocaleString("en-IN")} + /{selectedSlot === "Full Day" ? "day" : "slot"} +
+
+ +
+ + {/* Date Picker */} +
+ + + + + + {selectedDate ? selectedDate.toLocaleDateString("en-IN", { day: "numeric", month: "short", year: "numeric" }) : "Pick Date"} + + + + + + +
+ + {/* Slot Selector */} +
+ +
+ {["Morning", "Evening", "Full Day"].map((slotOption) => ( + + ))} +
+
+ + {/* Guest Count Stepper */} +
+ +
+ + {guestsCount} guests + +
+
+ + {/* Price Breakdown */} +
+
+ Base rate ({selectedSlot}) + ₹{basePrice.toLocaleString("en-IN")} +
+
+ GST (18% ) + ₹{gstTax.toLocaleString("en-IN")} +
+
+ Service Fee (2.5%) + ₹{serviceFee.toLocaleString("en-IN")} +
+
+ Estimated Total + ₹{totalPrice.toLocaleString("en-IN")} +
+
+ + {/* Booking Button */} + +
+
+
+
+ + {/* MOBILE STICKY BOTTOM PANEL (Fixed at bottom on mobile) */} +
+
+ Total Price + + ₹{totalPrice.toLocaleString("en-IN")} + +
+ + + + Configure & Book + + + + Reserve Venue + + +
+ {/* Date Picker */} +
+ + + + + + {selectedDate ? selectedDate.toLocaleDateString("en-IN", { day: "numeric", month: "short" }) : "Pick Date"} + + + + + + +
+ + {/* Slot Selector */} +
+ +
+ {["Morning", "Evening", "Full Day"].map((slotOption) => ( + + ))} +
+
+ + {/* Guest Count Stepper */} +
+ +
+ + {guestsCount} guests + +
+
+ + {/* Price Breakdown */} +
+
+ Base rate ({selectedSlot}) + ₹{basePrice.toLocaleString("en-IN")} +
+
+ GST (18%) + ₹{gstTax.toLocaleString("en-IN")} +
+
+ Service Fee (2.5%) + ₹{serviceFee.toLocaleString("en-IN")} +
+
+ Total Estimate + ₹{totalPrice.toLocaleString("en-IN")} +
+
+
+ +
+ +
+
+
+
+ + {/* LIGHTBOX MODAL */} + {lightboxOpen && ( +
+ {/* Top Bar */} +
+ + Photo {activePhotoIdx + 1} of {venue.images.length} + + +
+ + {/* Photo Slide Frame */} +
+ {/* Prev Button */} + + + {/* Slide image */} +
+ {`${venue.name} +
+ + {/* Next Button */} + +
+ + {/* Thumbnail Bottom Bar */} +
+ {venue.images.map((img, idx) => ( +
setActivePhotoIdx(idx)} + className={`relative h-12 w-16 shrink-0 rounded-md overflow-hidden cursor-pointer border-2 transition-all ${ + activePhotoIdx === idx ? "border-amber-cta opacity-100 scale-105" : "border-transparent opacity-50 hover:opacity-85" + }`} + > + {`Gallery +
+ ))} +
+
+ )} +
+ + {/* Spacing for mobile fixed bottom bar */} +
+ +
+
+ ); +} diff --git a/bmvfrontend/src/customer/pages/VenueListing.tsx b/bmvfrontend/src/customer/pages/VenueListing.tsx new file mode 100644 index 000000000..8fb707830 --- /dev/null +++ b/bmvfrontend/src/customer/pages/VenueListing.tsx @@ -0,0 +1,695 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useSearchParams, useRouter } from "next/navigation"; +import Link from "next/link"; +import Image from "next/image"; +import Header from "../components/Header"; +import Footer from "../components/Footer"; +import { MOCK_VENUES, MOCK_CITIES, Venue } from "@/src/lib/mockData"; +import { getWishlist, toggleWishlist } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { Checkbox } from "@/components/ui/checkbox"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { Slider } from "@/components/ui/slider"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Sheet, SheetContent, SheetTrigger, SheetTitle, SheetHeader } from "@/components/ui/sheet"; +import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion"; +import { + MapPin, + Users, + Grid, + List, + Heart, + SlidersHorizontal, + RotateCcw, + Star, + Sparkles, + Calendar as CalendarIcon, + Search, + Map, + X +} from "lucide-react"; + +export default function VenueListing() { + const searchParams = useSearchParams(); + const router = useRouter(); + + // URL search params + const paramLocation = searchParams.get("location") || ""; + const paramOccasion = searchParams.get("occasion") || ""; + const paramDate = searchParams.get("date") || ""; + const paramType = searchParams.get("type") || ""; + + // UI / View State + const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); + const [loading, setLoading] = useState(true); + const [wishlist, setWishlistState] = useState([]); + const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false); + const [showMockMap, setShowMockMap] = useState(false); + + // Search input state + const [searchLocation, setSearchLocation] = useState(paramLocation); + + // Filter States + const [selectedTypes, setSelectedTypes] = useState(paramType ? [paramType] : []); + const [selectedOccasion, setSelectedOccasion] = useState(paramOccasion); + const [priceRange, setPriceRange] = useState([5000, 500000]); + const [minCapacity, setMinCapacity] = useState(10); + const [selectedAmenities, setSelectedAmenities] = useState([]); + const [ratingFourPlus, setRatingFourPlus] = useState(false); + const [availableDate, setAvailableDate] = useState( + paramDate ? new Date(paramDate) : undefined + ); + + // Sort State + const [sortBy, setSortBy] = useState("relevance"); + + // Load wishlist & simulate initial page load + useEffect(() => { + setWishlistState(getWishlist()); + const timer = setTimeout(() => setLoading(false), 500); + return () => clearTimeout(timer); + }, []); + + // Update filters if search parameters change + useEffect(() => { + if (paramType) setSelectedTypes([paramType]); + if (paramOccasion) setSelectedOccasion(paramOccasion); + if (paramLocation) setSearchLocation(paramLocation); + if (paramDate) setAvailableDate(new Date(paramDate)); + }, [paramType, paramOccasion, paramLocation, paramDate]); + + // Compute active filter count + const getActiveFilterCount = () => { + let count = 0; + if (selectedTypes.length > 0) count += selectedTypes.length; + if (selectedOccasion) count += 1; + if (priceRange[0] !== 5000 || priceRange[1] !== 500000) count += 1; + if (minCapacity > 10) count += 1; + if (selectedAmenities.length > 0) count += selectedAmenities.length; + if (ratingFourPlus) count += 1; + if (availableDate) count += 1; + return count; + }; + + const activeCount = getActiveFilterCount(); + + const handleResetFilters = () => { + setSelectedTypes([]); + setSelectedOccasion(""); + setPriceRange([5000, 500000]); + setMinCapacity(10); + setSelectedAmenities([]); + setRatingFourPlus(false); + setAvailableDate(undefined); + setSearchLocation(""); + router.push("/venues"); + triggerSimulatedLoading(); + }; + + const triggerSimulatedLoading = () => { + setLoading(true); + setTimeout(() => setLoading(false), 450); + }; + + const handleTypeChange = (type: string, checked: boolean) => { + if (checked) { + setSelectedTypes([...selectedTypes, type]); + } else { + setSelectedTypes(selectedTypes.filter((t) => t !== type)); + } + }; + + const handleAmenityChange = (amenity: string, checked: boolean) => { + if (checked) { + setSelectedAmenities([...selectedAmenities, amenity]); + } else { + setSelectedAmenities(selectedAmenities.filter((a) => a !== amenity)); + } + }; + + const handleWishlistToggle = (id: string) => { + const updated = toggleWishlist(id); + setWishlistState(updated); + }; + + // Filter & Sort Logic + const filteredVenues = MOCK_VENUES.filter((venue) => { + // City / Location filter + if (searchLocation) { + const matchCity = venue.city.toLowerCase().includes(searchLocation.toLowerCase()); + const matchLoc = venue.location.toLowerCase().includes(searchLocation.toLowerCase()); + if (!matchCity && !matchLoc) return false; + } + + // Type filter + if (selectedTypes.length > 0 && !selectedTypes.includes(venue.type)) { + return false; + } + + // Occasion filter + if (selectedOccasion && !venue.occasions.includes(selectedOccasion as any)) { + return false; + } + + // Price filter (dual slider) + if (venue.pricePerDay < priceRange[0] || venue.pricePerDay > priceRange[1]) { + return false; + } + + // Capacity filter + if (venue.capacity < minCapacity) { + return false; + } + + // Amenities filter + if (selectedAmenities.length > 0) { + const hasAll = selectedAmenities.every((amenity) => venue.amenities.includes(amenity)); + if (!hasAll) return false; + } + + // Rating filter + if (ratingFourPlus && venue.rating < 4.0) { + return false; + } + + return true; + }); + + // Sort + const sortedVenues = [...filteredVenues].sort((a, b) => { + if (sortBy === "price-low") { + return a.pricePerDay - b.pricePerDay; + } + if (sortBy === "price-high") { + return b.pricePerDay - a.pricePerDay; + } + if (sortBy === "rating") { + return b.rating - a.rating; + } + // "relevance" / default + return b.reviewsCount - a.reviewsCount; + }); + + const renderFiltersContent = () => ( +
+ {/* Date Range / Availability */} +
+

Date Availability

+ + + + {availableDate ? availableDate.toLocaleDateString("en-IN") : "Select a date"} + + + { + setAvailableDate(d); + triggerSimulatedLoading(); + }} + className="bg-white border-0" + /> + + +
+ +
+ + {/* Accordions for Filter Sections */} + + {/* Venue Type Accordion */} + + Venue Type + + {["Hall", "Rooftop", "Lawn", "Auditorium", "Studio", "Resort"].map((type) => ( + + ))} + + + + {/* Occasion Accordion */} + + Occasion + + { + setSelectedOccasion(val); + triggerSimulatedLoading(); + }} + className="space-y-2.5" + > + {["Wedding", "Corporate", "Birthday", "Social"].map((occ) => ( +
+ + +
+ ))} +
+
+
+ + {/* Pricing Accordion */} + + Price Range (Per Day) + +
+ ₹{priceRange[0].toLocaleString("en-IN")} + ₹{priceRange[1].toLocaleString("en-IN")} +
+ setPriceRange(val as number[])} + onValueCommitted={triggerSimulatedLoading} + /> +
+
+ + {/* Capacity Accordion */} + + Minimum Guests + +
+ {minCapacity === 10 ? "Any capacity" : `${minCapacity}+ guests`} +
+ setMinCapacity(Array.isArray(val) ? val[0] : val)} + onValueCommitted={triggerSimulatedLoading} + /> +
+
+ + {/* Amenities Accordion */} + + Amenities + + {["Parking", "AC", "Catering", "AV Equipment", "Valet", "WiFi", "Decoration"].map((amenity) => ( + + ))} + + + + {/* Ratings Accordion */} + + Rating + + + + +
+
+ ); + + return ( +
+
+ +
+ + {/* Search header / quick location bar */} +
+
+ + + + ) => { + setSearchLocation(e.target.value); + }} + onKeyDown={(e: React.KeyboardEvent) => { + if (e.key === "Enter") triggerSimulatedLoading(); + }} + className="pl-9 h-11 bg-white border-input rounded-xl" + /> +
+ +
+
+ Active filters: {activeCount} +
+ {activeCount > 0 && ( + + )} +
+
+ + {/* MAIN LAYOUT: Sticky sidebar + scrollable panel */} +
+ + {/* Desktop Left Sidebar Filters (hidden on mobile) */} + + + {/* Right Main Panel */} +
+ + {/* Top Bar inside panel */} +
+
+ {loading ? ( + + ) : ( + + {sortedVenues.length} venues found + {searchLocation && in {searchLocation}} + + )} +
+ +
+ {/* Sort dropdown */} + + +
+ + {/* Grid/List View Toggles */} +
+ + +
+
+
+ + {/* RESULTS CONTAINER */} + {loading ? ( + // Loading skeletons +
+ {Array.from({ length: 4 }).map((_, idx) => ( +
+ +
+ + + +
+ + +
+
+
+ ))} +
+ ) : sortedVenues.length === 0 ? ( + // Empty State +
+
+ +
+

No venues match filters

+

+ We couldn't find any venues that match your current filter selections. Try broadening your budget range, decreasing guest counts, or selecting fewer amenities. +

+ +
+ ) : ( + // Result Cards (Grid or List view) +
+ {sortedVenues.map((venue) => { + const isStarred = wishlist.includes(venue.id); + return ( +
+ {/* Image Frame */} +
+ {venue.name} + {venue.verified && ( + + Verified + + )} + +
+ + {/* Content Frame */} +
+
+ {/* Badges */} +
+ + {venue.type} + + {venue.occasions.slice(0, 2).map((occ) => ( + + {occ} + + ))} +
+ +

+ {venue.name} +

+ +

+ {venue.location} +

+ +
+
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
+ {venue.rating.toFixed(1)} + ({venue.reviewsCount}) +
+
+ + {/* Price & Action Strip */} +
+
+ Per Day + + ₹{venue.pricePerDay.toLocaleString("en-IN")} + +
+ + + + +
+
+
+ ); + })} +
+ )} +
+
+ + {/* MOBILE BOTTOM ACTION BAR (Fixed on mobile bottom) */} +
+ {/* Bottom Sheet Filter Toggle */} + + + + Filter + {activeCount > 0 && ( + + {activeCount} + + )} + + + + Filters +
+ {activeCount > 0 && ( + + )} +
+
+
+ {renderFiltersContent()} +
+
+ +
+
+
+ + {/* Sort trigger on Mobile */} + + + + Sort By + + + {[ + { label: "Popularity", value: "relevance" }, + { label: "Price: Low to High", value: "price-low" }, + { label: "Price: High to Low", value: "price-high" }, + { label: "Top Rated", value: "rating" } + ].map((item) => ( + + ))} + + +
+ + {/* MAP OVERLAY POPUP MOCK */} + {showMockMap && ( +
+
+ +
+

+ Map View: Kochi Venues +

+
+
+ {/* Mock map graphic details */} +
+ [Google Maps Interactive Sandbox Placeholder] +
+ {/* Pins */} + {sortedVenues.map((v, i) => ( +
+ ₹{(v.pricePerDay / 1000).toFixed(0)}K +
+ ))} +
+
+
+ )} +
+ + {/* Extra space on mobile so bottom action bar doesn't overlay footer */} +
+ +
+
+ ); +} diff --git a/bmvfrontend/src/lib/authStore.ts b/bmvfrontend/src/lib/authStore.ts new file mode 100644 index 000000000..54eb6cfe5 --- /dev/null +++ b/bmvfrontend/src/lib/authStore.ts @@ -0,0 +1,77 @@ +"use client"; + +export interface UserSession { + name: string; + email: string; + phone?: string; + role: "customer" | "partner"; +} + +export interface Booking { + id: string; + venueId: string; + venueName: string; + venueImage: string; + date: string; + slot: "Morning" | "Evening" | "Full Day"; + guests: number; + totalPrice: number; + status: "Confirmed" | "Pending" | "Cancelled"; + bookingDate: string; +} + +export const getSession = (): UserSession | null => { + if (typeof window === "undefined") return null; + const session = localStorage.getItem("bmv_session"); + return session ? JSON.parse(session) : null; +}; + +export const setSession = (user: UserSession) => { + if (typeof window === "undefined") return; + localStorage.setItem("bmv_session", JSON.stringify(user)); +}; + +export const clearSession = () => { + if (typeof window === "undefined") return; + localStorage.removeItem("bmv_session"); +}; + +export const getWishlist = (): string[] => { + if (typeof window === "undefined") return []; + const wishlist = localStorage.getItem("bmv_wishlist"); + return wishlist ? JSON.parse(wishlist) : []; +}; + +export const toggleWishlist = (venueId: string): string[] => { + if (typeof window === "undefined") return []; + const wishlist = getWishlist(); + const index = wishlist.indexOf(venueId); + if (index > -1) { + wishlist.splice(index, 1); + } else { + wishlist.push(venueId); + } + localStorage.setItem("bmv_wishlist", JSON.stringify(wishlist)); + return wishlist; +}; + +export const getBookings = (): Booking[] => { + if (typeof window === "undefined") return []; + const bookings = localStorage.getItem("bmv_bookings"); + return bookings ? JSON.parse(bookings) : []; +}; + +export const addBooking = (booking: Omit): Booking => { + const bookings = getBookings(); + const newBooking: Booking = { + ...booking, + id: `BK-${Math.floor(100000 + Math.random() * 900000)}`, + bookingDate: new Date().toLocaleDateString(), + status: "Confirmed" + }; + bookings.push(newBooking); + if (typeof window !== "undefined") { + localStorage.setItem("bmv_bookings", JSON.stringify(bookings)); + } + return newBooking; +}; diff --git a/bmvfrontend/src/lib/mockData.ts b/bmvfrontend/src/lib/mockData.ts new file mode 100644 index 000000000..af4e1c32a --- /dev/null +++ b/bmvfrontend/src/lib/mockData.ts @@ -0,0 +1,193 @@ +export interface Venue { + id: string; + name: string; + location: string; + city: string; + type: "Hall" | "Rooftop" | "Lawn" | "Auditorium" | "Studio" | "Resort"; + occasions: ("Wedding" | "Corporate" | "Birthday" | "Social" | "Other")[]; + capacity: number; + pricePerDay: number; + pricePerSlot: number; + rating: number; + reviewsCount: number; + verified: boolean; + image: string; + images: string[]; + amenities: string[]; + description: string; +} + +export const MOCK_CITIES = [ + "Kochi", + "Bangalore", + "Mumbai", + "Chennai", + "Delhi NCR", + "Hyderabad", + "Goa", + "Pune" +]; + +export const MOCK_VENUES: Venue[] = [ + { + id: "1", + name: "The Grand Pearl Ballroom", + location: "Vyttila, Kochi", + city: "Kochi", + type: "Hall", + occasions: ["Wedding", "Corporate", "Social"], + capacity: 800, + pricePerDay: 150000, + pricePerSlot: 65000, + rating: 4.8, + reviewsCount: 142, + verified: true, + image: "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=800", + images: [ + "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=800", + "https://images.unsplash.com/photo-1464366400600-7168b8af9bc3?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1505232458627-a7272658a1c1?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1527529482837-4698179dc6ce?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1511795409834-ef04bbd61622?auto=format&fit=crop&q=80&w=600" + ], + amenities: ["Parking", "AC", "Catering", "AV Equipment", "Valet", "WiFi", "Decoration"], + description: "An exquisite ballroom offering unparalleled luxury in the heart of Kochi. Features elegant high ceilings, crystal chandeliers, a grand stage, and state-of-the-art acoustics. Ideal for weddings, large corporate events, and social galas. Our in-house culinary team serves global cuisines, and our professional planners handle every detail seamlessly." + }, + { + id: "2", + name: "Apex Conference & Event Center", + location: "Indiranagar, Bangalore", + city: "Bangalore", + type: "Hall", + occasions: ["Corporate", "Social"], + capacity: 150, + pricePerDay: 45000, + pricePerSlot: 20000, + rating: 4.5, + reviewsCount: 88, + verified: true, + image: "https://images.unsplash.com/photo-1431540015161-0bf868a2d407?auto=format&fit=crop&q=80&w=800", + images: [ + "https://images.unsplash.com/photo-1431540015161-0bf868a2d407?auto=format&fit=crop&q=80&w=800", + "https://images.unsplash.com/photo-1517502884422-41eaaced0168?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1542744094-2ab25be78b90?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1522071820081-009f0129c71c?auto=format&fit=crop&q=80&w=600" + ], + amenities: ["Parking", "AC", "AV Equipment", "WiFi"], + description: "Designed specifically for modern business meetings, seminars, and networking events. Equipped with ultra-fast WiFi, high-definition projectors, and video conferencing suites. Modular layouts allow settings from theater seating to round tables. Centrally located with abundant corporate dining options nearby." + }, + { + id: "3", + name: "Skyline Rooftop Lounge", + location: "Bandra West, Mumbai", + city: "Mumbai", + type: "Rooftop", + occasions: ["Social", "Birthday", "Other"], + capacity: 100, + pricePerDay: 85000, + pricePerSlot: 40000, + rating: 4.7, + reviewsCount: 65, + verified: false, + image: "https://images.unsplash.com/photo-1533105079780-92b9be482077?auto=format&fit=crop&q=80&w=800", + images: [ + "https://images.unsplash.com/photo-1533105079780-92b9be482077?auto=format&fit=crop&q=80&w=800", + "https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1519671482749-fd09be7ccebf?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1566737236500-c8ac43014a67?auto=format&fit=crop&q=80&w=600" + ], + amenities: ["Parking", "Catering", "AV Equipment", "Valet", "WiFi", "Decoration"], + description: "Capture the dazzling Bandra skyline and sunset views. Our rooftop lounge features sleek glass railings, comfortable lounge seating, a premium bar counter, and accent lighting. Perfect for intimate cocktail parties, birthdays, and startup launches." + }, + { + id: "4", + name: "Whispering Meadows Lawn", + location: "Kakkanad, Kochi", + city: "Kochi", + type: "Lawn", + occasions: ["Wedding", "Social"], + capacity: 1500, + pricePerDay: 210000, + pricePerSlot: 95000, + rating: 4.9, + reviewsCount: 204, + verified: true, + image: "https://images.unsplash.com/photo-1545232979-8bf34eb9757b?auto=format&fit=crop&q=80&w=800", + images: [ + "https://images.unsplash.com/photo-1545232979-8bf34eb9757b?auto=format&fit=crop&q=80&w=800", + "https://images.unsplash.com/photo-1469371670807-013ccf25f16a?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1502633017216-1676437a18a0?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1519741497674-611481863552?auto=format&fit=crop&q=80&w=600" + ], + amenities: ["Parking", "Catering", "Valet", "Decoration"], + description: "A sweeping 2-acre manicured green lawn framed by tropical palms and ambient garden lighting. Designed for massive outdoor celebrations and dream destination weddings. Features a dedicated catering prep zone, luxury dressing rooms, and a permanent elevated mandap stage." + }, + { + id: "5", + name: "Pixel & Sound Creative Studio", + location: "Koramangala, Bangalore", + city: "Bangalore", + type: "Studio", + occasions: ["Social", "Other"], + capacity: 35, + pricePerDay: 15000, + pricePerSlot: 7000, + rating: 4.2, + reviewsCount: 37, + verified: false, + image: "https://images.unsplash.com/photo-1598488035139-bdbb2231ce04?auto=format&fit=crop&q=80&w=800", + images: [ + "https://images.unsplash.com/photo-1598488035139-bdbb2231ce04?auto=format&fit=crop&q=80&w=800", + "https://images.unsplash.com/photo-1478737270239-2f02b77fc618?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1516280440614-37939bbacd6a?auto=format&fit=crop&q=80&w=600" + ], + amenities: ["AC", "AV Equipment", "WiFi"], + description: "A highly versatile, soundproofed black-box studio perfect for photography shoots, video productions, art exhibitions, podcasting, and acoustic jamming. Features adjustable overhead grid lighting, green screen backdrop options, and studio monitors." + }, + { + id: "6", + name: "The Palm Banquet Hall", + location: "Edappally, Kochi", + city: "Kochi", + type: "Hall", + occasions: ["Birthday", "Social", "Corporate"], + capacity: 300, + pricePerDay: 60000, + pricePerSlot: 28000, + rating: 4.4, + reviewsCount: 52, + verified: true, + image: "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=800&id2", + images: [ + "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=800&id2", + "https://images.unsplash.com/photo-1464366400600-7168b8af9bc3?auto=format&fit=crop&q=80&w=600&id2", + "https://images.unsplash.com/photo-1505232458627-a7272658a1c1?auto=format&fit=crop&q=80&w=600&id2" + ], + amenities: ["Parking", "AC", "Catering", "AV Equipment", "WiFi", "Decoration"], + description: "A gorgeous modern banquet hall suitable for birthdays, engagements, corporate workshops, and intimate family functions. Located near Edappally Metro station, making it highly accessible." + }, + { + id: "7", + name: "The Royal Serenity Resort & Lawn", + location: "Cherai Beach, Kochi", + city: "Kochi", + type: "Resort", + occasions: ["Wedding", "Social", "Corporate"], + capacity: 2000, + pricePerDay: 450000, + pricePerSlot: 200000, + rating: 5.0, + reviewsCount: 112, + verified: true, + image: "https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&q=80&w=800", + images: [ + "https://images.unsplash.com/photo-1540555700478-4be289fbecef?auto=format&fit=crop&q=80&w=800", + "https://images.unsplash.com/photo-1566073771259-6a8506099945?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?auto=format&fit=crop&q=80&w=600", + "https://images.unsplash.com/photo-1584132967334-10e028bd69f7?auto=format&fit=crop&q=80&w=600" + ], + amenities: ["Parking", "AC", "Catering", "AV Equipment", "Valet", "WiFi", "Decoration"], + description: "A luxury beach resort boasting a massive beachfront lawn and high-end air-conditioned banquet facilities. Located near Cherai beach, it is Kochi's premier destination for ultra-luxury weddings and elite corporate retreats." + } +]; diff --git a/bmvfrontend/src/partner/auth/PartnerLogin.tsx b/bmvfrontend/src/partner/auth/PartnerLogin.tsx new file mode 100644 index 000000000..1e70d5baf --- /dev/null +++ b/bmvfrontend/src/partner/auth/PartnerLogin.tsx @@ -0,0 +1,149 @@ +"use client"; + +import React, { useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { setSession } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Card, CardContent } from "@/components/ui/card"; +import { Eye, EyeOff, Mail, Lock, ShieldAlert } from "lucide-react"; + +export default function PartnerLogin() { + const router = useRouter(); + + // Form states + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [error, setError] = useState(""); + + const handleLogin = (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + + if (!email.includes("@")) { + setError("Please enter a valid business email."); + return; + } + if (password.length < 6) { + setError("Password must be at least 6 characters."); + return; + } + + // Set mock partner session + setSession({ + name: email.split("@")[0].charAt(0).toUpperCase() + email.split("@")[0].slice(1), + email: email, + phone: "9898989898", + role: "partner" + }); + + // Go to partner dashboard + router.push("/partner/dashboard"); + }; + + return ( +
+ + {/* Top right escape link */} +
+ + Looking to book a venue? Customer login → + +
+ +
+
+ + BookMyVenue + + Partner +

Welcome Back, Partner

+

+ Access your venue management panel, check calendar slots, and view payouts. +

+
+
+ +
+ + + + {error && ( +
+ + {error} +
+ )} + +
+ {/* Email */} +
+ +
+ + + + setEmail(e.target.value)} + placeholder="partner@yourvenue.com" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+ + {/* Password */} +
+
+ + + Forgot Password? + +
+
+ + + + setPassword(e.target.value)} + placeholder="••••••••" + className="pl-9 pr-9 h-10 border-input rounded-xl bg-white" + /> + +
+
+ + +
+ +

+ New partner?{" "} + + Register here + +

+ +
+
+
+
+ ); +} diff --git a/bmvfrontend/src/partner/auth/PartnerRegister.tsx b/bmvfrontend/src/partner/auth/PartnerRegister.tsx new file mode 100644 index 000000000..96973ded9 --- /dev/null +++ b/bmvfrontend/src/partner/auth/PartnerRegister.tsx @@ -0,0 +1,460 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { setSession } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Card, CardContent } from "@/components/ui/card"; +import { Progress } from "@/components/ui/progress"; +import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp"; +import { + Smartphone, + User, + Mail, + Lock, + Eye, + EyeOff, + Building, + MapPin, + ChevronLeft, + ShieldAlert, + Sparkles, + Store +} from "lucide-react"; + +export default function PartnerRegister() { + const router = useRouter(); + + // Step state: 1, 2, or 3 + const [step, setStep] = useState(1); + const [error, setError] = useState(""); + + // Step 1: Mobile verification + const [phone, setPhone] = useState(""); + const [otpSent, setOtpSent] = useState(false); + const [otpValue, setOtpValue] = useState(""); + const [timer, setTimer] = useState(30); + + // Step 2: Business details + const [ownerName, setOwnerName] = useState(""); + const [businessEmail, setBusinessEmail] = useState(""); + const [password, setPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [venueName, setVenueName] = useState(""); + const [city, setCity] = useState(""); + const [showPassword, setShowPassword] = useState(false); + + // Step 3: Venue Onboarding Questions + const [venueType, setVenueType] = useState(""); + const [venueCapacity, setVenueCapacity] = useState(""); + + // OTP resend timer + useEffect(() => { + let interval: any; + if (otpSent && timer > 0) { + interval = setInterval(() => { + setTimer((prev) => prev - 1); + }, 1000); + } + return () => clearInterval(interval); + }, [otpSent, timer]); + + const handleSendOtp = (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + if (phone.length < 10) { + setError("Please enter a valid 10-digit mobile number."); + return; + } + setOtpSent(true); + setTimer(30); + }; + + const handleVerifyOtp = () => { + setError(""); + if (otpValue === "123456" || otpValue.length === 6) { + setStep(2); + } else { + setError("Incorrect OTP. Enter '123456' to pass."); + } + }; + + const handleResendOtp = () => { + setTimer(30); + setOtpValue(""); + setError(""); + }; + + const handleBusinessDetailsSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setError(""); + + if (!ownerName.trim()) { + setError("Owner/Manager name is required."); + return; + } + if (!businessEmail.includes("@")) { + setError("Please enter a valid business email."); + return; + } + if (password.length < 6) { + setError("Password must be at least 6 characters."); + return; + } + if (password !== confirmPassword) { + setError("Passwords do not match."); + return; + } + if (!venueName.trim()) { + setError("Venue Name is required."); + return; + } + if (!city.trim()) { + setError("City is required."); + return; + } + + setStep(3); + }; + + const handleCompleteRegistration = (skipped = false) => { + // Save partner session + setSession({ + name: ownerName, + email: businessEmail, + phone: phone, + role: "partner" + }); + + // Go to dashboard + router.push("/partner/dashboard"); + }; + + const progressPercent = step === 1 ? 33 : step === 2 ? 66 : 100; + + return ( +
+ + {/* Back button */} + {step > 1 && ( + + )} + +
+
+ + BookMyVenue + + Partner +

Register Venue Business

+

+ Partner with us to capture local inquiries and bookings. +

+
+
+ +
+ + {/* Progress Bar */} +
+
+ Step {step} of 3 + {step === 1 ? "Owner Verify" : step === 2 ? "Business Info" : "Venue Details"} +
+ +
+ + + + + {error && ( +
+ + {error} +
+ )} + + {/* STEP 1: MOBILE VERIFICATION */} + {step === 1 && ( +
+ {!otpSent ? ( +
+

+ Enter your mobile number to verify your partner profile identity. +

+
+ +
+ +
+ + + + setPhone(e.target.value.replace(/\D/g, ""))} + placeholder="9876543210" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+
+ +
+ ) : ( +
+
+ We've sent a 6-digit OTP code to +91 {phone}. +

Demo Code: Enter 123456

+
+ +
+ + + + + + + + + + + + {timer > 0 ? ( + Resend OTP in {timer}s + ) : ( + + )} +
+ + +
+ )} +
+ )} + + {/* STEP 2: BUSINESS DETAILS */} + {step === 2 && ( +
+
+
+ +
+ + + + setOwnerName(e.target.value)} + placeholder="Rajesh Kumar" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+ +
+ +
+ + + + setBusinessEmail(e.target.value)} + placeholder="rajesh@banquets.com" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+
+ +
+
+ +
+ + + + setPassword(e.target.value)} + placeholder="Min 6 characters" + className="pl-9 pr-9 h-10 border-input rounded-xl bg-white" + /> + +
+
+ +
+ +
+ + + + setConfirmPassword(e.target.value)} + placeholder="Confirm Password" + className="pl-9 pr-9 h-10 border-input rounded-xl bg-white" + /> +
+
+
+ +
+ +
+
+ +
+ + + + setVenueName(e.target.value)} + placeholder="Pearl Banquet Hall" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+ +
+ +
+ + + + setCity(e.target.value)} + placeholder="Kochi" + className="pl-9 h-10 border-input rounded-xl bg-white" + /> +
+
+
+ + +
+ )} + + {/* STEP 3: VENUE ONBOARDING QUESTIONS */} + {step === 3 && ( +
+
+ +
+ +
+

Tell Us About Your Venue

+

+ Set up your first listing to start receiving booking requests. +

+
+ +
+
+ + +
+ +
+ + setVenueCapacity(e.target.value)} + placeholder="e.g. 500" + className="h-10 border-input rounded-xl bg-white" + /> +
+
+ +
+ + +
+
+ )} + + {/* Back to Login link */} + {step < 3 && ( +

+ Already registered?{" "} + + Login here + +

+ )} + +
+
+
+
+ ); +} diff --git a/bmvfrontend/src/partner/pages/Dashboard.tsx b/bmvfrontend/src/partner/pages/Dashboard.tsx new file mode 100644 index 000000000..37179a01e --- /dev/null +++ b/bmvfrontend/src/partner/pages/Dashboard.tsx @@ -0,0 +1,300 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { getSession, clearSession, UserSession } from "@/src/lib/authStore"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Users, + Calendar, + Building, + TrendingUp, + LogOut, + Plus, + Clock, + CheckCircle, + XCircle, + MessageSquare, + ShieldCheck, + Star +} from "lucide-react"; + +export default function Dashboard() { + const router = useRouter(); + const [session, setSessionState] = useState(null); + + useEffect(() => { + const activeSession = getSession(); + if (!activeSession || activeSession.role !== "partner") { + router.push("/partner/login"); + } else { + setSessionState(activeSession); + } + }, [router]); + + const handleLogout = () => { + clearSession(); + router.push("/partner/login"); + }; + + const recentBookings = [ + { id: "BK-482019", name: "Ananya Nair", email: "ananya@gmail.com", date: "24/06/2026", slot: "Full Day", guests: 600, amount: 178500, status: "Confirmed" }, + { id: "BK-903827", name: "Viren Joseph", email: "viren@outlook.com", date: "30/06/2026", slot: "Evening", guests: 120, amount: 77375, status: "Pending" }, + { id: "BK-123490", name: "Kunal Shah", email: "kunal@cred.com", date: "05/07/2026", slot: "Morning", guests: 80, amount: 48625, status: "Pending" }, + { id: "BK-339841", name: "Manoj Kumar", email: "manoj@gmail.com", date: "12/07/2026", slot: "Full Day", guests: 1200, amount: 247000, status: "Cancelled" } + ]; + + if (!session) { + return ( +
+

Verifying partner session...

+
+ ); + } + + return ( +
+ + {/* Dashboard Top Header */} +
+
+ + BookMyVenue + + Partner Console +
+ +
+
+ Hello, {session.name} + Partner Account +
+
+ +
+
+ +
+ + {/* Main Content Area */} +
+ + {/* Welcome banner */} +
+
+

Venue Management Overview

+

Manage live availability slot grids, approve user booking requests, and audit payments.

+
+ +
+ + {/* Stats strip */} +
+ + +
+ Venue Views + 14,250 + + +12% this month + +
+
+ +
+
+
+ + + +
+ Confirmed Bookings + 34 + + +8% this month + +
+
+ +
+
+
+ + + +
+ Gross Earnings + ₹12.4L + + +24% this month + +
+
+ +
+
+
+ + + +
+ Average Rating + 4.8 +
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
+
+
+ +
+
+
+
+ + {/* Recent Bookings table */} +
+
+

Recent Booking Requests

+ Auto-Refresh Active +
+ +
+ + + + + + + + + + + + + + {recentBookings.map((bk) => ( + + + + + + + + + + ))} + +
Booking IDCustomerDate / SlotGuestsEarning AmountStatusActions
{bk.id} + {bk.name} + {bk.email} + + {bk.date} + {bk.slot} + {bk.guests}₹{bk.amount.toLocaleString("en-IN")} + + {bk.status === "Confirmed" ? : bk.status === "Pending" ? : } + {bk.status} + + + {bk.status === "Pending" ? ( +
+ + +
+ ) : ( + - + )} +
+
+
+
+ + {/* Sidebar panels */} + + +
+
+ ); +} diff --git a/bmvfrontend/src/partner/pages/PartnerLanding.tsx b/bmvfrontend/src/partner/pages/PartnerLanding.tsx new file mode 100644 index 000000000..5561612fe --- /dev/null +++ b/bmvfrontend/src/partner/pages/PartnerLanding.tsx @@ -0,0 +1,357 @@ +"use client"; + +import React, { useState } from "react"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion"; +import { + TrendingUp, + Percent, + Calendar, + Award, + BarChart3, + ChevronRight, + ShieldCheck, + CheckCircle, + HelpCircle, + Building, + Menu, + X +} from "lucide-react"; + +export default function PartnerLanding() { + const [mobileNavOpen, setMobileNavOpen] = useState(false); + + const benefits = [ + { + icon: , + title: "0% Commission", + desc: "Pay absolute zero commission fees on your first 10 bookings. Keep 100% of your earnings." + }, + { + icon: , + title: "Real-time Booking Manager", + desc: "Manage slot availability, guest details, and slot pricing from a simplified calendar dashboard." + }, + { + icon: , + title: "Verified Partner Badge", + desc: "Gain customer trust and double search click rates with our verified venue badge of quality." + }, + { + icon: , + title: "Analytics Dashboard", + desc: "Monitor views, inquiry click rates, and monthly revenue performance with simplified graphs." + } + ]; + + const testimonials = [ + { + quote: "Joining BookMyVenue boosted our monthly bookings by 45%. The calendar manager saved us hours of coordination work.", + author: "Rajesh Kumar", + city: "Kochi", + role: "Owner, Pearl Banquets" + }, + { + quote: "The zero-commission offer for the first 10 bookings let us test the platform with zero risk. Highly recommended!", + author: "Sneha Hegde", + city: "Bangalore", + role: "Manager, Indiranagar Creative Studio" + } + ]; + + const faqs = [ + { + q: "How does BookMyVenue charge venue partners?", + a: "We offer a zero-commission model for your first 10 bookings. After that, we charge a flat 3% platform servicing fee per booking, with no hidden subscription or listing costs." + }, + { + q: "How long does it take for my venue to go live?", + a: "Onboarding takes less than 10 minutes. Once you submit details and proof of ownership/licenses, our moderation team verifies the listing within 24 hours." + }, + { + q: "Can I manage multiple venues under one account?", + a: "Yes! Our partner dashboard is fully multi-venue capable. You can add, edit, and manage calendars for multiple banquet halls, lawns, or rooftops under a single partner profile." + }, + { + q: "How are payments routed to partners?", + a: "Customers pay secure booking amounts online. Payouts are directly deposited into your registered business bank account within 24 hours of successful event completion." + } + ]; + + return ( +
+ + {/* PARTNER CUSTOM HEADER */} +
+
+
+ + + BookMyVenue + + Partner + + +
+ +
+ + + + + + +
+ + +
+ + {/* Mobile menu */} + {mobileNavOpen && ( + + )} +
+ +
+ + {/* HERO SECTION */} +
+
+
+ + {/* Left text */} +
+
+ High Conversion Listing +
+

+ Grow Your Venue Business with BookMyVenue +

+

+ List your banquet halls, outdoor lawns, rooftops, or studios on India's premier venue booking network. Receive direct inquiries, manage automated slots, and boost booking revenue. +

+
+ + + + + + +
+
+ + {/* Right Mockup */} +
+
+
+ Partner Dashboard Mock + +
+ {/* Grid mockup graphic */} +
+
+ Views + 14,250 +
+
+ Bookings + 34 +
+
+ Revenue + ₹12.4L +
+
+ {/* Graph line visualization */} +
+ {Array.from({ length: 12 }).map((_, i) => ( +
+ ))} +
+
+
+ +
+
+
+ + {/* BENEFITS STRIP */} +
+
+
+

Why Partner With Us

+

Designed to Help Your Business Grow

+
+ +
+ {benefits.map((b, idx) => ( +
+
+ {b.icon} +
+

{b.title}

+

{b.desc}

+
+ ))} +
+
+
+ + {/* HOW IT WORKS FOR PARTNERS */} +
+
+

Simplified Onboarding

+

How it Works

+ +
+
+
+ 1 +
+

Register Your Business

+

+ Submit mobile verification, business ownership documents, and details of your staff/managers. +

+
+ +
+
+ 2 +
+

Upload Venue Listings

+

+ Add photos, select your capacity limits, list available amenities, and configure your slot pricing structure. +

+
+ +
+
+ 3 +
+

Start Receiving Bookings

+

+ Manage slot reservations from our calendar app, capture reviews, and enjoy direct bank transfers. +

+
+
+
+
+ + {/* TESTIMONIALS / SOCIAL PROOF */} +
+
+
+

Success Stories

+

Trusted by Venue Owners

+
+ +
+ {testimonials.map((t, idx) => ( +
+

+ "{t.quote}" +

+
+

{t.author}

+

{t.role} · {t.city}

+
+
+ ))} +
+
+
+ + {/* FAQS SECTION */} +
+
+
+

+ Have Questions? +

+

Frequently Asked Questions

+
+ + + {faqs.map((faq, idx) => ( + + + {faq.q} + + + {faq.a} + + + ))} + +
+
+ + {/* FINAL CTA BANNER */} +
+
+

+ Ready to list your venue? +

+

+ Register now to take advantage of zero-commission bookings and double your local visibility. +

+
+ + + + + + +
+
+
+
+ + {/* FOOTER */} +
+

© {new Date().getFullYear()} BookMyVenue Partner Network. All rights reserved.

+
+
+ ); +} diff --git a/bmvfrontend/tsconfig.json b/bmvfrontend/tsconfig.json new file mode 100644 index 000000000..3a13f90a7 --- /dev/null +++ b/bmvfrontend/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} From c48b98a2feaad29915cda626dca4881965c9d1f1 Mon Sep 17 00:00:00 2001 From: Gokul Shaji Date: Thu, 11 Jun 2026 23:34:56 +0530 Subject: [PATCH 03/16] fix:auth bug testing --- bmvfrontend/app/api/[...path]/route.ts | 175 +++++++++++++++++ bmvfrontend/app/customer/register/page.tsx | 2 +- bmvfrontend/app/login/page.tsx | 2 +- bmvfrontend/app/register/page.tsx | 2 +- bmvfrontend/proxy.ts | 55 ++++++ bmvfrontend/src/auth/AuthDto.ts | 0 bmvfrontend/src/{customer => }/auth/Login.tsx | 43 ++-- .../src/{customer => }/auth/Register.tsx | 183 ++++++++---------- bmvfrontend/src/auth/route.ts | 39 ++++ bmvfrontend/src/customer/pages/Home.tsx | 54 +++--- bmvfrontend/src/customer/pages/Profile.tsx | 5 + bmvfrontend/src/hooks/auth.ts | 25 +++ bmvfrontend/src/lib/api.ts | 71 +++++++ 13 files changed, 511 insertions(+), 145 deletions(-) create mode 100644 bmvfrontend/app/api/[...path]/route.ts create mode 100644 bmvfrontend/proxy.ts create mode 100644 bmvfrontend/src/auth/AuthDto.ts rename bmvfrontend/src/{customer => }/auth/Login.tsx (89%) rename bmvfrontend/src/{customer => }/auth/Register.tsx (73%) create mode 100644 bmvfrontend/src/auth/route.ts create mode 100644 bmvfrontend/src/customer/pages/Profile.tsx create mode 100644 bmvfrontend/src/hooks/auth.ts create mode 100644 bmvfrontend/src/lib/api.ts diff --git a/bmvfrontend/app/api/[...path]/route.ts b/bmvfrontend/app/api/[...path]/route.ts new file mode 100644 index 000000000..ab942720f --- /dev/null +++ b/bmvfrontend/app/api/[...path]/route.ts @@ -0,0 +1,175 @@ +import { NextRequest, NextResponse } from "next/server"; +import { cookies } from "next/headers"; + +const BACKEND_URL = process.env.BACKEND_INTERNAL_URL || "http://localhost:5000/api"; + +async function handleProxy(req: NextRequest, { params }: { params: Promise<{ path: string[] }> | { path: string[] } }) { + // In Next.js 15+, params is a Promise. Let's await it to be compatible. + const resolvedParams = await (params instanceof Promise ? params : Promise.resolve(params)); + const pathStr = resolvedParams.path.join("/"); + const query = req.nextUrl.search; + const targetUrl = `${BACKEND_URL}/${pathStr}${query}`; + + const cookieStore = await cookies(); + let accessToken = cookieStore.get("bmv_access_token")?.value; + let refreshToken = cookieStore.get("bmv_refresh_token")?.value; + + // 1. Intercept Logout + if (pathStr === "auth/logout" && req.method === "POST") { + try { + if (refreshToken) { + await fetch(`${BACKEND_URL}/auth/logout`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ refreshToken }), + }); + } + } catch (err) { + console.error("Backend logout error in proxy:", err); + } + + // Clear browser cookies + cookieStore.delete("bmv_access_token"); + cookieStore.delete("bmv_refresh_token"); + cookieStore.delete("bmv_user_role"); + return NextResponse.json({ message: "Logged out successfully" }); + } + + // 2. Block direct manual refresh requests from client-side JS + if (pathStr === "auth/refresh") { + return NextResponse.json({ message: "Refresh token rotation is handled automatically by proxy." }, { status: 400 }); + } + + // Helper to fetch backend with bearer token + const makeRequest = async (token?: string) => { + const headers = new Headers(req.headers); + headers.delete("host"); + headers.delete("connection"); + headers.delete("content-length"); // Let fetch recalculate content length + + if (token) { + headers.set("Authorization", `Bearer ${token}`); + } else { + headers.delete("Authorization"); + } + + const options: RequestInit = { + method: req.method, + headers, + }; + + if (req.method !== "GET" && req.method !== "HEAD") { + // Read request body as ArrayBuffer to handle both JSON and binary payloads (e.g. multipart file uploads) + const arrayBuffer = await req.arrayBuffer(); + if (arrayBuffer.byteLength > 0) { + options.body = arrayBuffer; + } + } + + return fetch(targetUrl, options); + }; + + // 3. Make initial request to backend + let response = await makeRequest(accessToken); + + // 4. Intercept 401 Unauthorized to perform silent token refresh + if (response.status === 401 && refreshToken) { + try { + const refreshResponse = await fetch(`${BACKEND_URL}/auth/refresh`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ refreshToken }), + }); + + if (refreshResponse.ok) { + const refreshData = await refreshResponse.json(); + accessToken = refreshData.accessToken; + refreshToken = refreshData.refreshToken; + + // Save rotated tokens in secure HttpOnly cookies + cookieStore.set("bmv_access_token", accessToken!, { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + path: "/", + maxAge: 15 * 60, // 15 minutes + }); + + cookieStore.set("bmv_refresh_token", refreshToken!, { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + path: "/", + maxAge: 7 * 24 * 60 * 60, // 7 days + }); + + // Retry the original request with the new access token + response = await makeRequest(accessToken); + } else { + // Refresh token has expired/invalidated, clear credentials + cookieStore.delete("bmv_access_token"); + cookieStore.delete("bmv_refresh_token"); + cookieStore.delete("bmv_user_role"); + } + } catch (err) { + console.error("Token refresh failed in proxy:", err); + } + } + + // 5. Intercept Login or Registration success to save cookies and sanitize client response + const isAuthSuccess = (pathStr === "auth/login" || pathStr === "users/register") && req.method === "POST" && response.ok; + if (isAuthSuccess) { + try { + const responseData = await response.json(); + const { accessToken: newAccessToken, refreshToken: newRefreshToken, user } = responseData; + + cookieStore.set("bmv_access_token", newAccessToken, { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + path: "/", + maxAge: 15 * 60, + }); + + cookieStore.set("bmv_refresh_token", newRefreshToken, { + httpOnly: true, + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + path: "/", + maxAge: 7 * 24 * 60 * 60, + }); + + cookieStore.set("bmv_user_role", user.role, { + httpOnly: false, // Let client JS read it for quick UI layouts + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + path: "/", + maxAge: 7 * 24 * 60 * 60, + }); + + // Strip tokens from JSON body before sending to client JS + return NextResponse.json({ user }); + } catch (err) { + console.error("Authentication mapping failed in proxy:", err); + } + } + + // 6. Return response with original headers and status + const responseData = await response.arrayBuffer(); + const responseHeaders = new Headers(response.headers); + responseHeaders.delete("transfer-encoding"); + responseHeaders.delete("content-encoding"); + + return new NextResponse(responseData, { + status: response.status, + headers: responseHeaders, + }); +} + +export { + handleProxy as GET, + handleProxy as POST, + handleProxy as PUT, + handleProxy as PATCH, + handleProxy as DELETE +}; diff --git a/bmvfrontend/app/customer/register/page.tsx b/bmvfrontend/app/customer/register/page.tsx index db589ad12..820063f28 100644 --- a/bmvfrontend/app/customer/register/page.tsx +++ b/bmvfrontend/app/customer/register/page.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import Register from "@/src/customer/auth/Register"; +import Register from "@/src/auth/Register"; export default function Page() { return ( diff --git a/bmvfrontend/app/login/page.tsx b/bmvfrontend/app/login/page.tsx index e5256a36f..60e9935a2 100644 --- a/bmvfrontend/app/login/page.tsx +++ b/bmvfrontend/app/login/page.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import Login from "@/src/customer/auth/Login"; +import Login from "@/src/auth/Login"; export default function Page() { return ( diff --git a/bmvfrontend/app/register/page.tsx b/bmvfrontend/app/register/page.tsx index db589ad12..820063f28 100644 --- a/bmvfrontend/app/register/page.tsx +++ b/bmvfrontend/app/register/page.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import Register from "@/src/customer/auth/Register"; +import Register from "@/src/auth/Register"; export default function Page() { return ( diff --git a/bmvfrontend/proxy.ts b/bmvfrontend/proxy.ts new file mode 100644 index 000000000..6004c12f2 --- /dev/null +++ b/bmvfrontend/proxy.ts @@ -0,0 +1,55 @@ +import { NextResponse } from "next/server"; +import type { NextRequest } from "next/server"; + +export function proxy(req: NextRequest) { + const pathname = req.nextUrl.pathname; + + // Read cookies set by BFF proxy + const hasRefreshToken = req.cookies.has("bmv_refresh_token"); + const userRole = req.cookies.get("bmv_user_role")?.value; + + // Define public sub-routes that should not be blocked/redirected + const publicPaths = [ + "/customer/register", + "/partner/login", + "/partner/register", + "/partner" // The public partner landing/marketing page + ]; + + if (publicPaths.some(path => pathname === path)) { + return NextResponse.next(); + } + + // 1. Protect Customer & Booking routes (must be logged in as 'customer') + const isCustomerRoute = pathname.startsWith("/customer") || pathname.startsWith("/booking"); + if (isCustomerRoute) { + if (!hasRefreshToken || userRole !== "customer") { + const loginUrl = req.nextUrl.clone(); + loginUrl.pathname = "/login"; + loginUrl.search = `returnUrl=${encodeURIComponent(req.nextUrl.pathname + req.nextUrl.search)}`; + return NextResponse.redirect(loginUrl); + } + } + + // 2. Protect Partner routes (must be logged in as 'venue_owner') + const isPartnerRoute = pathname.startsWith("/partner"); + if (isPartnerRoute) { + if (!hasRefreshToken || userRole !== "venue_owner") { + const partnerLoginUrl = req.nextUrl.clone(); + partnerLoginUrl.pathname = "/partner/login"; + partnerLoginUrl.search = `returnUrl=${encodeURIComponent(req.nextUrl.pathname + req.nextUrl.search)}`; + return NextResponse.redirect(partnerLoginUrl); + } + } + + return NextResponse.next(); +} + +// Next.js Middleware Matcher configuration +export const config = { + matcher: [ + "/customer/:path*", + "/partner/:path*", + "/booking/:path*" + ], +}; diff --git a/bmvfrontend/src/auth/AuthDto.ts b/bmvfrontend/src/auth/AuthDto.ts new file mode 100644 index 000000000..e69de29bb diff --git a/bmvfrontend/src/customer/auth/Login.tsx b/bmvfrontend/src/auth/Login.tsx similarity index 89% rename from bmvfrontend/src/customer/auth/Login.tsx rename to bmvfrontend/src/auth/Login.tsx index d18924533..d2afc1cf0 100644 --- a/bmvfrontend/src/customer/auth/Login.tsx +++ b/bmvfrontend/src/auth/Login.tsx @@ -4,6 +4,7 @@ import React, { useState, useEffect } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import Link from "next/link"; import { setSession } from "@/src/lib/authStore"; +import { apiFetch } from "@/src/lib/api"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardContent } from "@/components/ui/card"; @@ -20,11 +21,11 @@ export default function Login() { const [showPassword, setShowPassword] = useState(false); const [error, setError] = useState(""); - const handleLogin = (e: React.FormEvent) => { + const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setError(""); - // Validation mock + // Validation if (!email.includes("@")) { setError("Please enter a valid email address."); return; @@ -34,16 +35,34 @@ export default function Login() { return; } - // Set mock customer session - setSession({ - name: email.split("@")[0].charAt(0).toUpperCase() + email.split("@")[0].slice(1), - email: email, - phone: "9876543210", - role: "customer" - }); - - // Navigate to return URL or home - router.push(returnUrl); + try { + const data = await apiFetch<{ + user: { + id: string; + name: string; + email: string; + phone: string; + role: string; + isProfileCompleted: boolean; + }; + }>("/auth/login", { + method: "POST", + body: { email, password }, + }); + + // Set user session in UI store (secrets/tokens are secured in HttpOnly cookies) + setSession({ + name: data.user.name, + email: data.user.email, + phone: data.user.phone, + role: (data.user.role === "venue_owner" ? "partner" : "customer") as "customer" | "partner", + }); + + // Navigate to return URL or home + router.push(returnUrl); + } catch (err: any) { + setError(err.message || "Failed to log in. Please try again."); + } }; const handleGoogleLogin = () => { diff --git a/bmvfrontend/src/customer/auth/Register.tsx b/bmvfrontend/src/auth/Register.tsx similarity index 73% rename from bmvfrontend/src/customer/auth/Register.tsx rename to bmvfrontend/src/auth/Register.tsx index a4795b0dc..3a57ab998 100644 --- a/bmvfrontend/src/customer/auth/Register.tsx +++ b/bmvfrontend/src/auth/Register.tsx @@ -22,8 +22,17 @@ import { Sparkles, PartyPopper } from "lucide-react"; +import { sendOtp, verifyOtp, register } from "./route"; +import Home from "../customer/pages/Home"; + +export interface VerifyOtpResponse { + verified: boolean; + phone: string; + phoneVerifiedToken: string; +} export default function Register() { + const router = useRouter(); const searchParams = useSearchParams(); const returnUrl = searchParams.get("returnUrl") || "/"; @@ -42,12 +51,14 @@ export default function Register() { const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); - const [confirmPassword, setConfirmPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); // Step 3: Personalization const [favOccasion, setFavOccasion] = useState(""); + const [phoneToken, setPhoneToken] = useState(null); + const role = "customer"; // OTP resend timer useEffect(() => { let interval: any; @@ -59,24 +70,39 @@ export default function Register() { return () => clearInterval(interval); }, [otpSent, timer]); - const handleSendOtp = (e: React.FormEvent) => { + const handleSendOtp = async (e: React.FormEvent) => { e.preventDefault(); - setError(""); + // setError(""); if (phone.length < 10) { setError("Please enter a valid 10-digit mobile number."); return; } - setOtpSent(true); - setTimer(30); + + try { + await sendOtp(phone); + console.log(phone) + setOtpSent(true); + setTimer(30); + } catch (error) { + setError("Failed to send OTP. Please try again."); + console.error(error); + } }; + const handleVerifyOtp = async () => { + if (otpValue.length < 6) { + setError("Please enter a valid 6-digit Otp"); + return; + } - const handleVerifyOtp = () => { - setError(""); - // Accept any 6 digit, but instruct to use 123456 - if (otpValue === "123456" || otpValue.length === 6) { - setStep(2); - } else { - setError("Incorrect OTP. Enter '123456' to pass."); + try { + const response = await verifyOtp(phone, otpValue); + console.log(response); + setPhoneToken(response.phoneVerifiedToken); + setStep(2) + } + catch (error) { + setError("Failed to verify"); + console.error(error); } }; @@ -86,7 +112,7 @@ export default function Register() { setError(""); }; - const handleBasicDetailsSubmit = (e: React.FormEvent) => { + const handleBasicDetailsSubmit = async (e: React.FormEvent) => { e.preventDefault(); setError(""); @@ -98,49 +124,52 @@ export default function Register() { setError("Please enter a valid email address."); return; } - if (password.length < 6) { - setError("Password must be at least 6 characters."); + if (password.length < 8) { + setError("Password must be at least 8 characters."); return; } - if (password !== confirmPassword) { - setError("Passwords do not match."); + if (!phoneToken) { + setError("Please verify OTP first"); return; } - setStep(3); - }; + console.log(name); + console.log(phone); + console.log(email); + console.log(password); + console.log(phoneToken); + console.log(role); + try { + await register(name, phone, email, password, phoneToken, role); + + router.push('/Home'); + } + catch (error) { + setError("Failed to register"); + console.error(error); + } + - const handleCompleteRegistration = (skipped = false) => { - // Save customer session - setSession({ - name: name, - email: email, - phone: phone, - role: "customer" - }); - - // Success redirect - router.push(returnUrl); }; + // const handleCompleteRegistration = (skipped = false) => { + // // Save customer session + // setSession({ + // name: name, + // email: email, + // phone: phone, + // role: "customer" + // }); + + // // Success redirect + // router.push(returnUrl); + // }; + // Step Progress values - const progressPercent = step === 1 ? 33 : step === 2 ? 66 : 100; + const progressPercent = step === 1 ? 50 : step === 2 ? 100 : 100; return (
- - {/* Back button */} - {step > 1 && ( - - )}
@@ -155,11 +184,11 @@ export default function Register() {
- + {/* Progress Bar */}
- Step {step} of 3 + Step {step} of 2 {step === 1 ? "Verification" : step === 2 ? "Basic Info" : "Personalize"}
@@ -169,7 +198,7 @@ export default function Register() { - + {error && (
@@ -203,7 +232,7 @@ export default function Register() { maxLength={10} value={phone} onChange={(e) => setPhone(e.target.value.replace(/\D/g, ""))} - placeholder="9876543210" + placeholder="**********" className="pl-9 h-10 border-input rounded-xl bg-white" />
@@ -217,7 +246,7 @@ export default function Register() {
We've sent a 6-digit OTP to +91 {phone}. -

Demo Code: Enter 123456

+
@@ -310,7 +339,7 @@ export default function Register() {
-
+ {/*
@@ -325,7 +354,7 @@ export default function Register() { className="pl-9 pr-9 h-10 border-input rounded-xl bg-white" />
-
+
*/} - ))} -
-
-
- -
- - -
-
- )} - {/* Back to Login link */} {step < 3 && (

Already have an account?{" "} diff --git a/bmvfrontend/src/auth/route.ts b/bmvfrontend/src/auth/route.ts new file mode 100644 index 000000000..27ec993c0 --- /dev/null +++ b/bmvfrontend/src/auth/route.ts @@ -0,0 +1,39 @@ +import { POST } from "@/app/api/[...path]/route"; +import { apiFetch } from "@/src/lib/api"; + +export interface VerifyOtpResponse { + verified: boolean; + phone: string; + phoneVerifiedToken: string; +} + + +export const sendOtp = ( + phone: string +) => { + return apiFetch(`/auth/send-otp`, { + method: "POST", + body: { phone } + }) +} + +export const verifyOtp = ( + phone: string, + otp: string +) => { + return apiFetch( + "/auth/verify-otp", { + method: "POST", + body: { phone, otp } + }) +} + +export const register = ( + name: string, phone: string, email: string, password: string, phoneToken: string, role: string +) => { + return apiFetch(`/users/register`, { + method: "POST", + body: { name, phone, email, password, phoneVerifiedToken: phoneToken, role } + }) + +} \ No newline at end of file diff --git a/bmvfrontend/src/customer/pages/Home.tsx b/bmvfrontend/src/customer/pages/Home.tsx index 24f53d089..6c4d58a1c 100644 --- a/bmvfrontend/src/customer/pages/Home.tsx +++ b/bmvfrontend/src/customer/pages/Home.tsx @@ -35,7 +35,7 @@ import { export default function Home() { const router = useRouter(); - + // Search Form State const [location, setLocation] = useState(""); const [showCities, setShowCities] = useState(false); @@ -81,7 +81,7 @@ export default function Home() { return (

- +
{/* HERO SECTION */}
@@ -101,23 +101,15 @@ export default function Home() {
{/* Accent badge */}
- Premium Venue Booking + Venue Booking
- -

- Find the Perfect Venue for Every Occasion -

-

- Discover verified spaces, compare transparent pricing, and secure your booking in minutes. -

- {/* UNIFIED SEARCH BAR */} -
{/* Location Input with suggestions */} -
+
Location @@ -132,7 +124,7 @@ export default function Home() { onFocus={() => setShowCities(true)} className="w-full bg-transparent border-0 outline-none text-sm font-semibold pt-1 focus:ring-0 placeholder:text-neutral-muted/70 placeholder:font-normal" /> - + {/* Auto-suggest dropdown */} {showCities && (
@@ -199,8 +191,8 @@ export default function Home() { {/* Search CTA */}
-
+

+ Find the Perfect Venue for Every Occasion +

+

+ Discover verified spaces, compare transparent pricing, and secure your booking in minutes. +

+ +
@@ -224,7 +224,7 @@ export default function Home() {

Verified & curated spaces

- +
@@ -256,13 +256,13 @@ export default function Home() {

Explore by Space Type

- + {/* Scrollable container */}
{categories.map((cat, idx) => ( -
@@ -300,8 +300,8 @@ export default function Home() { {featuredVenues.map((venue) => { const isStarred = wishlist.includes(venue.id); return ( -
{/* Venue Image */} @@ -312,14 +312,14 @@ export default function Home() { fill className="object-cover group-hover:scale-105 transition-transform duration-500" /> - + {/* Badge Overlays */} {venue.verified && ( Verified )} - + {/* Wishlist Heart */}
- +
View All Venues @@ -385,7 +385,7 @@ export default function Home() {

Simplicity First

How It Works

- +
diff --git a/bmvfrontend/src/customer/pages/Profile.tsx b/bmvfrontend/src/customer/pages/Profile.tsx new file mode 100644 index 000000000..456238944 --- /dev/null +++ b/bmvfrontend/src/customer/pages/Profile.tsx @@ -0,0 +1,5 @@ +export default function Profile() { + return ( +
Hello Profile
+ ) +} \ No newline at end of file diff --git a/bmvfrontend/src/hooks/auth.ts b/bmvfrontend/src/hooks/auth.ts new file mode 100644 index 000000000..aa417be27 --- /dev/null +++ b/bmvfrontend/src/hooks/auth.ts @@ -0,0 +1,25 @@ +// import { register } from "module"; +import { useState } from "react"; +import { sendOtp } from "../auth/route"; +import { verifyOtp } from "../auth/route"; +import { register } from "../auth/route"; +// export const useRegister = () => { + +// const sendOtpHandler = async (phone: string) => { +// return sendOtp(phone); +// } + +// const verifyOtphandler = async (phone: string, otp: string) => { +// return verifyOtp(phone, otp); +// } + +// const registerHandler = async (data: registerDto) => { +// return register() +// } + +// return { +// sendOtp: sendOtpHandler, +// verifyOtp: verifyOtphandler, +// register: registerHandler +// } +// } \ No newline at end of file diff --git a/bmvfrontend/src/lib/api.ts b/bmvfrontend/src/lib/api.ts new file mode 100644 index 000000000..ab4349445 --- /dev/null +++ b/bmvfrontend/src/lib/api.ts @@ -0,0 +1,71 @@ +/** + * API client utility for communicating with the NestJS backend via the Next.js BFF secure proxy. + */ +import { clearSession } from "./authStore"; + +const getBaseUrl = () => { + if (typeof window !== "undefined") { + // Client-side: use relative URL to trigger local proxy + return "/api"; + } + // Server-side: call localhost + return "http://localhost:3000/api"; +}; + +export interface RequestOptions extends RequestInit { + body?: any; +} + +/** + * Custom fetch wrapper that handles proxy routing, content types, and credentials. + */ +export async function apiFetch(endpoint: string, options: RequestOptions = {}): Promise { + const baseUrl = getBaseUrl(); + const url = `${baseUrl}${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`; + + const headers = new Headers(options.headers); + + // Set content type to JSON by default unless payload is FormData (e.g. file uploads) + if (!headers.has("Content-Type") && !(options.body instanceof FormData)) { + headers.set("Content-Type", "application/json"); + } + + const fetchOptions: RequestInit = { + ...options, + headers, + credentials: options.credentials || "same-origin", // Automatically include same-origin HttpOnly cookies + }; + + if (options.body) { + fetchOptions.body = options.body instanceof FormData ? options.body : JSON.stringify(options.body); + } + + const response = await fetch(url, fetchOptions); + + if (!response.ok) { + // Intercept unrecoverable 401 Unauthorized (invalid/expired refresh tokens) + if (response.status === 401 && typeof window !== "undefined") { + clearSession(); + const isPartner = window.location.pathname.startsWith("/partner"); + const redirectUrl = isPartner ? "/partner/login" : "/login"; + const returnUrl = encodeURIComponent(window.location.pathname + window.location.search); + window.location.href = `${redirectUrl}?returnUrl=${returnUrl}`; + } + + let errorMessage = `API request failed with status ${response.status}`; + try { + const errorData = await response.json(); + errorMessage = errorData.message || errorMessage; + } catch { + // Fallback if response is not JSON + } + throw new Error(errorMessage); + } + + const contentType = response.headers.get("content-type"); + if (contentType && contentType.includes("application/json")) { + return response.json() as Promise; + } + + return response.text() as unknown as Promise; +} From 52b8be591c1d9126af3950bff9523c88b0adaea2 Mon Sep 17 00:00:00 2001 From: Gokul Shaji Date: Fri, 12 Jun 2026 13:27:14 +0530 Subject: [PATCH 04/16] feat: completed customer auth --- bmvBackend/.env.example | 3 ++- bmvBackend/package.json | 3 +++ bmvBackend/src/main.ts | 12 ++++++++++++ bmvBackend/src/users/dto/onboard-customer.dto.ts | 1 + bmvBackend/test/jest-e2e.json | 3 +++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/bmvBackend/.env.example b/bmvBackend/.env.example index 15ba67df6..bc69be11b 100644 --- a/bmvBackend/.env.example +++ b/bmvBackend/.env.example @@ -6,5 +6,6 @@ DB_PASS= DB_NAME=bookmyvenue # ─── App ──────────────────────────────────────────────────── -PORT=3000 +PORT=5000 NODE_ENV=development +CORS_ORIGIN=http://localhost:3000 diff --git a/bmvBackend/package.json b/bmvBackend/package.json index 9159733dc..a37521915 100644 --- a/bmvBackend/package.json +++ b/bmvBackend/package.json @@ -85,6 +85,9 @@ "collectCoverageFrom": [ "**/*.(t|j)s" ], + "moduleNameMapper": { + "^src/(.*)$": "/$1" + }, "coverageDirectory": "../coverage", "testEnvironment": "node" } diff --git a/bmvBackend/src/main.ts b/bmvBackend/src/main.ts index fcf05d336..f35ee4546 100644 --- a/bmvBackend/src/main.ts +++ b/bmvBackend/src/main.ts @@ -5,6 +5,18 @@ import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + // Set global API prefix, excluding the root endpoint + app.setGlobalPrefix('api', { exclude: ['/'] }); + + // Enable CORS for frontend requests + const corsOrigin = process.env.CORS_ORIGIN; + app.enableCors({ + origin: corsOrigin ? corsOrigin.split(',') : 'http://localhost:3000', + credentials: true, + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', + allowedHeaders: 'Content-Type, Accept, Authorization', + }); + // Enforce DTO validation globally across all endpoints app.useGlobalPipes( new ValidationPipe({ diff --git a/bmvBackend/src/users/dto/onboard-customer.dto.ts b/bmvBackend/src/users/dto/onboard-customer.dto.ts index 074611b5e..c9e0413b3 100644 --- a/bmvBackend/src/users/dto/onboard-customer.dto.ts +++ b/bmvBackend/src/users/dto/onboard-customer.dto.ts @@ -26,6 +26,7 @@ export class OnboardCustomerDto { @Length(1, 20) pincode: string; + @IsOptional() @IsString() @IsUrl({}, { message: 'googleLocationUrl must be a valid URL' }) googleLocationUrl?: string; diff --git a/bmvBackend/test/jest-e2e.json b/bmvBackend/test/jest-e2e.json index e9d912f3e..54be8a3f7 100644 --- a/bmvBackend/test/jest-e2e.json +++ b/bmvBackend/test/jest-e2e.json @@ -5,5 +5,8 @@ "testRegex": ".e2e-spec.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" + }, + "moduleNameMapper": { + "^src/(.*)$": "/../src/$1" } } From 3b5385b644ee53da87306c9be157f619a288974f Mon Sep 17 00:00:00 2001 From: Gokul Shaji Date: Fri, 12 Jun 2026 13:27:54 +0530 Subject: [PATCH 05/16] feat: completed customer frontend auth --- bmvfrontend/app/customer/profile/page.tsx | 5 + bmvfrontend/app/customer/register/page.tsx | 2 +- bmvfrontend/app/login/page.tsx | 2 +- bmvfrontend/app/register/page.tsx | 2 +- .../app/venues/{%5Bid%5D => [id]}/page.tsx | 0 bmvfrontend/next.config.ts | 4 + bmvfrontend/package-lock.json | 39 +- bmvfrontend/package.json | 5 +- bmvfrontend/src/auth/AuthDto.ts | 0 .../auth/login/page.tsx} | 13 +- .../src/customer/auth/profile/Profile.tsx | 397 ++++++++++++++++++ .../auth/register/page.tsx} | 20 +- bmvfrontend/src/{ => customer}/auth/route.ts | 0 bmvfrontend/src/customer/pages/Home.tsx | 33 +- bmvfrontend/src/customer/pages/Profile.tsx | 5 - .../src/customer/pages/VenueDetail.tsx | 374 ++++++++++------- .../src/customer/pages/VenueListing.tsx | 364 +++++++++------- bmvfrontend/src/hooks/auth.ts | 6 +- bmvfrontend/src/lib/authStore.ts | 1 + bmvfrontend/src/venues/route.ts | 40 ++ bmvfrontend/src/venues/types.ts | 89 ++++ 21 files changed, 1056 insertions(+), 345 deletions(-) create mode 100644 bmvfrontend/app/customer/profile/page.tsx rename bmvfrontend/app/venues/{%5Bid%5D => [id]}/page.tsx (100%) delete mode 100644 bmvfrontend/src/auth/AuthDto.ts rename bmvfrontend/src/{auth/Login.tsx => customer/auth/login/page.tsx} (97%) create mode 100644 bmvfrontend/src/customer/auth/profile/Profile.tsx rename bmvfrontend/src/{auth/Register.tsx => customer/auth/register/page.tsx} (98%) rename bmvfrontend/src/{ => customer}/auth/route.ts (100%) delete mode 100644 bmvfrontend/src/customer/pages/Profile.tsx create mode 100644 bmvfrontend/src/venues/route.ts create mode 100644 bmvfrontend/src/venues/types.ts diff --git a/bmvfrontend/app/customer/profile/page.tsx b/bmvfrontend/app/customer/profile/page.tsx new file mode 100644 index 000000000..e0056a727 --- /dev/null +++ b/bmvfrontend/app/customer/profile/page.tsx @@ -0,0 +1,5 @@ +import Profile from "@/src/customer/auth/profile/Profile"; + +export default function Page() { + return +} \ No newline at end of file diff --git a/bmvfrontend/app/customer/register/page.tsx b/bmvfrontend/app/customer/register/page.tsx index 820063f28..511a8940f 100644 --- a/bmvfrontend/app/customer/register/page.tsx +++ b/bmvfrontend/app/customer/register/page.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import Register from "@/src/auth/Register"; +import Register from "@/src/customer/auth/register/page"; export default function Page() { return ( diff --git a/bmvfrontend/app/login/page.tsx b/bmvfrontend/app/login/page.tsx index 60e9935a2..f76cbad47 100644 --- a/bmvfrontend/app/login/page.tsx +++ b/bmvfrontend/app/login/page.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import Login from "@/src/auth/Login"; +import Login from "@/src/customer/auth/login/page"; export default function Page() { return ( diff --git a/bmvfrontend/app/register/page.tsx b/bmvfrontend/app/register/page.tsx index 820063f28..511a8940f 100644 --- a/bmvfrontend/app/register/page.tsx +++ b/bmvfrontend/app/register/page.tsx @@ -1,5 +1,5 @@ import React, { Suspense } from "react"; -import Register from "@/src/auth/Register"; +import Register from "@/src/customer/auth/register/page"; export default function Page() { return ( diff --git a/bmvfrontend/app/venues/%5Bid%5D/page.tsx b/bmvfrontend/app/venues/[id]/page.tsx similarity index 100% rename from bmvfrontend/app/venues/%5Bid%5D/page.tsx rename to bmvfrontend/app/venues/[id]/page.tsx diff --git a/bmvfrontend/next.config.ts b/bmvfrontend/next.config.ts index 6be334a85..c5415df9f 100644 --- a/bmvfrontend/next.config.ts +++ b/bmvfrontend/next.config.ts @@ -8,6 +8,10 @@ const nextConfig: NextConfig = { protocol: "https", hostname: "images.unsplash.com", }, + { + protocol: "https", + hostname: "res.cloudinary.com", + }, ], }, }; diff --git a/bmvfrontend/package-lock.json b/bmvfrontend/package-lock.json index 2f7dcbc53..27f75e69a 100644 --- a/bmvfrontend/package-lock.json +++ b/bmvfrontend/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@base-ui/react": "^1.5.0", + "@hookform/resolvers": "^5.4.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.4.0", @@ -18,9 +19,11 @@ "react": "19.2.4", "react-day-picker": "^10.0.1", "react-dom": "19.2.4", + "react-hook-form": "^7.78.0", "shadcn": "^4.11.0", "tailwind-merge": "^3.6.0", - "tw-animate-css": "^1.4.0" + "tw-animate-css": "^1.4.0", + "zod": "^4.4.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -952,6 +955,18 @@ "hono": "^4" } }, + "node_modules/@hookform/resolvers": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.4.0.tgz", + "integrity": "sha512-EIsqr/t/qbinPIhGjMdtvutIN1Kk4uwbROE9/UQ93CAVGR7GkA7Y92+fX80OzXi/OB67jVFYwKGO1WzkxmkFZw==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1863,6 +1878,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -7752,6 +7773,22 @@ "react": "^19.2.4" } }, + "node_modules/react-hook-form": { + "version": "7.78.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.78.0.tgz", + "integrity": "sha512-EEZqc+N23moyzTlz61Pj+JvcXo76ICkpfOZo8JZw+sM4+wLQGh6nI2Ms+PdMOYNluFu0ghlM7B8mCzhRYtJCnA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/bmvfrontend/package.json b/bmvfrontend/package.json index 481ab0bb4..e8e8547f5 100644 --- a/bmvfrontend/package.json +++ b/bmvfrontend/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@base-ui/react": "^1.5.0", + "@hookform/resolvers": "^5.4.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.4.0", @@ -19,9 +20,11 @@ "react": "19.2.4", "react-day-picker": "^10.0.1", "react-dom": "19.2.4", + "react-hook-form": "^7.78.0", "shadcn": "^4.11.0", "tailwind-merge": "^3.6.0", - "tw-animate-css": "^1.4.0" + "tw-animate-css": "^1.4.0", + "zod": "^4.4.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/bmvfrontend/src/auth/AuthDto.ts b/bmvfrontend/src/auth/AuthDto.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/bmvfrontend/src/auth/Login.tsx b/bmvfrontend/src/customer/auth/login/page.tsx similarity index 97% rename from bmvfrontend/src/auth/Login.tsx rename to bmvfrontend/src/customer/auth/login/page.tsx index d2afc1cf0..469bff01b 100644 --- a/bmvfrontend/src/auth/Login.tsx +++ b/bmvfrontend/src/customer/auth/login/page.tsx @@ -56,10 +56,15 @@ export default function Login() { email: data.user.email, phone: data.user.phone, role: (data.user.role === "venue_owner" ? "partner" : "customer") as "customer" | "partner", + isProfileCompleted: data.user.isProfileCompleted, }); // Navigate to return URL or home - router.push(returnUrl); + if (data.user.role === "customer" && !data.user.isProfileCompleted) { + router.push("/customer/profile"); + } else { + router.push(returnUrl); + } } catch (err: any) { setError(err.message || "Failed to log in. Please try again."); } @@ -78,7 +83,7 @@ export default function Login() { return (
- + {/* Top right escape link */}
@@ -101,7 +106,7 @@ export default function Login() {
- + {error && (
@@ -110,7 +115,7 @@ export default function Login() { )}
- + {/* Email */}
diff --git a/bmvfrontend/src/customer/auth/profile/Profile.tsx b/bmvfrontend/src/customer/auth/profile/Profile.tsx new file mode 100644 index 000000000..a22096ae7 --- /dev/null +++ b/bmvfrontend/src/customer/auth/profile/Profile.tsx @@ -0,0 +1,397 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + MapPin, + Loader2, + Building, + Home, + Globe, + ShieldAlert, + CheckCircle, + ArrowRight, + Info +} from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Card, CardContent } from "@/components/ui/card"; +import { getSession, setSession, UserSession } from "@/src/lib/authStore"; +import { apiFetch } from "@/src/lib/api"; + +// Define validation schema using Zod +const onboardingSchema = z.object({ + addressLine1: z + .string() + .min(1, "Address Line 1 is required") + .max(255, "Address must be less than 255 characters"), + addressLine2: z + .string() + .max(255, "Address must be less than 255 characters") + .optional() + .or(z.literal("")), + city: z + .string() + .min(1, "City is required") + .max(100, "City name must be less than 100 characters"), + state: z + .string() + .min(1, "State is required") + .max(100, "State name must be less than 100 characters"), + pincode: z + .string() + .min(1, "Pincode is required") + .regex(/^\d{6}$/, "Pincode must be exactly 6 digits"), + googleLocationUrl: z + .string() + .optional() + .or(z.literal("")) + .refine( + (val) => { + if (!val) return true; + try { + const url = new URL(val); + return url.protocol === "http:" || url.protocol === "https:"; + } catch { + return false; + } + }, + { + message: "Please enter a valid URL starting with http:// or https:// (e.g. https://maps.app.goo.gl/...)" + } + ) +}); + +type OnboardingFormValues = z.infer; + +export default function Profile() { + const router = useRouter(); + const [session, setSessionState] = useState(null); + const [loadingSession, setLoadingSession] = useState(true); + const [isSubmitting, setIsSubmitting] = useState(false); + const [apiError, setApiError] = useState(""); + const [success, setSuccess] = useState(false); + + // Load session on mount + useEffect(() => { + const currentSession = getSession(); + setSessionState(currentSession); + setLoadingSession(false); + + // If user is logged in and profile is already completed, redirect to customer dashboard + if (currentSession && currentSession.isProfileCompleted) { + router.push("/customer"); + } + }, [router]); + + // Set up React Hook Form + const { + register: registerField, + handleSubmit, + formState: { errors } + } = useForm({ + resolver: zodResolver(onboardingSchema), + defaultValues: { + addressLine1: "", + addressLine2: "", + city: "", + state: "", + pincode: "", + googleLocationUrl: "" + } + }); + + const onSubmit = async (values: OnboardingFormValues) => { + setIsSubmitting(true); + setApiError(""); + + // Omit optional fields if they are empty + const payload = { + addressLine1: values.addressLine1, + addressLine2: values.addressLine2 || undefined, + city: values.city, + state: values.state, + pincode: values.pincode, + googleLocationUrl: values.googleLocationUrl || undefined + }; + + try { + await apiFetch<{ + message: string; + isProfileCompleted: boolean; + profile: any; + }>("/users/customer/onboard", { + method: "POST", + body: payload + }); + + // Update session in local storage + const currentSession = getSession(); + if (currentSession) { + setSession({ + ...currentSession, + isProfileCompleted: true + }); + } + + setSuccess(true); + setTimeout(() => { + router.push("/customer"); + }, 1500); + } catch (err: any) { + setApiError(err.message || "Failed to complete onboarding. Please check your inputs."); + } finally { + setIsSubmitting(false); + } + }; + + // Show loading spinner while determining session state + if (loadingSession) { + return ( +
+ + Loading session... +
+ ); + } + + // Redirect to login if user session is not found + if (!session) { + return ( +
+
+ + +
+ +
+
+

Authentication Required

+

+ Please log in to complete your profile onboarding and access the dashboard. +

+
+ + + +
+
+
+
+ ); + } + + return ( +
+ + {/* Decorative background shapes */} +
+
+ +
+
+ + BookMyVenue + +
+
+ +
+

Complete Your Profile

+
+

+ Please provide your location details to continue to the dashboard. +

+
+
+ +
+ + + + {/* Show success message */} + {success ? ( +
+
+ +
+
+

Setup Complete!

+

+ Your location details have been saved. Redirecting to the dashboard... +

+
+
+ +
+
+ ) : ( + <> + {/* Show validation or API error */} + {apiError && ( +
+ + {apiError} +
+ )} + + + + {/* Address Line 1 */} +
+ + + {errors.addressLine1 && ( +

{errors.addressLine1.message}

+ )} +
+ + {/* Address Line 2 */} +
+ + + {errors.addressLine2 && ( +

{errors.addressLine2.message}

+ )} +
+ + {/* City + State Layout */} +
+ {/* City */} +
+ + + {errors.city && ( +

{errors.city.message}

+ )} +
+ + {/* State */} +
+ + + {errors.state && ( +

{errors.state.message}

+ )} +
+
+ + {/* Pincode + Google Location URL Layout */} +
+ {/* Pincode */} +
+ + + {errors.pincode && ( +

{errors.pincode.message}

+ )} +
+ + {/* Google Location URL */} +
+ + + {errors.googleLocationUrl && ( +

{errors.googleLocationUrl.message}

+ )} +
+
+ + {/* Google Maps Link Helper Instructions */} +
+ +
+ How to copy Google Maps Link: +

+ Open Google Maps → Search for your address → Click Share → Copy Link → Paste in the field above. +

+
+
+ + {/* CTA Submit Button */} + + + + )} + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/bmvfrontend/src/auth/Register.tsx b/bmvfrontend/src/customer/auth/register/page.tsx similarity index 98% rename from bmvfrontend/src/auth/Register.tsx rename to bmvfrontend/src/customer/auth/register/page.tsx index 3a57ab998..0cec3b7be 100644 --- a/bmvfrontend/src/auth/Register.tsx +++ b/bmvfrontend/src/customer/auth/register/page.tsx @@ -22,9 +22,7 @@ import { Sparkles, PartyPopper } from "lucide-react"; -import { sendOtp, verifyOtp, register } from "./route"; -import Home from "../customer/pages/Home"; - +import { sendOtp, verifyOtp, register } from "../route" export interface VerifyOtpResponse { verified: boolean; phone: string; @@ -133,16 +131,18 @@ export default function Register() { return; } - console.log(name); - console.log(phone); - console.log(email); - console.log(password); - console.log(phoneToken); - console.log(role); try { await register(name, phone, email, password, phoneToken, role); - router.push('/Home'); + setSession({ + name, + email, + phone, + role: "customer", + isProfileCompleted: false, + }); + + router.push('/customer/profile'); } catch (error) { setError("Failed to register"); diff --git a/bmvfrontend/src/auth/route.ts b/bmvfrontend/src/customer/auth/route.ts similarity index 100% rename from bmvfrontend/src/auth/route.ts rename to bmvfrontend/src/customer/auth/route.ts diff --git a/bmvfrontend/src/customer/pages/Home.tsx b/bmvfrontend/src/customer/pages/Home.tsx index 6c4d58a1c..594581e6a 100644 --- a/bmvfrontend/src/customer/pages/Home.tsx +++ b/bmvfrontend/src/customer/pages/Home.tsx @@ -7,7 +7,7 @@ import Image from "next/image"; import Header from "../components/Header"; import Footer from "../components/Footer"; import { MOCK_VENUES, MOCK_CITIES, Venue } from "@/src/lib/mockData"; -import { getWishlist, toggleWishlist } from "@/src/lib/authStore"; +import { getWishlist, toggleWishlist, getSession } from "@/src/lib/authStore"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { @@ -45,7 +45,11 @@ export default function Home() { useEffect(() => { setWishlistState(getWishlist()); - }, []); + const session = getSession(); + if (session && session.role === "customer" && session.isProfileCompleted === false) { + router.push("/customer/profile"); + } + }, [router]); const handleCityClick = (city: string) => { setLocation(city); @@ -103,12 +107,12 @@ export default function Home() {
Venue Booking
- {/* UNIFIED SEARCH BAR */} -
- {/* Location Input with suggestions */} +
Location @@ -125,7 +129,6 @@ export default function Home() { className="w-full bg-transparent border-0 outline-none text-sm font-semibold pt-1 focus:ring-0 placeholder:text-neutral-muted/70 placeholder:font-normal" /> - {/* Auto-suggest dropdown */} {showCities && (
Popular Cities
@@ -147,7 +150,7 @@ export default function Home() { )}
- {/* Occasion Selection */} +
Occasion @@ -166,7 +169,7 @@ export default function Home() {
- {/* Date Popover Picker */} +
Date @@ -189,7 +192,6 @@ export default function Home() {
- {/* Search CTA */}
- + */}

Find the Perfect Venue for Every Occasion

@@ -207,7 +209,16 @@ export default function Home() { Discover verified spaces, compare transparent pricing, and secure your booking in minutes.

- +
+ +
diff --git a/bmvfrontend/src/customer/pages/Profile.tsx b/bmvfrontend/src/customer/pages/Profile.tsx deleted file mode 100644 index 456238944..000000000 --- a/bmvfrontend/src/customer/pages/Profile.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export default function Profile() { - return ( -
Hello Profile
- ) -} \ No newline at end of file diff --git a/bmvfrontend/src/customer/pages/VenueDetail.tsx b/bmvfrontend/src/customer/pages/VenueDetail.tsx index dcfc28260..e656289ac 100644 --- a/bmvfrontend/src/customer/pages/VenueDetail.tsx +++ b/bmvfrontend/src/customer/pages/VenueDetail.tsx @@ -6,7 +6,6 @@ import Link from "next/link"; import Image from "next/image"; import Header from "../components/Header"; import Footer from "../components/Footer"; -import { MOCK_VENUES, Venue } from "@/src/lib/mockData"; import { getSession, toggleWishlist, getWishlist } from "@/src/lib/authStore"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; @@ -15,6 +14,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover import { Calendar } from "@/components/ui/calendar"; import { Badge } from "@/components/ui/badge"; import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet"; +import { Skeleton } from "@/components/ui/skeleton"; import { MapPin, Users, @@ -31,8 +31,12 @@ import { Clock, ShieldCheck, PartyPopper, - X + X, + ShieldAlert, + Loader2 } from "lucide-react"; +import { fetchPublicVenueDetail } from "@/src/venues/route"; +import { PublicVenueDetailResponseDto, VenueType, BookingType } from "@/src/venues/types"; interface VenueDetailProps { id: string; @@ -40,29 +44,75 @@ interface VenueDetailProps { export default function VenueDetail({ id }: VenueDetailProps) { const router = useRouter(); - const venue = MOCK_VENUES.find((v) => v.id === id); - // States + // API State + const [venue, setVenue] = useState(null); + const [loading, setLoading] = useState(true); + const [apiError, setApiError] = useState(""); + + // UI States const [selectedDate, setSelectedDate] = useState(new Date()); - const [selectedSlot, setSelectedSlot] = useState<"Morning" | "Evening" | "Full Day">("Morning"); const [guestsCount, setGuestsCount] = useState(50); const [wishlist, setWishlistState] = useState([]); const [lightboxOpen, setLightboxOpen] = useState(false); const [activePhotoIdx, setActivePhotoIdx] = useState(0); + // Load wishlist & fetch venue details useEffect(() => { setWishlistState(getWishlist()); - }, []); - if (!venue) { + const loadVenueDetail = async () => { + setLoading(true); + setApiError(""); + try { + const data = await fetchPublicVenueDetail(id); + setVenue(data); + // Default guest count to a reasonable value within max capacity + setGuestsCount(Math.min(50, data.maxCapacity)); + } catch (err: any) { + setApiError(err.message || "Failed to load venue details. Please try again later."); + } finally { + setLoading(false); + } + }; + + loadVenueDetail(); + }, [id]); + + if (loading) { return ( -
+
-
-

Venue Not Found

-

The venue you are looking for does not exist or has been removed.

+
+ + +
+
+ + +
+ +
+
+
+
+ ); + } + + if (apiError || !venue) { + return ( +
+
+
+
+ +
+

Venue Load Failed

+

{apiError || "The venue details could not be loaded."}

- +
@@ -77,55 +127,88 @@ export default function VenueDetail({ id }: VenueDetailProps) { setWishlistState(updated); }; - // Pricing calculation - const getBasePrice = () => { - if (selectedSlot === "Full Day") return venue.pricePerDay; - return venue.pricePerSlot; - }; - - const basePrice = getBasePrice(); + // Pricing calculation: strictly date-based (full-day only) + const basePrice = venue.startingPrice; const gstTax = Math.round(basePrice * 0.18); // 18% GST const serviceFee = Math.round(basePrice * 0.025); // 2.5% Service Fee const totalPrice = basePrice + gstTax + serviceFee; const handleBookNow = () => { const session = getSession(); - + // Save selection details to localStorage const bookingParams = { venueId: venue.id, - venueName: venue.name, - venueImage: venue.image, + venueName: venue.venueName, + venueImage: venueImages[0], date: selectedDate ? selectedDate.toLocaleDateString("en-IN") : new Date().toLocaleDateString("en-IN"), - slot: selectedSlot, + slot: "Full Day", guests: guestsCount, totalPrice: totalPrice, }; - + localStorage.setItem("bmv_pending_booking", JSON.stringify(bookingParams)); if (!session) { - // Redirect to login with return path router.push(`/login?returnUrl=${encodeURIComponent(`/booking/confirm`)}`); } else { router.push("/booking/confirm"); } }; - const handleOpenLightbox = (index: number) => { - setActivePhotoIdx(index); - setLightboxOpen(true); + // Helper mappings + const formatVenueType = (type: VenueType): string => { + switch (type) { + case VenueType.WEDDING_HALL: return "Wedding Hall"; + case VenueType.AUDITORIUM: return "Auditorium"; + case VenueType.RESORT: return "Resort"; + case VenueType.CONVENTION_CENTER: return "Convention Center"; + case VenueType.CAFE: return "Cafe"; + case VenueType.PARTY_HALL: return "Party Hall"; + case VenueType.MEETUP_SPACE: return "Meetup Space"; + case VenueType.MALL: return "Mall Space"; + case VenueType.HOTEL: return "Hotel Hall"; + default: return "Venue"; + } }; - const handleNextPhoto = () => { - setActivePhotoIdx((prev) => (prev + 1) % venue.images.length); + const getOccasionsForType = (type: VenueType): string[] => { + switch (type) { + case VenueType.WEDDING_HALL: return ["Wedding", "Social"]; + case VenueType.AUDITORIUM: return ["Corporate", "Academic"]; + case VenueType.RESORT: return ["Wedding", "Social"]; + case VenueType.CONVENTION_CENTER: return ["Corporate", "Exhibition"]; + case VenueType.CAFE: return ["Birthday", "Social"]; + case VenueType.PARTY_HALL: return ["Birthday", "Social"]; + case VenueType.MEETUP_SPACE: return ["Corporate", "Workshop"]; + default: return ["Event"]; + } }; - const handlePrevPhoto = () => { - setActivePhotoIdx((prev) => (prev - 1 + venue.images.length) % venue.images.length); + const getAmenities = () => { + const list = []; + if (venue.hasParking) { + list.push(`Parking${venue.parkingCapacity ? ` (Capacity: ${venue.parkingCapacity})` : ""}`); + } + + // Type-specific defaults to keep list rich + switch (venue.venueType) { + case VenueType.WEDDING_HALL: + list.push("AC", "Decoration", "Catering", "Valet"); + break; + case VenueType.AUDITORIUM: + list.push("AC", "AV Equipment", "WiFi"); + break; + case VenueType.RESORT: + list.push("Catering", "AC", "WiFi", "Valet"); + break; + default: + list.push("AC", "WiFi"); + break; + } + return list; }; - // Emoji dictionary for amenities const amenityEmojis: Record = { Parking: "🅿", AC: "❄", @@ -136,27 +219,48 @@ export default function VenueDetail({ id }: VenueDetailProps) { Decoration: "✨" }; + // Process Images from Backend + const images = venue.images?.map((img) => img.imageUrl) || []; + const venueImages = images.length > 0 ? images : [ + "https://images.unsplash.com/photo-1519167758481-83f550bb49b3?auto=format&fit=crop&q=80&w=800" + ]; + + const fullAddress = `${venue.address}, ${venue.city}, ${venue.district}, ${venue.state} - ${venue.pincode}`; + + const handleOpenLightbox = (index: number) => { + setActivePhotoIdx(index); + setLightboxOpen(true); + }; + + const handleNextPhoto = () => { + setActivePhotoIdx((prev) => (prev + 1) % venueImages.length); + }; + + const handlePrevPhoto = () => { + setActivePhotoIdx((prev) => (prev - 1 + venueImages.length) % venueImages.length); + }; + return ( -
+
-
- +
+ {/* Back Link */}
- + Back to Venues
{/* IMAGE GALLERY GRID */}
- + {/* Main Hero Photo */}
handleOpenLightbox(0)}> {`${venue.name} @@ -166,9 +270,9 @@ export default function VenueDetail({ id }: VenueDetailProps) {
- {/* 2x2 Thumbnail Grid (hidden on mobile, shown as a column of 2 or grid on tablet/desktop) */} + {/* Thumbnail Grid */}
- {venue.images.slice(1, 5).map((img, idx) => ( + {venueImages.slice(1, 5).map((img, idx) => (
{`${venue.name}
- - {/* Overlay on last item to show remaining counts if there are more */} - {idx === 3 && venue.images.length > 5 && ( + + {/* Overlay on last item */} + {idx === 3 && venueImages.length > 5 && (
- +{venue.images.length - 5} Photos + +{venueImages.length - 5} Photos
)}
))} - {/* Fallback if less than 5 images */} - {Array.from({ length: Math.max(0, 4 - venue.images.slice(1, 5).length) }).map((_, i) => ( + {/* Fallback empty placeholders */} + {Array.from({ length: Math.max(0, 4 - venueImages.slice(1, 5).length) }).map((_, i) => (
@@ -199,23 +303,21 @@ export default function VenueDetail({ id }: VenueDetailProps) {
- {/* DETAILS & STICKY BOOKING PANEL CONTAINER */} + {/* DETAILS & STICKY BOOKING PANEL */}
- + {/* Left Side: Venue Details */}
- - {/* Header info */} + + {/* Header Info Card */}
- {venue.type} + {formatVenueType(venue.venueType)} + + + Verified Venue - {venue.verified && ( - - Verified Venue - - )}
-

Cancellation Policy

-

Free cancellation up to 15 days before the event. 50% cancellation fee between 7-14 days. Non-refundable within 7 days of the booking.

+

Cancellation Terms

+

Free cancellation up to 15 days before the event date. 50% refund between 7-14 days. Bookings canceled within 7 days are non-refundable.

@@ -331,8 +436,8 @@ export default function VenueDetail({ id }: VenueDetailProps) {
-

Catering & External Vendors

-

In-house catering and decoration services are optional. External caterers and decorators are allowed with a minor vendor surcharge fee.

+

Catering and External Sur-charges

+

Catering services are optional. External vendors (Catering / Decoration) are permitted and require a basic coordination fee.

@@ -340,7 +445,7 @@ export default function VenueDetail({ id }: VenueDetailProps) {
- {/* Right Side: Desktop Sticky Booking Panel */} + {/* Right Side: Desktop Booking Card (Slots Selector Removed) */}
@@ -348,7 +453,7 @@ export default function VenueDetail({ id }: VenueDetailProps) { Booking Rate
₹{basePrice.toLocaleString("en-IN")} - /{selectedSlot === "Full Day" ? "day" : "slot"} + / day
@@ -375,27 +480,7 @@ export default function VenueDetail({ id }: VenueDetailProps) {
- {/* Slot Selector */} -
- -
- {["Morning", "Evening", "Full Day"].map((slotOption) => ( - - ))} -
-
- - {/* Guest Count Stepper */} + {/* Expected Guests Stepper */}
@@ -407,7 +492,7 @@ export default function VenueDetail({ id }: VenueDetailProps) { {guestsCount} guests @@ -447,7 +532,7 @@ export default function VenueDetail({ id }: VenueDetailProps) {
- {/* MOBILE STICKY BOTTOM PANEL (Fixed at bottom on mobile) */} + {/* MOBILE STICKY BOTTOM PANEL (Slots Selector Removed) */}
Total Price @@ -464,7 +549,7 @@ export default function VenueDetail({ id }: VenueDetailProps) { Reserve Venue - +
{/* Date Picker */}
@@ -487,26 +572,6 @@ export default function VenueDetail({ id }: VenueDetailProps) {
- {/* Slot Selector */} -
- -
- {["Morning", "Evening", "Full Day"].map((slotOption) => ( - - ))} -
-
- {/* Guest Count Stepper */}
@@ -519,7 +584,7 @@ export default function VenueDetail({ id }: VenueDetailProps) { {guestsCount} guests
- {/* Photo Slide Frame */}
- {/* Prev Button */} - {/* Slide image */}
{`${venue.name}
- {/* Next Button */}
- {/* Thumbnail Bottom Bar */}
- {venue.images.map((img, idx) => ( + {venueImages.map((img, idx) => (
setActivePhotoIdx(idx)} - className={`relative h-12 w-16 shrink-0 rounded-md overflow-hidden cursor-pointer border-2 transition-all ${ - activePhotoIdx === idx ? "border-amber-cta opacity-100 scale-105" : "border-transparent opacity-50 hover:opacity-85" - }`} + className={`relative h-12 w-16 shrink-0 rounded-md overflow-hidden cursor-pointer border-2 transition-all ${activePhotoIdx === idx ? "border-amber-cta opacity-100 scale-105" : "border-transparent opacity-50 hover:opacity-85" + }`} > {`Gallery
@@ -620,9 +678,7 @@ export default function VenueDetail({ id }: VenueDetailProps) { )} - {/* Spacing for mobile fixed bottom bar */}
-
); diff --git a/bmvfrontend/src/customer/pages/VenueListing.tsx b/bmvfrontend/src/customer/pages/VenueListing.tsx index 8fb707830..784f84896 100644 --- a/bmvfrontend/src/customer/pages/VenueListing.tsx +++ b/bmvfrontend/src/customer/pages/VenueListing.tsx @@ -6,8 +6,10 @@ import Link from "next/link"; import Image from "next/image"; import Header from "../components/Header"; import Footer from "../components/Footer"; -import { MOCK_VENUES, MOCK_CITIES, Venue } from "@/src/lib/mockData"; +import { MOCK_CITIES } from "@/src/lib/mockData"; import { getWishlist, toggleWishlist } from "@/src/lib/authStore"; +import { PublicVenueResponseDto, VenueType } from "@/src/venues/types"; +import { fetchPublicVenues } from "@/src/venues/route"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; @@ -33,13 +35,51 @@ import { Calendar as CalendarIcon, Search, Map, - X + X, + ShieldAlert } from "lucide-react"; export default function VenueListing() { const searchParams = useSearchParams(); const router = useRouter(); + // Backend API states + const [venues, setVenues] = useState([]); + const [totalCount, setTotalCount] = useState(0); + const [page, setPage] = useState(1); + const [apiError, setApiError] = useState(""); + const limit = 10; + + + + const formatVenueType = (type: VenueType): string => { + switch (type) { + case VenueType.WEDDING_HALL: return "Wedding Hall"; + case VenueType.AUDITORIUM: return "Auditorium"; + case VenueType.RESORT: return "Resort"; + case VenueType.CONVENTION_CENTER: return "Convention Center"; + case VenueType.CAFE: return "Cafe"; + case VenueType.PARTY_HALL: return "Party Hall"; + case VenueType.MEETUP_SPACE: return "Meetup Space"; + case VenueType.MALL: return "Mall Space"; + case VenueType.HOTEL: return "Hotel Hall"; + default: return "Venue"; + } + }; + + const getOccasionsForType = (type: VenueType): string[] => { + switch (type) { + case VenueType.WEDDING_HALL: return ["Wedding", "Social"]; + case VenueType.AUDITORIUM: return ["Corporate", "Academic"]; + case VenueType.RESORT: return ["Wedding", "Social"]; + case VenueType.CONVENTION_CENTER: return ["Corporate", "Exhibition"]; + case VenueType.CAFE: return ["Birthday", "Social"]; + case VenueType.PARTY_HALL: return ["Birthday", "Social"]; + case VenueType.MEETUP_SPACE: return ["Corporate", "Workshop"]; + default: return ["Event"]; + } + }; + // URL search params const paramLocation = searchParams.get("location") || ""; const paramOccasion = searchParams.get("occasion") || ""; @@ -66,15 +106,13 @@ export default function VenueListing() { const [availableDate, setAvailableDate] = useState( paramDate ? new Date(paramDate) : undefined ); - + // Sort State const [sortBy, setSortBy] = useState("relevance"); - // Load wishlist & simulate initial page load + // Load wishlist on mount useEffect(() => { setWishlistState(getWishlist()); - const timer = setTimeout(() => setLoading(false), 500); - return () => clearTimeout(timer); }, []); // Update filters if search parameters change @@ -85,6 +123,42 @@ export default function VenueListing() { if (paramDate) setAvailableDate(new Date(paramDate)); }, [paramType, paramOccasion, paramLocation, paramDate]); + // Load venues from backend REST API + const loadVenues = async () => { + setLoading(true); + setApiError(""); + try { + const isPopularCity = MOCK_CITIES.some(c => c.toLowerCase() === searchLocation.toLowerCase()); + const typeParam = selectedTypes[0] as VenueType | undefined; + + const response = await fetchPublicVenues({ + city: isPopularCity ? searchLocation : undefined, + search: !isPopularCity && searchLocation ? searchLocation : undefined, + venueType: typeParam, + maxCapacity: minCapacity > 10 ? minCapacity : undefined, + page, + limit, + }); + + setVenues(response.data); + setTotalCount(response.total); + } catch (err: any) { + setApiError(err.message || "Failed to fetch venues from server."); + } finally { + setLoading(false); + } + }; + + // Re-load venues when parameters or page change + useEffect(() => { + loadVenues(); + }, [searchLocation, selectedTypes, minCapacity, page]); + + // Reset to first page when query filters are changed + useEffect(() => { + setPage(1); + }, [searchLocation, selectedTypes, minCapacity]); + // Compute active filter count const getActiveFilterCount = () => { let count = 0; @@ -109,13 +183,12 @@ export default function VenueListing() { setRatingFourPlus(false); setAvailableDate(undefined); setSearchLocation(""); + setPage(1); router.push("/venues"); - triggerSimulatedLoading(); }; const triggerSimulatedLoading = () => { - setLoading(true); - setTimeout(() => setLoading(false), 450); + loadVenues(); }; const handleTypeChange = (type: string, checked: boolean) => { @@ -139,62 +212,16 @@ export default function VenueListing() { setWishlistState(updated); }; - // Filter & Sort Logic - const filteredVenues = MOCK_VENUES.filter((venue) => { - // City / Location filter - if (searchLocation) { - const matchCity = venue.city.toLowerCase().includes(searchLocation.toLowerCase()); - const matchLoc = venue.location.toLowerCase().includes(searchLocation.toLowerCase()); - if (!matchCity && !matchLoc) return false; - } - - // Type filter - if (selectedTypes.length > 0 && !selectedTypes.includes(venue.type)) { - return false; - } - - // Occasion filter - if (selectedOccasion && !venue.occasions.includes(selectedOccasion as any)) { - return false; - } - - // Price filter (dual slider) - if (venue.pricePerDay < priceRange[0] || venue.pricePerDay > priceRange[1]) { - return false; - } - - // Capacity filter - if (venue.capacity < minCapacity) { - return false; - } - - // Amenities filter - if (selectedAmenities.length > 0) { - const hasAll = selectedAmenities.every((amenity) => venue.amenities.includes(amenity)); - if (!hasAll) return false; - } - - // Rating filter - if (ratingFourPlus && venue.rating < 4.0) { - return false; - } - - return true; - }); - - // Sort - const sortedVenues = [...filteredVenues].sort((a, b) => { + // Local sort on fetched page results + const sortedVenues = [...venues].sort((a, b) => { if (sortBy === "price-low") { - return a.pricePerDay - b.pricePerDay; + return a.startingPrice - b.startingPrice; } if (sortBy === "price-high") { - return b.pricePerDay - a.pricePerDay; - } - if (sortBy === "rating") { - return b.rating - a.rating; + return b.startingPrice - a.startingPrice; } - // "relevance" / default - return b.reviewsCount - a.reviewsCount; + // Default/Popularity/Relevance: keep backend order (newest first) + return 0; }); const renderFiltersContent = () => ( @@ -229,7 +256,7 @@ export default function VenueListing() { Venue Type - {["Hall", "Rooftop", "Lawn", "Auditorium", "Studio", "Resort"].map((type) => ( + {Object.values(VenueType).map((type) => ( ))} @@ -347,7 +374,7 @@ export default function VenueListing() {
- + {/* Search header / quick location bar */}
@@ -367,7 +394,7 @@ export default function VenueListing() { className="pl-9 h-11 bg-white border-input rounded-xl" />
- +
Active filters: {activeCount} @@ -387,7 +414,7 @@ export default function VenueListing() { {/* MAIN LAYOUT: Sticky sidebar + scrollable panel */}
- + {/* Desktop Left Sidebar Filters (hidden on mobile) */}