diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml new file mode 100644 index 0000000..d348129 --- /dev/null +++ b/.github/workflows/api.yml @@ -0,0 +1,44 @@ +name: API + +on: + push: + branches: [main] + paths: + - 'apps/api/**' + - 'packages/shared/**' + - 'package.json' + - 'package-lock.json' + - '.github/workflows/api.yml' + pull_request: + paths: + - 'apps/api/**' + - 'packages/shared/**' + - 'package.json' + - 'package-lock.json' + - '.github/workflows/api.yml' + +jobs: + api: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build shared package + run: npm run build -w @discoverly/shared + + - name: Lint + run: npm run lint -w @discoverly/api + + - name: Test + run: npm run test -w @discoverly/api + + - name: Build + run: npm run build -w @discoverly/api diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml deleted file mode 100644 index 2958646..0000000 --- a/.github/workflows/backend.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Backend CI - -on: - pull_request: - branches: ['main'] - paths: - - 'backend/**' - - '.github/workflows/backend.yml' - push: - branches: ['main'] - -jobs: - backend-checks: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '20' - - run: npm ci - - run: npm run format -w backend - - run: npm run lint -w backend - - run: npm run test -w backend - - run: npm run typecheck -w backend diff --git a/.github/workflows/mobile.yml b/.github/workflows/mobile.yml index 8439c46..68d569c 100644 --- a/.github/workflows/mobile.yml +++ b/.github/workflows/mobile.yml @@ -1,25 +1,36 @@ -name: Mobile CI +name: Mobile on: + push: + branches: [main] + paths: + - 'apps/mobile/**' + - 'package.json' + - 'package-lock.json' + - '.github/workflows/mobile.yml' pull_request: - branches: ['main'] paths: - - 'mobile/**' + - 'apps/mobile/**' + - 'package.json' + - 'package-lock.json' - '.github/workflows/mobile.yml' - push: - branches: ['main'] + jobs: - mobile-checks: + mobile: runs-on: ubuntu-latest - env: - EXPO_NO_TELEMETRY: 1 - EXPO_PUBLIC_API_BASE_URL: http://localhost:5000 steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: '20' - - run: npm ci - - run: npm run lint -w mobile - - run: npm run typecheck -w mobile - - run: npm run export:check -w mobile + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint -w @discoverly/mobile + + - name: Test + run: npm run test -w @discoverly/mobile diff --git a/.github/workflows/shared.yml b/.github/workflows/shared.yml new file mode 100644 index 0000000..c4968ae --- /dev/null +++ b/.github/workflows/shared.yml @@ -0,0 +1,44 @@ +name: Shared & Stellar Packages + +on: + push: + branches: [main] + paths: + - 'packages/shared/**' + - 'packages/stellar/**' + - 'package.json' + - 'package-lock.json' + - '.github/workflows/shared.yml' + pull_request: + paths: + - 'packages/shared/**' + - 'packages/stellar/**' + - 'package.json' + - 'package-lock.json' + - '.github/workflows/shared.yml' + +jobs: + shared: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Lint shared + run: npm run lint -w @discoverly/shared + + - name: Build shared + run: npm run build -w @discoverly/shared + + - name: Lint stellar + run: npm run lint -w @discoverly/stellar + + - name: Build stellar + run: npm run build -w @discoverly/stellar diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 9d182e9..502f65d 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -1,23 +1,44 @@ -name: Web CI +name: Web on: + push: + branches: [main] + paths: + - 'apps/web/**' + - 'packages/shared/**' + - 'package.json' + - 'package-lock.json' + - '.github/workflows/web.yml' pull_request: - branches: ['main'] paths: - - 'web/**' + - 'apps/web/**' + - 'packages/shared/**' + - 'package.json' + - 'package-lock.json' - '.github/workflows/web.yml' - push: - branches: ['main'] jobs: - web-checks: + web: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: '20' - - run: npm ci - - run: npm run lint -w web - - run: npm run typecheck -w web - - run: npm run build -w web + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build shared package + run: npm run build -w @discoverly/shared + + - name: Lint + run: npm run lint -w @discoverly/web + + - name: Test + run: npm run test -w @discoverly/web + + - name: Build + run: npm run build -w @discoverly/web diff --git a/.gitignore b/.gitignore index 618cf3e..1eb2038 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ **/dist-export **/.expo **/.turbo +**/.next +*.tsbuildinfo # Logs diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..df17263 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +node_modules +dist +build +.next +.expo +coverage +*.lock +package-lock.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..4cbc711 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "tabWidth": 2 +} diff --git a/README.md b/README.md index 189b4cb..68fafbb 100644 --- a/README.md +++ b/README.md @@ -1,362 +1,132 @@ -# Discoverly +# Discoverly — Hackathon Foundation -**Discoverly** is a swipe-first food discovery and ordering platform that helps users discover restaurants, explore meals, and complete purchases through a seamless mobile and web experience. +This repository is the foundation monorepo for Discoverly: a swipe-first +food discovery and ordering platform with Stellar-powered checkout. It +provides a clean, tested starting point with **authentication** working +end-to-end, ready for hackathon participants to build on. -The platform combines a modern food discovery experience with Stellar-powered checkout, enabling fast, affordable, and transparent payment flows between customers and restaurants. - -Discoverly is built around making food discovery more engaging by replacing traditional search-heavy restaurant browsing with an intuitive recommendation-driven experience. - ---- - -## Overview - -Traditional food ordering platforms often require users to search through large restaurant lists, compare menus, and make decisions manually. - -Discoverly introduces a more interactive approach where users can discover food through a swipe-based experience while restaurants gain tools to manage their digital presence. - -The platform consists of: - -* a mobile application for customers, -* a web dashboard for restaurants and operations, -* an API backend powering the ecosystem, -* a Stellar integration layer for payments. - ---- - -## Core Features - -## Food Discovery - -Discoverly makes finding food simple and engaging through a swipe-first interface. - -Users can: - -* browse restaurants and meals, -* swipe through food recommendations, -* discover new restaurants, -* save preferences, -* interact with personalized suggestions, -* move directly from discovery to checkout. - -The goal is to make choosing food feel more like discovery than searching. - ---- - -## Customer Experience (Mobile) - -The mobile app is the main customer-facing experience. - -Users can: - -* create and manage accounts, -* explore restaurants, -* view food listings, -* discover meals through swiping, -* manage preferences, -* place orders, -* complete payments, -* track purchase activity. - -The mobile experience is optimized for quick interactions and everyday food discovery. +Only the foundational setup and authentication functionality are +implemented. Everything else (discovery, ordering, payments, Stellar +integration, restaurant dashboards, etc.) is intentionally out of scope +for now — see [docs/architecture.md](docs/architecture.md) for how the +codebase is structured to support those features later. --- -## Restaurant Experience (Web Dashboard) - -The web application provides restaurants with the tools they need to manage their presence on Discoverly. - -Restaurants can use the dashboard for: +## Repository Structure -* restaurant onboarding, -* profile management, -* menu creation and updates, -* food item management, -* order management, -* customer activity insights, -* payment tracking, -* business operations. - -The web dashboard acts as the operational layer of the platform, allowing restaurants to manage the content and experiences customers interact with. - ---- - -## Stellar-Powered Checkout - -Discoverly uses Stellar as the payment infrastructure for transactions between customers and restaurants. - -The Stellar integration enables: - -* fast payment settlement, -* low transaction costs, -* wallet-based checkout, -* transaction verification, -* transparent payment history. - -Stellar allows Discoverly to build a modern payment experience while keeping blockchain complexity behind the scenes. - ---- - -# Tech Stack - -Discoverly is built using a full-stack TypeScript architecture. - -| Area | Technology | -| ------------------ | --------------------------------------- | -| Mobile Application | Expo + React Native + TypeScript | -| Web Dashboard | React + TypeScript | -| Backend API | Node.js + Express + TypeScript | -| Database | MongoDB + Mongoose | -| Blockchain | Stellar | -| File Storage | AWS S3 | -| Architecture | Mobile + Web + API + Blockchain Service | - ---- - -# Repository Structure - -```text -discoverly/ -│ -├── backend/ # Express API implementation -│ -├── mobile/ # Customer mobile application -│ -├── web/ # Restaurant dashboard application -│ -├── docs/ -│ └── adr/ # Architecture decision records -│ -└── legacy/ - └── nest-backend/ # Archived legacy backend (read-only) ``` +apps/ + api/ # Express + TypeScript modular monolith (auth API) + web/ # Next.js + TypeScript web app (login / create account) + mobile/ # Expo + React Native + TypeScript foundation ---- - -# Applications - -## Backend - -`backend/` - -The backend provides the central API layer for Discoverly. - -Responsibilities include: - -* authentication, -* user management, -* restaurant management, -* food discovery APIs, -* menu data, -* order workflows, -* payment coordination, -* upload handling, -* Stellar transaction communication. - -The backend uses Express, TypeScript, and MongoDB with Mongoose for data modeling. - ---- - -## Mobile App - -`mobile/` - -The mobile application provides the customer experience. - -Built with Expo and React Native, it handles: +packages/ + shared/ # Shared types & validation schemas (zod) + stellar/ # Placeholder scaffold for future Stellar integration -* food discovery, -* swipe interactions, -* restaurant browsing, -* meal selection, -* checkout, -* payment interactions, -* user preferences. - -The mobile app is designed around fast and simple customer interactions. - ---- - -## Web Dashboard - -`web/` - -The web application provides restaurant-facing functionality. - -The dashboard allows restaurants to manage their Discoverly presence and operations. - -Planned capabilities include: - -* restaurant onboarding, -* restaurant profile management, -* menu management, -* food item uploads, -* availability updates, -* order management, -* analytics, -* payment history. - -The web app is focused on helping restaurants operate efficiently while providing accurate data for the customer discovery experience. - ---- - -# Stellar Integration - -Discoverly uses Stellar as the payment layer powering checkout. - -The blockchain integration handles: - -* wallet operations, -* payment creation, -* transaction submission, -* transaction confirmation, -* payment tracking. - -The Stellar layer is separated from the core application logic to keep the system modular and easier to maintain. - ---- - -# Backend Upload Service - -The backend includes a media upload service for restaurant and food assets. - -Current endpoint: - -```http -POST /api/upload -``` - -Request: - -* Content Type: `multipart/form-data` -* File field: `file` -* Maximum size: `5MB` - -Storage: - -```text -AWS S3 +.github/workflows/ # CI for each package +docs/ # Architecture, environment, testing, contributing ``` -Required configuration: - -```text -backend/.env.example -``` +Each package's own README has package-specific details: +[apps/api](apps/api/README.md), [apps/web](apps/web/README.md), +[apps/mobile](apps/mobile/README.md). --- -# Getting Started +## Local Development Setup -## Requirements +### Requirements -Install: +- Node.js 20+ +- npm 10+ +- MongoDB (only required to run the API outside of tests — tests use an + in-memory database) -* Node.js -* npm -* Docker (optional) -* Expo tooling -* MongoDB (or Docker) - ---- - -# Installation +### Install From the repository root: ```bash -npm run bootstrap +npm install ``` ---- - -# Run Backend - -```bash -cd backend - -cp .env.example .env +This installs dependencies for every workspace (`apps/*`, `packages/*`). -npm run dev -``` - ---- +### Build shared packages -# Run Web Dashboard +`apps/api` and `apps/web` depend on `@discoverly/shared`. Build it once +after installing (and whenever you change it): ```bash -cd web - -cp .env.example .env - -npm run dev +npm run build -w @discoverly/shared ``` --- -# Run Mobile App - -```bash -cd mobile +## Running Applications -cp .env.example .env +### API (`apps/api`) -npm run start +```bash +cp apps/api/.env.example apps/api/.env +npm run dev -w @discoverly/api ``` ---- +The API listens on `http://localhost:5000` by default and exposes: -# Run Everything with Docker +- `POST /api/auth/register` +- `POST /api/auth/login` +- `GET /api/auth/me` (requires `Authorization: Bearer `) +- `GET /api/health` -From root: +### Web (`apps/web`) ```bash -docker-compose up --build +cp apps/web/.env.example apps/web/.env +npm run dev -w @discoverly/web ``` -Health check: +Visit `http://localhost:3000/login` or `http://localhost:3000/register`. + +### Mobile (`apps/mobile`) ```bash -curl http://localhost:5000/api/health +cp apps/mobile/.env.example apps/mobile/.env +npm run start -w @discoverly/mobile ``` +This launches Expo. The mobile app currently contains only the project +foundation (no screens yet). + --- -# Architecture Decisions +## Running Tests -Major technical decisions are documented in: +```bash +# everything +npm run test --workspaces --if-present -```text -docs/adr/ +# individually +npm run test -w @discoverly/api +npm run test -w @discoverly/web +npm run test -w @discoverly/mobile ``` -These cover: - -* system architecture, -* technology choices, -* backend patterns, -* Stellar integration decisions, -* scalability considerations. +See [docs/testing.md](docs/testing.md) for details. --- -# Product Vision - -Discoverly aims to transform food discovery from a manual search experience into an engaging recommendation platform. - -The roadmap focuses on: - -1. Swipe-based food discovery. -2. Personalized recommendations. -3. Restaurant digital presence. -4. Restaurant management tools. -5. Seamless checkout. -6. Stellar-powered payments. -7. A complete food discovery ecosystem. +## Documentation -The long-term vision is to create a platform where customers discover food naturally and restaurants build stronger digital relationships with their customers. +- [Architecture](docs/architecture.md) — modular monolith, package boundaries, conventions +- [Environment Setup](docs/environment.md) — required environment variables +- [Testing Guide](docs/testing.md) — running and writing tests, CI expectations +- [Contributor Guide](docs/contributing.md) — coding standards and workflow --- -# License +## License License information will be added before release. diff --git a/apps/api/.env.example b/apps/api/.env.example new file mode 100644 index 0000000..7ebf703 --- /dev/null +++ b/apps/api/.env.example @@ -0,0 +1,5 @@ +NODE_ENV=development +PORT=5000 +MONGODB_URI=mongodb://localhost:27017/discoverly +JWT_SECRET=replace_me +JWT_EXPIRES_IN=7d diff --git a/apps/api/.eslintrc.cjs b/apps/api/.eslintrc.cjs new file mode 100644 index 0000000..b57ee41 --- /dev/null +++ b/apps/api/.eslintrc.cjs @@ -0,0 +1,22 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + sourceType: 'module', + }, + plugins: ['@typescript-eslint'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + ], + env: { + node: true, + es2022: true, + }, + rules: { + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + }, + ignorePatterns: ['dist', 'node_modules'], +}; diff --git a/apps/api/README.md b/apps/api/README.md new file mode 100644 index 0000000..ad3e159 --- /dev/null +++ b/apps/api/README.md @@ -0,0 +1,49 @@ +# @discoverly/api + +Express + TypeScript backend, structured as a modular monolith. + +## Structure + +``` +src/ + modules/ + auth/ # registration, login, JWT issuance, auth middleware + users/ # user persistence (Mongoose model + repository) + shared/ + config/ # environment validation + database/ # MongoDB connection helper + errors/ # AppError + logger/ # JSON logger + middleware/# error handler, 404 handler + types/ # shared Express types + app.ts # express app wiring + server.ts # process entrypoint +``` + +Each module owns its controllers, services, repositories, validators, routes, +types, and tests. Cross-cutting infrastructure lives under `src/shared`. + +## Development + +```bash +cp .env.example .env +npm run dev -w @discoverly/api +``` + +## Testing + +Tests use Vitest, Supertest, and an in-memory MongoDB instance +(`mongodb-memory-server`), so no external database is required: + +```bash +npm run test -w @discoverly/api +``` + +## API + +| Method | Path | Auth | Description | +| ------ | ---------------- | ---- | ------------------------ | +| POST | `/api/auth/register` | No | Create an account | +| POST | `/api/auth/login` | No | Log in and receive a JWT | +| GET | `/api/auth/me` | Yes | Get the current user | +| GET | `/api/health` | No | Health check | diff --git a/backend/package.json b/apps/api/package.json similarity index 57% rename from backend/package.json rename to apps/api/package.json index e45aed0..f9b9960 100644 --- a/backend/package.json +++ b/apps/api/package.json @@ -1,21 +1,18 @@ { - "name": "@discoverly/backend", + "name": "@discoverly/api", "version": "0.1.0", "private": true, "type": "module", "scripts": { - "dev": "tsx watch src/index.ts", + "dev": "tsx watch src/server.ts", "build": "tsc -p tsconfig.json", - "start": "node dist/index.js", - "lint": "eslint \"src/**/*.ts\" \"scripts/**/*.ts\"", - "format": "prettier --check \"src/**/*.ts\" \"scripts/**/*.ts\" \"test/**/*.ts\"", - "format:write": "prettier --write \"src/**/*.ts\" \"scripts/**/*.ts\" \"test/**/*.ts\"", + "start": "node dist/server.js", + "lint": "eslint \"src/**/*.ts\"", "typecheck": "tsc --noEmit", - "test": "NODE_ENV=test MONGODB_URI=mongodb://localhost:27017/discoverly_test JWT_SECRET=test_secret tsx --test test/*.test.ts", - "seed": "tsx scripts/seed.ts" + "test": "vitest run" }, "dependencies": { - "@aws-sdk/client-s3": "^3.883.0", + "@discoverly/shared": "*", "bcryptjs": "^2.4.3", "cors": "^2.8.5", "dotenv": "^16.4.5", @@ -25,7 +22,6 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.6.2", "morgan": "^1.10.0", - "multer": "^1.4.5-lts.1", "zod": "^3.23.8" }, "devDependencies": { @@ -34,16 +30,16 @@ "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.6", "@types/morgan": "^1.9.9", - "@types/multer": "^1.4.12", "@types/node": "^20.16.5", "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "eslint": "8.57.1", "eslint-config-prettier": "^9.1.0", - "prettier": "^3.3.3", + "mongodb-memory-server": "^9.4.1", "supertest": "^7.0.0", "tsx": "^4.19.1", - "typescript": "^5.6.2" + "typescript": "^5.6.2", + "vitest": "^2.1.1" } } diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts new file mode 100644 index 0000000..23a800a --- /dev/null +++ b/apps/api/src/app.ts @@ -0,0 +1,33 @@ +import cors from 'cors'; +import express, { type Express } from 'express'; +import { rateLimit } from 'express-rate-limit'; +import helmet from 'helmet'; +import morgan from 'morgan'; +import { authRouter } from './modules/auth/routes/auth.routes.js'; +import { errorHandler } from './shared/middleware/error-handler.js'; +import { notFoundHandler } from './shared/middleware/not-found.js'; + +export function createApp(): Express { + const app = express(); + + app.use(helmet()); + app.use(cors()); + app.use(express.json()); + app.use(morgan('dev')); + + app.use( + '/api/auth', + rateLimit({ windowMs: 15 * 60 * 1000, limit: 100, standardHeaders: true, legacyHeaders: false }), + ); + + app.get('/api/health', (_req, res) => { + res.status(200).json({ status: 'ok' }); + }); + + app.use('/api/auth', authRouter); + + app.use(notFoundHandler); + app.use(errorHandler); + + return app; +} diff --git a/apps/api/src/modules/auth/controllers/auth.controller.ts b/apps/api/src/modules/auth/controllers/auth.controller.ts new file mode 100644 index 0000000..9c26fc1 --- /dev/null +++ b/apps/api/src/modules/auth/controllers/auth.controller.ts @@ -0,0 +1,46 @@ +import type { NextFunction, Request, Response } from 'express'; +import { AppError } from '../../../shared/errors/app-error.js'; +import { userRepository } from '../../users/repositories/user.repository.js'; +import { toPublicUser } from '../../users/services/user.service.js'; +import { authService } from '../services/auth.service.js'; +import type { AuthenticatedRequest } from '../../../shared/types/express.js'; +import { loginSchema, registerSchema } from '../validators/auth.validators.js'; + +export const authController = { + async register(req: Request, res: Response, next: NextFunction): Promise { + try { + const { email, password } = registerSchema.parse(req.body); + const result = await authService.register(email, password); + res.status(201).json(result); + } catch (error) { + next(error); + } + }, + + async login(req: Request, res: Response, next: NextFunction): Promise { + try { + const { email, password } = loginSchema.parse(req.body); + const result = await authService.login(email, password); + res.status(200).json(result); + } catch (error) { + next(error); + } + }, + + async me(req: AuthenticatedRequest, res: Response, next: NextFunction): Promise { + try { + if (!req.user) { + throw AppError.unauthorized(); + } + + const user = await userRepository.findById(req.user.id); + if (!user) { + throw AppError.unauthorized(); + } + + res.status(200).json({ user: toPublicUser(user) }); + } catch (error) { + next(error); + } + }, +}; diff --git a/apps/api/src/modules/auth/middleware/authenticate.ts b/apps/api/src/modules/auth/middleware/authenticate.ts new file mode 100644 index 0000000..7de9403 --- /dev/null +++ b/apps/api/src/modules/auth/middleware/authenticate.ts @@ -0,0 +1,23 @@ +import type { NextFunction, Response } from 'express'; +import { AppError } from '../../../shared/errors/app-error.js'; +import type { AuthenticatedRequest } from '../../../shared/types/express.js'; +import { authService } from '../services/auth.service.js'; + +export function authenticate(req: AuthenticatedRequest, _res: Response, next: NextFunction): void { + const header = req.headers.authorization; + + if (!header?.startsWith('Bearer ')) { + next(AppError.unauthorized('Missing or invalid Authorization header')); + return; + } + + const token = header.slice('Bearer '.length); + + try { + const payload = authService.verifyToken(token); + req.user = { id: payload.sub, email: payload.email }; + next(); + } catch (error) { + next(error); + } +} diff --git a/apps/api/src/modules/auth/routes/auth.routes.ts b/apps/api/src/modules/auth/routes/auth.routes.ts new file mode 100644 index 0000000..5d8c5a7 --- /dev/null +++ b/apps/api/src/modules/auth/routes/auth.routes.ts @@ -0,0 +1,9 @@ +import { Router } from 'express'; +import { authController } from '../controllers/auth.controller.js'; +import { authenticate } from '../middleware/authenticate.js'; + +export const authRouter = Router(); + +authRouter.post('/register', authController.register); +authRouter.post('/login', authController.login); +authRouter.get('/me', authenticate, authController.me); diff --git a/apps/api/src/modules/auth/services/auth.service.ts b/apps/api/src/modules/auth/services/auth.service.ts new file mode 100644 index 0000000..44fe84a --- /dev/null +++ b/apps/api/src/modules/auth/services/auth.service.ts @@ -0,0 +1,60 @@ +import bcrypt from 'bcryptjs'; +import jwt from 'jsonwebtoken'; +import type { AuthResponse } from '@discoverly/shared'; +import { env } from '../../../shared/config/env.js'; +import { AppError } from '../../../shared/errors/app-error.js'; +import { userRepository } from '../../users/repositories/user.repository.js'; +import { toPublicUser } from '../../users/services/user.service.js'; +import type { JwtPayload } from '../types/auth.types.js'; + +const PASSWORD_SALT_ROUNDS = 10; + +function signToken(payload: JwtPayload): string { + const options: jwt.SignOptions = { expiresIn: env.JWT_EXPIRES_IN as jwt.SignOptions['expiresIn'] }; + return jwt.sign(payload, env.JWT_SECRET, options); +} + +export const authService = { + async register(email: string, password: string): Promise { + const existing = await userRepository.findByEmail(email); + if (existing) { + throw AppError.conflict('An account with this email already exists'); + } + + const passwordHash = await bcrypt.hash(password, PASSWORD_SALT_ROUNDS); + const user = await userRepository.create(email, passwordHash); + const publicUser = toPublicUser(user); + + return { + user: publicUser, + token: signToken({ sub: publicUser.id, email: publicUser.email }), + }; + }, + + async login(email: string, password: string): Promise { + const user = await userRepository.findByEmail(email); + if (!user) { + throw AppError.unauthorized('Invalid email or password'); + } + + const passwordMatches = await bcrypt.compare(password, user.passwordHash); + if (!passwordMatches) { + throw AppError.unauthorized('Invalid email or password'); + } + + const publicUser = toPublicUser(user); + + return { + user: publicUser, + token: signToken({ sub: publicUser.id, email: publicUser.email }), + }; + }, + + verifyToken(token: string): JwtPayload { + try { + return jwt.verify(token, env.JWT_SECRET) as JwtPayload; + } catch { + throw AppError.unauthorized('Invalid or expired token'); + } + }, +}; diff --git a/apps/api/src/modules/auth/tests/auth.test.ts b/apps/api/src/modules/auth/tests/auth.test.ts new file mode 100644 index 0000000..cfcd827 --- /dev/null +++ b/apps/api/src/modules/auth/tests/auth.test.ts @@ -0,0 +1,102 @@ +import { MongoMemoryServer } from 'mongodb-memory-server'; +import mongoose from 'mongoose'; +import request from 'supertest'; +import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'; +import { createApp } from '../../../app.js'; + +let mongoServer: MongoMemoryServer; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + await mongoose.connect(mongoServer.getUri()); +}); + +afterEach(async () => { + await mongoose.connection.dropDatabase(); +}); + +afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); +}); + +const app = createApp(); + +describe('POST /api/auth/register', () => { + it('registers a new user successfully', async () => { + const response = await request(app).post('/api/auth/register').send({ + email: 'new-user@example.com', + password: 'password123', + }); + + expect(response.status).toBe(201); + expect(response.body.user.email).toBe('new-user@example.com'); + expect(response.body.token).toBeTypeOf('string'); + expect(response.body.user.passwordHash).toBeUndefined(); + }); + + it('rejects registration with a duplicate email', async () => { + await request(app).post('/api/auth/register').send({ + email: 'duplicate@example.com', + password: 'password123', + }); + + const response = await request(app).post('/api/auth/register').send({ + email: 'duplicate@example.com', + password: 'anotherPassword123', + }); + + expect(response.status).toBe(409); + }); + + it('rejects registration with invalid input', async () => { + const response = await request(app).post('/api/auth/register').send({ + email: 'not-an-email', + password: 'short', + }); + + expect(response.status).toBe(400); + expect(response.body.errors).toBeDefined(); + }); +}); + +describe('POST /api/auth/login', () => { + it('logs in successfully with valid credentials', async () => { + await request(app).post('/api/auth/register').send({ + email: 'login-user@example.com', + password: 'password123', + }); + + const response = await request(app).post('/api/auth/login').send({ + email: 'login-user@example.com', + password: 'password123', + }); + + expect(response.status).toBe(200); + expect(response.body.token).toBeTypeOf('string'); + expect(response.body.user.email).toBe('login-user@example.com'); + }); + + it('rejects login with an invalid password', async () => { + await request(app).post('/api/auth/register').send({ + email: 'wrong-password@example.com', + password: 'password123', + }); + + const response = await request(app).post('/api/auth/login').send({ + email: 'wrong-password@example.com', + password: 'incorrect-password', + }); + + expect(response.status).toBe(401); + }); + + it('rejects login for a non-existent account', async () => { + const response = await request(app).post('/api/auth/login').send({ + email: 'does-not-exist@example.com', + password: 'password123', + }); + + expect(response.status).toBe(401); + }); +}); diff --git a/apps/api/src/modules/auth/types/auth.types.ts b/apps/api/src/modules/auth/types/auth.types.ts new file mode 100644 index 0000000..cb10558 --- /dev/null +++ b/apps/api/src/modules/auth/types/auth.types.ts @@ -0,0 +1,4 @@ +export interface JwtPayload { + sub: string; + email: string; +} diff --git a/apps/api/src/modules/auth/validators/auth.validators.ts b/apps/api/src/modules/auth/validators/auth.validators.ts new file mode 100644 index 0000000..632ed9e --- /dev/null +++ b/apps/api/src/modules/auth/validators/auth.validators.ts @@ -0,0 +1,2 @@ +export { loginSchema, registerSchema } from '@discoverly/shared'; +export type { LoginInput, RegisterInput } from '@discoverly/shared'; diff --git a/apps/api/src/modules/users/repositories/user.model.ts b/apps/api/src/modules/users/repositories/user.model.ts new file mode 100644 index 0000000..63ba39d --- /dev/null +++ b/apps/api/src/modules/users/repositories/user.model.ts @@ -0,0 +1,14 @@ +import { Schema, model, type InferSchemaType, type HydratedDocument } from 'mongoose'; + +const userSchema = new Schema( + { + email: { type: String, required: true, unique: true, lowercase: true, trim: true }, + passwordHash: { type: String, required: true }, + }, + { timestamps: true }, +); + +export type UserAttributes = InferSchemaType; +export type UserDocument = HydratedDocument; + +export const UserModel = model('User', userSchema); diff --git a/apps/api/src/modules/users/repositories/user.repository.ts b/apps/api/src/modules/users/repositories/user.repository.ts new file mode 100644 index 0000000..82c7078 --- /dev/null +++ b/apps/api/src/modules/users/repositories/user.repository.ts @@ -0,0 +1,15 @@ +import { UserModel, type UserDocument } from './user.model.js'; + +export const userRepository = { + async findByEmail(email: string): Promise { + return UserModel.findOne({ email: email.toLowerCase() }); + }, + + async findById(id: string): Promise { + return UserModel.findById(id); + }, + + async create(email: string, passwordHash: string): Promise { + return UserModel.create({ email: email.toLowerCase(), passwordHash }); + }, +}; diff --git a/apps/api/src/modules/users/services/user.service.ts b/apps/api/src/modules/users/services/user.service.ts new file mode 100644 index 0000000..8b47306 --- /dev/null +++ b/apps/api/src/modules/users/services/user.service.ts @@ -0,0 +1,11 @@ +import type { UserDocument } from '../repositories/user.model.js'; +import type { PublicUser } from '../types/user.types.js'; + +export function toPublicUser(user: UserDocument): PublicUser { + return { + id: user._id.toString(), + email: user.email, + createdAt: user.createdAt.toISOString(), + updatedAt: user.updatedAt.toISOString(), + }; +} diff --git a/apps/api/src/modules/users/types/user.types.ts b/apps/api/src/modules/users/types/user.types.ts new file mode 100644 index 0000000..6bec406 --- /dev/null +++ b/apps/api/src/modules/users/types/user.types.ts @@ -0,0 +1,14 @@ +export interface UserRecord { + id: string; + email: string; + passwordHash: string; + createdAt: Date; + updatedAt: Date; +} + +export interface PublicUser { + id: string; + email: string; + createdAt: string; + updatedAt: string; +} diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts new file mode 100644 index 0000000..c91d8d7 --- /dev/null +++ b/apps/api/src/server.ts @@ -0,0 +1,19 @@ +import { createApp } from './app.js'; +import { env } from './shared/config/env.js'; +import { connectDatabase } from './shared/database/connection.js'; +import { logger } from './shared/logger/logger.js'; + +async function main(): Promise { + await connectDatabase(); + + const app = createApp(); + + app.listen(env.PORT, () => { + logger.info(`API server listening on port ${env.PORT}`); + }); +} + +main().catch((error) => { + logger.error('Failed to start server', { error: error instanceof Error ? error.message : error }); + process.exit(1); +}); diff --git a/apps/api/src/shared/config/env.ts b/apps/api/src/shared/config/env.ts new file mode 100644 index 0000000..19cfc6f --- /dev/null +++ b/apps/api/src/shared/config/env.ts @@ -0,0 +1,14 @@ +import 'dotenv/config'; +import { z } from 'zod'; + +const envSchema = z.object({ + NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), + PORT: z.coerce.number().int().positive().default(5000), + MONGODB_URI: z.string().min(1, 'MONGODB_URI is required'), + JWT_SECRET: z.string().min(1, 'JWT_SECRET is required'), + JWT_EXPIRES_IN: z.string().default('7d'), +}); + +export type Env = z.infer; + +export const env: Env = envSchema.parse(process.env); diff --git a/apps/api/src/shared/database/connection.ts b/apps/api/src/shared/database/connection.ts new file mode 100644 index 0000000..2c23d59 --- /dev/null +++ b/apps/api/src/shared/database/connection.ts @@ -0,0 +1,14 @@ +import mongoose from 'mongoose'; +import { env } from '../config/env.js'; +import { logger } from '../logger/logger.js'; + +export async function connectDatabase(uri: string = env.MONGODB_URI): Promise { + mongoose.set('strictQuery', true); + const connection = await mongoose.connect(uri); + logger.info(`Connected to MongoDB at ${connection.connection.host}`); + return connection; +} + +export async function disconnectDatabase(): Promise { + await mongoose.disconnect(); +} diff --git a/apps/api/src/shared/errors/app-error.ts b/apps/api/src/shared/errors/app-error.ts new file mode 100644 index 0000000..c740aff --- /dev/null +++ b/apps/api/src/shared/errors/app-error.ts @@ -0,0 +1,27 @@ +export class AppError extends Error { + public readonly statusCode: number; + public readonly details?: unknown; + + constructor(message: string, statusCode = 500, details?: unknown) { + super(message); + this.name = 'AppError'; + this.statusCode = statusCode; + this.details = details; + } + + static badRequest(message: string, details?: unknown): AppError { + return new AppError(message, 400, details); + } + + static unauthorized(message = 'Unauthorized'): AppError { + return new AppError(message, 401); + } + + static conflict(message: string): AppError { + return new AppError(message, 409); + } + + static notFound(message = 'Not found'): AppError { + return new AppError(message, 404); + } +} diff --git a/apps/api/src/shared/logger/logger.ts b/apps/api/src/shared/logger/logger.ts new file mode 100644 index 0000000..fbd0e46 --- /dev/null +++ b/apps/api/src/shared/logger/logger.ts @@ -0,0 +1,26 @@ +type LogLevel = 'info' | 'warn' | 'error'; + +function log(level: LogLevel, message: string, meta?: unknown): void { + const entry = { + level, + message, + timestamp: new Date().toISOString(), + ...(meta ? { meta } : {}), + }; + + const line = JSON.stringify(entry); + + if (level === 'error') { + console.error(line); + } else if (level === 'warn') { + console.warn(line); + } else { + console.log(line); + } +} + +export const logger = { + info: (message: string, meta?: unknown) => log('info', message, meta), + warn: (message: string, meta?: unknown) => log('warn', message, meta), + error: (message: string, meta?: unknown) => log('error', message, meta), +}; diff --git a/apps/api/src/shared/middleware/error-handler.ts b/apps/api/src/shared/middleware/error-handler.ts new file mode 100644 index 0000000..e20c728 --- /dev/null +++ b/apps/api/src/shared/middleware/error-handler.ts @@ -0,0 +1,30 @@ +import type { NextFunction, Request, Response } from 'express'; +import { ZodError } from 'zod'; +import { AppError } from '../errors/app-error.js'; +import { logger } from '../logger/logger.js'; + +export function errorHandler( + err: unknown, + _req: Request, + res: Response, + _next: NextFunction, +): void { + if (err instanceof ZodError) { + res.status(400).json({ + message: 'Validation failed', + errors: err.flatten().fieldErrors, + }); + return; + } + + if (err instanceof AppError) { + res.status(err.statusCode).json({ + message: err.message, + ...(err.details ? { errors: err.details } : {}), + }); + return; + } + + logger.error('Unhandled error', { error: err instanceof Error ? err.message : err }); + res.status(500).json({ message: 'Internal server error' }); +} diff --git a/apps/api/src/shared/middleware/not-found.ts b/apps/api/src/shared/middleware/not-found.ts new file mode 100644 index 0000000..889168b --- /dev/null +++ b/apps/api/src/shared/middleware/not-found.ts @@ -0,0 +1,5 @@ +import type { Request, Response } from 'express'; + +export function notFoundHandler(_req: Request, res: Response): void { + res.status(404).json({ message: 'Resource not found' }); +} diff --git a/apps/api/src/shared/types/express.ts b/apps/api/src/shared/types/express.ts new file mode 100644 index 0000000..7a1d69f --- /dev/null +++ b/apps/api/src/shared/types/express.ts @@ -0,0 +1,10 @@ +import type { Request } from 'express'; + +export interface AuthenticatedUser { + id: string; + email: string; +} + +export interface AuthenticatedRequest extends Request { + user?: AuthenticatedUser; +} diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..991700c --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2022"], + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src"] +} diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts new file mode 100644 index 0000000..0bb60b1 --- /dev/null +++ b/apps/api/vitest.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + globals: true, + testTimeout: 30000, + hookTimeout: 30000, + env: { + NODE_ENV: 'test', + MONGODB_URI: 'mongodb://localhost:27017/discoverly_test', + JWT_SECRET: 'test_secret', + JWT_EXPIRES_IN: '7d', + }, + }, +}); diff --git a/apps/mobile/.env.example b/apps/mobile/.env.example new file mode 100644 index 0000000..3767da9 --- /dev/null +++ b/apps/mobile/.env.example @@ -0,0 +1 @@ +EXPO_PUBLIC_API_URL=http://localhost:5000 diff --git a/apps/mobile/.eslintrc.cjs b/apps/mobile/.eslintrc.cjs new file mode 100644 index 0000000..9afb2eb --- /dev/null +++ b/apps/mobile/.eslintrc.cjs @@ -0,0 +1,5 @@ +module.exports = { + root: true, + extends: ['expo', 'prettier'], + ignorePatterns: ['/dist/*'], +}; diff --git a/apps/mobile/App.tsx b/apps/mobile/App.tsx new file mode 100644 index 0000000..97145cc --- /dev/null +++ b/apps/mobile/App.tsx @@ -0,0 +1,30 @@ +import { StatusBar } from 'expo-status-bar'; +import { StyleSheet, Text, View } from 'react-native'; + +export default function App() { + return ( + + Discoverly + Mobile foundation ready for development. + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + alignItems: 'center', + justifyContent: 'center', + }, + title: { + fontSize: 24, + fontWeight: '700', + }, + subtitle: { + marginTop: 8, + fontSize: 14, + color: '#666', + }, +}); diff --git a/apps/mobile/README.md b/apps/mobile/README.md new file mode 100644 index 0000000..2c32af4 --- /dev/null +++ b/apps/mobile/README.md @@ -0,0 +1,31 @@ +# @discoverly/mobile + +Expo + React Native + TypeScript foundation for the customer mobile app. + +This package currently contains **only project setup** — no screens, +navigation flows, or authentication. The folder structure below is in +place so feature work can be added without restructuring: + +``` +src/ + components/ # shared UI components + screens/ # app screens + navigation/ # navigators + hooks/ # custom hooks + services/ # API/service clients + utils/ # helpers + assets/ # images, fonts, etc. +``` + +## Development + +```bash +cp .env.example .env +npm run start -w @discoverly/mobile +``` + +## Testing + +```bash +npm run test -w @discoverly/mobile +``` diff --git a/apps/mobile/__tests__/App.test.tsx b/apps/mobile/__tests__/App.test.tsx new file mode 100644 index 0000000..2c509f9 --- /dev/null +++ b/apps/mobile/__tests__/App.test.tsx @@ -0,0 +1,11 @@ +import { render } from '@testing-library/react-native'; +import App from '../App'; + +describe('App', () => { + it('renders the foundation screen', () => { + const { getByText } = render(); + + expect(getByText('Discoverly')).toBeTruthy(); + expect(getByText('Mobile foundation ready for development.')).toBeTruthy(); + }); +}); diff --git a/apps/mobile/app.json b/apps/mobile/app.json new file mode 100644 index 0000000..6cf538f --- /dev/null +++ b/apps/mobile/app.json @@ -0,0 +1,21 @@ +{ + "expo": { + "name": "Discoverly", + "slug": "discoverly-mobile", + "version": "0.1.0", + "orientation": "portrait", + "scheme": "discoverly", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true, + "bundleIdentifier": "com.discoverly.mobile" + }, + "android": { + "package": "com.discoverly.mobile" + }, + "web": { + "bundler": "metro" + } + } +} diff --git a/apps/mobile/babel.config.js b/apps/mobile/babel.config.js new file mode 100644 index 0000000..9d89e13 --- /dev/null +++ b/apps/mobile/babel.config.js @@ -0,0 +1,6 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + }; +}; diff --git a/apps/mobile/package.json b/apps/mobile/package.json new file mode 100644 index 0000000..2b7bf81 --- /dev/null +++ b/apps/mobile/package.json @@ -0,0 +1,37 @@ +{ + "name": "@discoverly/mobile", + "version": "0.1.0", + "private": true, + "main": "node_modules/expo/AppEntry.js", + "scripts": { + "start": "expo start", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "lint": "eslint --no-error-on-unmatched-pattern \"src/**/*.{ts,tsx}\" \"App.tsx\" \"__tests__/**/*.{ts,tsx}\"", + "typecheck": "tsc --noEmit", + "test": "jest" + }, + "dependencies": { + "expo": "~52.0.0", + "expo-status-bar": "~2.0.0", + "react": "18.3.1", + "react-native": "0.76.3" + }, + "devDependencies": { + "@babel/core": "^7.25.2", + "@testing-library/react-native": "^12.7.2", + "@types/jest": "^29.5.12", + "@types/react": "~18.3.12", + "eslint": "8.57.1", + "eslint-config-expo": "^8.0.0", + "eslint-config-prettier": "^9.1.0", + "jest": "^29.7.0", + "jest-expo": "~52.0.2", + "react-test-renderer": "18.3.1", + "typescript": "^5.6.2" + }, + "jest": { + "preset": "jest-expo" + } +} diff --git a/apps/mobile/src/assets/.gitkeep b/apps/mobile/src/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/src/components/.gitkeep b/apps/mobile/src/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/src/hooks/.gitkeep b/apps/mobile/src/hooks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/src/navigation/.gitkeep b/apps/mobile/src/navigation/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/src/screens/.gitkeep b/apps/mobile/src/screens/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/src/services/.gitkeep b/apps/mobile/src/services/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/apps/mobile/src/utils/.gitkeep b/apps/mobile/src/utils/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/mobile/tsconfig.json b/apps/mobile/tsconfig.json similarity index 55% rename from mobile/tsconfig.json rename to apps/mobile/tsconfig.json index 89a4bd3..c07b778 100644 --- a/mobile/tsconfig.json +++ b/apps/mobile/tsconfig.json @@ -2,10 +2,9 @@ "extends": "expo/tsconfig.base", "compilerOptions": { "strict": true, - "baseUrl": ".", "paths": { - "@/*": ["./*"] + "@/*": ["./src/*"] } }, - "exclude": ["dist-export", "node_modules"] + "include": ["**/*.ts", "**/*.tsx"] } diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..0c368c3 --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1 @@ +NEXT_PUBLIC_API_URL=http://localhost:5000 diff --git a/apps/web/.eslintrc.cjs b/apps/web/.eslintrc.cjs new file mode 100644 index 0000000..6042768 --- /dev/null +++ b/apps/web/.eslintrc.cjs @@ -0,0 +1,30 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + sourceType: 'module', + ecmaFeatures: { jsx: true }, + }, + plugins: ['@typescript-eslint', 'react', 'react-hooks'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended', + 'prettier', + ], + settings: { + react: { version: 'detect' }, + }, + env: { + browser: true, + es2022: true, + node: true, + }, + rules: { + 'react/react-in-jsx-scope': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + }, + ignorePatterns: ['.next', 'dist', 'node_modules'], +}; diff --git a/apps/web/README.md b/apps/web/README.md new file mode 100644 index 0000000..569af5f --- /dev/null +++ b/apps/web/README.md @@ -0,0 +1,28 @@ +# @discoverly/web + +Next.js (App Router) + TypeScript web client providing authentication +(login and account creation) against `@discoverly/api`. + +## Development + +```bash +cp .env.example .env +npm run dev -w @discoverly/web +``` + +Set `NEXT_PUBLIC_API_URL` to the URL of the running API (defaults to +`http://localhost:5000`). + +## Pages + +- `/login` — log in with an existing account +- `/register` — create a new account + +Auth state (user + token) is managed via `AuthProvider` +(`lib/auth-context.tsx`) and persisted to `localStorage`. + +## Testing + +```bash +npm run test -w @discoverly/web +``` diff --git a/apps/web/__tests__/login-page.test.tsx b/apps/web/__tests__/login-page.test.tsx new file mode 100644 index 0000000..6c91d21 --- /dev/null +++ b/apps/web/__tests__/login-page.test.tsx @@ -0,0 +1,40 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import LoginPage from '../app/login/page'; +import { AuthProvider } from '../lib/auth-context'; + +vi.mock('next/navigation', () => ({ + useRouter: () => ({ push: vi.fn() }), +})); + +function renderLoginPage() { + return render( + + + , + ); +} + +describe('LoginPage', () => { + it('renders the login form', () => { + renderLoginPage(); + + expect(screen.getByRole('heading', { name: 'Log in' })).toBeInTheDocument(); + expect(screen.getByLabelText('Email')).toBeInTheDocument(); + expect(screen.getByLabelText('Password')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Log in' })).toBeInTheDocument(); + }); + + it('shows validation errors for invalid input', async () => { + renderLoginPage(); + + fireEvent.change(screen.getByLabelText('Email'), { target: { value: 'not-an-email' } }); + fireEvent.change(screen.getByLabelText('Password'), { target: { value: '' } }); + fireEvent.click(screen.getByRole('button', { name: 'Log in' })); + + await waitFor(() => { + expect(screen.getByText('Enter a valid email address')).toBeInTheDocument(); + expect(screen.getByText('Password is required')).toBeInTheDocument(); + }); + }); +}); diff --git a/apps/web/__tests__/register-page.test.tsx b/apps/web/__tests__/register-page.test.tsx new file mode 100644 index 0000000..3ae3773 --- /dev/null +++ b/apps/web/__tests__/register-page.test.tsx @@ -0,0 +1,39 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import RegisterPage from '../app/register/page'; +import { AuthProvider } from '../lib/auth-context'; + +vi.mock('next/navigation', () => ({ + useRouter: () => ({ push: vi.fn() }), +})); + +function renderRegisterPage() { + return render( + + + , + ); +} + +describe('RegisterPage', () => { + it('renders the registration form', () => { + renderRegisterPage(); + + expect(screen.getByRole('heading', { name: 'Create account' })).toBeInTheDocument(); + expect(screen.getByLabelText('Email')).toBeInTheDocument(); + expect(screen.getByLabelText('Password')).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'Create account' })).toBeInTheDocument(); + }); + + it('shows a validation error when the password is too short', async () => { + renderRegisterPage(); + + fireEvent.change(screen.getByLabelText('Email'), { target: { value: 'user@example.com' } }); + fireEvent.change(screen.getByLabelText('Password'), { target: { value: 'short' } }); + fireEvent.click(screen.getByRole('button', { name: 'Create account' })); + + await waitFor(() => { + expect(screen.getByText('Password must be at least 8 characters long')).toBeInTheDocument(); + }); + }); +}); diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css new file mode 100644 index 0000000..71f4d73 --- /dev/null +++ b/apps/web/app/globals.css @@ -0,0 +1,61 @@ +:root { + font-family: system-ui, sans-serif; +} + +body { + margin: 0; + display: flex; + min-height: 100vh; + align-items: center; + justify-content: center; + background: #f5f5f5; +} + +main { + width: 100%; + max-width: 360px; + padding: 2rem; + background: #fff; + border-radius: 8px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1); +} + +.field { + margin-bottom: 1rem; + display: flex; + flex-direction: column; +} + +.field label { + margin-bottom: 0.25rem; + font-weight: 600; +} + +.field input { + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; +} + +.field-error, +.form-error { + color: #c0392b; + font-size: 0.875rem; + margin: 0.25rem 0 0; +} + +button { + width: 100%; + padding: 0.6rem; + border: none; + border-radius: 4px; + background: #111; + color: #fff; + font-weight: 600; + cursor: pointer; +} + +button:disabled { + opacity: 0.6; + cursor: not-allowed; +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx new file mode 100644 index 0000000..c20d3e0 --- /dev/null +++ b/apps/web/app/layout.tsx @@ -0,0 +1,21 @@ +import type { Metadata } from 'next'; +import type { ReactNode } from 'react'; +import { AuthProvider } from '../lib/auth-context'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'Discoverly', + description: 'Sign in to Discoverly', +}; + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + + +
{children}
+
+ + + ); +} diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx new file mode 100644 index 0000000..09fde0b --- /dev/null +++ b/apps/web/app/login/page.tsx @@ -0,0 +1,26 @@ +'use client'; + +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { AuthForm } from '../../components/AuthForm'; +import { useAuth } from '../../lib/auth-context'; + +export default function LoginPage() { + const { login } = useAuth(); + const router = useRouter(); + + async function handleSubmit(email: string, password: string) { + await login(email, password); + router.push('/'); + } + + return ( + <> +

Log in

+ +

+ Need an account? Create one +

+ + ); +} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx new file mode 100644 index 0000000..acf7fec --- /dev/null +++ b/apps/web/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from 'next/navigation'; + +export default function RootPage() { + redirect('/login'); +} diff --git a/apps/web/app/register/page.tsx b/apps/web/app/register/page.tsx new file mode 100644 index 0000000..2f58390 --- /dev/null +++ b/apps/web/app/register/page.tsx @@ -0,0 +1,26 @@ +'use client'; + +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { AuthForm } from '../../components/AuthForm'; +import { useAuth } from '../../lib/auth-context'; + +export default function RegisterPage() { + const { register } = useAuth(); + const router = useRouter(); + + async function handleSubmit(email: string, password: string) { + await register(email, password); + router.push('/'); + } + + return ( + <> +

Create account

+ +

+ Already have an account? Log in +

+ + ); +} diff --git a/apps/web/components/AuthForm.tsx b/apps/web/components/AuthForm.tsx new file mode 100644 index 0000000..8a5a8d5 --- /dev/null +++ b/apps/web/components/AuthForm.tsx @@ -0,0 +1,79 @@ +'use client'; + +import { loginSchema, registerSchema } from '@discoverly/shared'; +import { useState, type FormEvent } from 'react'; +import { TextField } from './TextField'; + +interface AuthFormProps { + mode: 'login' | 'register'; + onSubmit: (email: string, password: string) => Promise; +} + +const schemas = { + login: loginSchema, + register: registerSchema, +}; + +export function AuthForm({ mode, onSubmit }: AuthFormProps) { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string }>({}); + const [formError, setFormError] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + const submitLabel = mode === 'login' ? 'Log in' : 'Create account'; + + async function handleSubmit(event: FormEvent) { + event.preventDefault(); + setFormError(null); + + const result = schemas[mode].safeParse({ email, password }); + if (!result.success) { + const errors = result.error.flatten().fieldErrors; + setFieldErrors({ email: errors.email?.[0], password: errors.password?.[0] }); + return; + } + + setFieldErrors({}); + setIsSubmitting(true); + + try { + await onSubmit(result.data.email, result.data.password); + } catch (error) { + setFormError(error instanceof Error ? error.message : 'Something went wrong.'); + } finally { + setIsSubmitting(false); + } + } + + return ( +
+ + + {formError ? ( +

+ {formError} +

+ ) : null} + + + ); +} diff --git a/apps/web/components/TextField.tsx b/apps/web/components/TextField.tsx new file mode 100644 index 0000000..4c39e20 --- /dev/null +++ b/apps/web/components/TextField.tsx @@ -0,0 +1,32 @@ +interface TextFieldProps { + id: string; + label: string; + type: string; + value: string; + onChange: (value: string) => void; + error?: string; + autoComplete?: string; +} + +export function TextField({ id, label, type, value, onChange, error, autoComplete }: TextFieldProps) { + return ( +
+ + onChange(event.target.value)} + aria-invalid={Boolean(error)} + aria-describedby={error ? `${id}-error` : undefined} + /> + {error ? ( + + ) : null} +
+ ); +} diff --git a/apps/web/lib/api-client.ts b/apps/web/lib/api-client.ts new file mode 100644 index 0000000..e96d9ae --- /dev/null +++ b/apps/web/lib/api-client.ts @@ -0,0 +1,27 @@ +import type { AuthResponse } from '@discoverly/shared'; + +const API_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:5000'; + +async function request(path: string, body: unknown): Promise { + const response = await fetch(`${API_URL}${path}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message ?? 'Something went wrong. Please try again.'); + } + + return data as T; +} + +export function registerRequest(email: string, password: string): Promise { + return request('/api/auth/register', { email, password }); +} + +export function loginRequest(email: string, password: string): Promise { + return request('/api/auth/login', { email, password }); +} diff --git a/apps/web/lib/auth-context.tsx b/apps/web/lib/auth-context.tsx new file mode 100644 index 0000000..34ae33a --- /dev/null +++ b/apps/web/lib/auth-context.tsx @@ -0,0 +1,81 @@ +'use client'; + +import type { AuthUser } from '@discoverly/shared'; +import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react'; +import { loginRequest, registerRequest } from './api-client'; + +const TOKEN_STORAGE_KEY = 'discoverly_token'; +const USER_STORAGE_KEY = 'discoverly_user'; + +interface AuthContextValue { + user: AuthUser | null; + token: string | null; + isLoading: boolean; + login: (email: string, password: string) => Promise; + register: (email: string, password: string) => Promise; + logout: () => void; +} + +const AuthContext = createContext(undefined); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [user, setUser] = useState(null); + const [token, setToken] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const storedToken = window.localStorage.getItem(TOKEN_STORAGE_KEY); + const storedUser = window.localStorage.getItem(USER_STORAGE_KEY); + + if (storedToken && storedUser) { + setToken(storedToken); + setUser(JSON.parse(storedUser) as AuthUser); + } + + setIsLoading(false); + }, []); + + const persistSession = useCallback((nextUser: AuthUser, nextToken: string) => { + setUser(nextUser); + setToken(nextToken); + window.localStorage.setItem(TOKEN_STORAGE_KEY, nextToken); + window.localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(nextUser)); + }, []); + + const login = useCallback( + async (email: string, password: string) => { + const result = await loginRequest(email, password); + persistSession(result.user, result.token); + }, + [persistSession], + ); + + const register = useCallback( + async (email: string, password: string) => { + const result = await registerRequest(email, password); + persistSession(result.user, result.token); + }, + [persistSession], + ); + + const logout = useCallback(() => { + setUser(null); + setToken(null); + window.localStorage.removeItem(TOKEN_STORAGE_KEY); + window.localStorage.removeItem(USER_STORAGE_KEY); + }, []); + + return ( + + {children} + + ); +} + +export function useAuth(): AuthContextValue { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +} diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts new file mode 100644 index 0000000..830fb59 --- /dev/null +++ b/apps/web/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs new file mode 100644 index 0000000..d5456a1 --- /dev/null +++ b/apps/web/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +export default nextConfig; diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..85fc11f --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,37 @@ +{ + "name": "@discoverly/web", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint \"app/**/*.{ts,tsx}\" \"components/**/*.{ts,tsx}\" \"lib/**/*.{ts,tsx}\" \"__tests__/**/*.{ts,tsx}\"", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@discoverly/shared": "*", + "next": "^15.0.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "@testing-library/jest-dom": "^6.5.0", + "@testing-library/react": "^16.0.1", + "@types/node": "^20.16.5", + "@types/react": "^18.3.12", + "@types/react-dom": "^18.3.1", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "@vitejs/plugin-react": "^4.3.1", + "eslint": "8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^4.6.2", + "jsdom": "^25.0.1", + "typescript": "^5.6.2", + "vitest": "^2.1.1" + } +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..02e4bc5 --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["dom", "dom.iterable", "ES2022"], + "allowJs": false, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts new file mode 100644 index 0000000..39c6536 --- /dev/null +++ b/apps/web/vitest.config.ts @@ -0,0 +1,11 @@ +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [react()], + test: { + environment: 'jsdom', + globals: true, + setupFiles: ['./vitest.setup.ts'], + }, +}); diff --git a/apps/web/vitest.setup.ts b/apps/web/vitest.setup.ts new file mode 100644 index 0000000..bb02c60 --- /dev/null +++ b/apps/web/vitest.setup.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom/vitest'; diff --git a/backend/.dockerignore b/backend/.dockerignore deleted file mode 100644 index 9c97bbd..0000000 --- a/backend/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -dist -.env diff --git a/backend/.env.example b/backend/.env.example deleted file mode 100644 index b627e46..0000000 --- a/backend/.env.example +++ /dev/null @@ -1,14 +0,0 @@ -NODE_ENV=development -PORT=5000 -MONGODB_URI=mongodb://localhost:27017/discoverly -# Optional separate DB for seeding experiments: -# MONGODB_URI=mongodb://localhost:27017/discoverly_seed -JWT_SECRET=replace_me -STELLAR_NETWORK=testnet -STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org -STELLAR_DESTINATION_ADDRESS= -AWS_REGION=us-east-1 -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_S3_BUCKET= -AWS_S3_PUBLIC_BASE_URL= diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs deleted file mode 100644 index a17d5de..0000000 --- a/backend/.eslintrc.cjs +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - root: true, - env: { - es2022: true, - node: true, - }, - parser: "@typescript-eslint/parser", - parserOptions: { - project: "./tsconfig.json", - sourceType: "module", - }, - plugins: ["@typescript-eslint"], - extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], - ignorePatterns: ["dist"], -} diff --git a/backend/.prettierrc b/backend/.prettierrc deleted file mode 100644 index f313bb2..0000000 --- a/backend/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "semi": false, - "singleQuote": false, - "trailingComma": "all", - "printWidth": 100 -} diff --git a/backend/Dockerfile b/backend/Dockerfile deleted file mode 100644 index 099b0b3..0000000 --- a/backend/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:20-alpine - -WORKDIR /app - -COPY package*.json ./ -RUN npm install - -COPY . . - -EXPOSE 5000 - -CMD ["npm", "run", "dev"] diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index 8b3b9a3..0000000 --- a/backend/README.md +++ /dev/null @@ -1,172 +0,0 @@ -# Discoverly Backend - -Active backend stack for contributors: - -- Node.js -- Express -- TypeScript -- MongoDB (Mongoose) - -## Quick Start - -1. Copy `.env.example` to `.env` -2. Install dependencies: - -```bash -npm install -``` - -3. Run dev server: - -```bash -npm run dev -``` - -## Seed Data - -Run the seed script to create: - -```bash -npm run seed -``` - -This creates: - -- 1 dummy restaurant -- 5 dummy food items - -## Validation Example - -`POST /api/ping` validates request bodies with Zod. - -Valid payload: - -```json -{ - "message": "hello", - "timestamp": "2026-03-07T10:00:00.000Z" -} -``` - -Invalid payloads return a structured `400` response with a `details` array. - -## Scope - -This folder is the target for Phase 1 and Phase 2 backend issues. -Legacy NestJS code is archived under `/legacy/nest-backend`. - -## Upload Endpoint - -`POST /api/upload` - -- Content-Type: `multipart/form-data` -- Field name: `file` -- Max file size: `5MB` -- Allowed image types: `jpeg`, `png`, `webp` -- Response: `{ ok: true, url: "https://..." }` - -Required environment variables: - -- `AWS_REGION` -- `AWS_ACCESS_KEY_ID` -- `AWS_SECRET_ACCESS_KEY` -- `AWS_S3_BUCKET` -- `AWS_S3_PUBLIC_BASE_URL` (optional) - -## Docker - -From repository root: - -```bash -docker-compose up --build -``` - -Health endpoint: - -```bash -curl http://localhost:5000/api/health -``` - -## Discovery Swipe Filtering - -`GET /api/foods/discover` supports filtering out previously swiped food items. - -Query params: - -- `longitude` (required) -- `latitude` (required) -- `cursor` (optional) -- `user_id` (optional; when present, excludes swiped food for that user) - -`UserSwipe` model fields: - -- `user_id` -- `food_id` -- `action` (`like` | `pass`) -- `timestamp` - -Index: - -- compound index on `{ user_id: 1, food_id: 1 }` -## Discovery Endpoint - -`GET /api/foods/discover` - -Query params: - -- `longitude` (number, required) -- `latitude` (number, required) -- `cursor` (string, optional) - -Behavior: - -- Uses MongoDB geospatial query with a 10km radius -- Sorts results by nearest restaurant first -- Returns up to 10 items per page -- Includes `distanceMeters` on each item -- Returns next `cursor` when additional items exist - -## Swipe Endpoint - -`POST /api/swipe` - -Payload: - -```json -{ - "foodId": "660000000000000000000100", - "action": "like" -} -``` - -Headers: - -- `x-user-id`: current user id (temporary until full JWT auth integration) - -Behavior: - -- Always writes to `UserSwipe` -- `pass`: no cart mutation -- `like`: also creates an `active` `CartItem` -- Returns `404` if `foodId` does not exist - -## Restaurant Menu CRUD - -Protected routes (JWT required): - -- `POST /api/restaurant/foods` -- `PUT /api/restaurant/foods/:id` -- `DELETE /api/restaurant/foods/:id` - -Authorization: - -- Allowed roles: `restaurant`, `admin` -- Non-admin restaurants can only edit/delete their own items -- Delete is soft delete (`is_active: false`) - -Create payload requires: - -- `name` -- `price` -- `description` -- `image_url` diff --git a/backend/scripts/seed.ts b/backend/scripts/seed.ts deleted file mode 100644 index 16d3ff5..0000000 --- a/backend/scripts/seed.ts +++ /dev/null @@ -1,82 +0,0 @@ -import mongoose from "mongoose" -import { connectDatabase } from "../src/config/database.js" -import { FoodItemModel, RestaurantModel } from "../src/models/index.js" - -async function seed() { - await connectDatabase() - - const restaurantName = "Discoverly Demo Kitchen" - const imageBase = "https://images.unsplash.com" - - await FoodItemModel.deleteMany({}) - await RestaurantModel.deleteMany({ name: restaurantName }) - - const restaurant = await RestaurantModel.create({ - owner_user_id: new mongoose.Types.ObjectId(), - name: restaurantName, - location: { - type: "Point", - coordinates: [-74.006, 40.7128], - }, - stellar_wallet: "GBDEMORESTAURANTWALLET", - is_active: true, - }) - - const foodItems = [ - { - restaurant_id: restaurant._id, - owner_user_id: restaurant.owner_user_id, - name: "Spicy Smash Burger", - description: "Double patty, chili aioli, pickles, toasted brioche.", - price: 13.99, - image_url: `${imageBase}/photo-1550547660-d9450f859349`, - }, - { - restaurant_id: restaurant._id, - owner_user_id: restaurant.owner_user_id, - name: "Truffle Fries", - description: "Hand-cut fries, truffle oil, parmesan, parsley.", - price: 7.5, - image_url: `${imageBase}/photo-1573080496219-bb080dd4f877`, - }, - { - restaurant_id: restaurant._id, - owner_user_id: restaurant.owner_user_id, - name: "Firecracker Pizza", - description: "Pepperoni, jalapeno, hot honey, mozzarella.", - price: 15.25, - image_url: `${imageBase}/photo-1513104890138-7c749659a591`, - }, - { - restaurant_id: restaurant._id, - owner_user_id: restaurant.owner_user_id, - name: "Mango Citrus Bowl", - description: "Fresh mango, avocado, greens, citrus vinaigrette.", - price: 11.0, - image_url: `${imageBase}/photo-1546069901-ba9599a7e63c`, - }, - { - restaurant_id: restaurant._id, - owner_user_id: restaurant.owner_user_id, - name: "Midnight Tiramisu", - description: "Espresso-soaked layers with mascarpone cream.", - price: 8.75, - image_url: `${imageBase}/photo-1571877227200-a0d98ea607e9`, - }, - ] - - await FoodItemModel.insertMany(foodItems) - - console.log("Seed complete:") - console.log(`- Restaurant: ${restaurant.name}`) - console.log(`- Food items: ${foodItems.length}`) -} - -seed() - .catch((error) => { - console.error("Seed failed:", error) - process.exitCode = 1 - }) - .finally(async () => { - await mongoose.disconnect() - }) diff --git a/backend/src/app.ts b/backend/src/app.ts deleted file mode 100644 index d0a9540..0000000 --- a/backend/src/app.ts +++ /dev/null @@ -1,38 +0,0 @@ -import cors from "cors" -import express from "express" -import rateLimit from "express-rate-limit" -import helmet from "helmet" -import morgan from "morgan" -import { apiRouter } from "./routes/index.js" -import { errorHandler } from "./middleware/error.middleware.js" -import { notFoundHandler } from "./middleware/not-found.middleware.js" - -export function createApp() { - const app = express() - - app.use(helmet()) - app.use(cors()) - app.use(morgan("dev")) - app.use(express.json()) - - app.use( - rateLimit({ - windowMs: 15 * 60 * 1000, - limit: 100, - standardHeaders: true, - legacyHeaders: false, - handler: (_req, res) => { - res.status(429).json({ - error: "Too Many Requests", - message: "Rate limit exceeded. Please try again later.", - }) - }, - }), - ) - - app.use("/api", apiRouter) - app.use(notFoundHandler) - app.use(errorHandler) - - return app -} diff --git a/backend/src/config/database.ts b/backend/src/config/database.ts deleted file mode 100644 index 9f78755..0000000 --- a/backend/src/config/database.ts +++ /dev/null @@ -1,19 +0,0 @@ -import mongoose from "mongoose" -import { env } from "./env.js" - -export async function connectDatabase(): Promise { - const maxAttempts = 10 - const retryDelayMs = 2000 - - for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { - try { - await mongoose.connect(env.MONGODB_URI) - return - } catch (error) { - if (attempt === maxAttempts) { - throw error - } - await new Promise((resolve) => setTimeout(resolve, retryDelayMs)) - } - } -} diff --git a/backend/src/config/env.ts b/backend/src/config/env.ts deleted file mode 100644 index 99b0bb3..0000000 --- a/backend/src/config/env.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { config } from "dotenv" -import { z } from "zod" - -config() - -const envSchema = z.object({ - NODE_ENV: z.enum(["development", "test", "production"]).default("development"), - PORT: z.coerce.number().int().positive().default(5000), - MONGODB_URI: z.string().min(1), - JWT_SECRET: z.string().min(1), - STELLAR_NETWORK: z.enum(["testnet", "public"]).default("testnet"), - STELLAR_HORIZON_URL: z.string().url().optional(), - STELLAR_DESTINATION_ADDRESS: z.string().optional(), - AWS_REGION: z.string().optional(), - AWS_ACCESS_KEY_ID: z.string().optional(), - AWS_SECRET_ACCESS_KEY: z.string().optional(), - AWS_S3_BUCKET: z.string().optional(), - AWS_S3_PUBLIC_BASE_URL: z.string().url().optional(), -}) - -const parsed = envSchema.safeParse(process.env) - -if (!parsed.success) { - console.error("Invalid backend environment variables:", parsed.error.flatten().fieldErrors) - process.exit(1) -} - -export const env = parsed.data diff --git a/backend/src/index.ts b/backend/src/index.ts deleted file mode 100644 index 19b0761..0000000 --- a/backend/src/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { createServer } from "node:http" -import { createApp } from "./app.js" -import { connectDatabase } from "./config/database.js" -import { env } from "./config/env.js" - -async function bootstrap() { - await connectDatabase() - const app = createApp() - const server = createServer(app) - - server.listen(env.PORT, () => { - console.log(`[backend] listening on port ${env.PORT}`) - }) -} - -bootstrap().catch((error) => { - console.error("[backend] failed to start", error) - process.exit(1) -}) diff --git a/backend/src/middleware/authenticate.middleware.ts b/backend/src/middleware/authenticate.middleware.ts deleted file mode 100644 index 0305533..0000000 --- a/backend/src/middleware/authenticate.middleware.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { NextFunction, Request, Response } from "express" -import jwt from "jsonwebtoken" -import { env } from "../config/env.js" -import type { JwtPayload } from "../types/auth.js" - -export function authenticate(req: Request, res: Response, next: NextFunction): void { - const authHeader = req.headers.authorization - const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null - - if (!token) { - res.status(401).json({ - error: "Unauthorized", - message: "Missing bearer token", - }) - return - } - - try { - const payload = jwt.verify(token, env.JWT_SECRET) as JwtPayload - req.user = { - id: payload.sub, - role: payload.role, - } - next() - } catch { - res.status(401).json({ - error: "Unauthorized", - message: "Invalid or expired token", - }) - } -} diff --git a/backend/src/middleware/error.middleware.ts b/backend/src/middleware/error.middleware.ts deleted file mode 100644 index 7d16f01..0000000 --- a/backend/src/middleware/error.middleware.ts +++ /dev/null @@ -1,26 +0,0 @@ -import type { NextFunction, Request, Response } from "express" -import multer from "multer" - -export function errorHandler(err: unknown, _req: Request, res: Response, next: NextFunction): void { - void next - if (err instanceof multer.MulterError && err.code === "LIMIT_FILE_SIZE") { - res.status(400).json({ - error: "Bad Request", - message: "File exceeds maximum size of 5MB", - }) - return - } - - if (err instanceof Error && err.message.startsWith("Unsupported file type.")) { - res.status(400).json({ - error: "Bad Request", - message: err.message, - }) - return - } - const message = err instanceof Error ? err.message : "Internal Server Error" - res.status(500).json({ - error: "Internal Server Error", - message, - }) -} diff --git a/backend/src/middleware/not-found.middleware.ts b/backend/src/middleware/not-found.middleware.ts deleted file mode 100644 index 67d2ae4..0000000 --- a/backend/src/middleware/not-found.middleware.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { Request, Response } from "express" - -export function notFoundHandler(req: Request, res: Response): void { - res.status(404).json({ - error: "Not Found", - path: req.path, - }) -} diff --git a/backend/src/middleware/require-role.middleware.ts b/backend/src/middleware/require-role.middleware.ts deleted file mode 100644 index 108131a..0000000 --- a/backend/src/middleware/require-role.middleware.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { NextFunction, Request, Response } from "express" -import type { UserRole } from "../models/user.model.js" - -export function requireRole(roles: UserRole[]) { - return (req: Request, res: Response, next: NextFunction): void => { - if (!req.user) { - res.status(401).json({ - error: "Unauthorized", - message: "Authentication required", - }) - return - } - - if (!roles.includes(req.user.role)) { - res.status(403).json({ - error: "Forbidden", - message: "Insufficient role permissions", - }) - return - } - - next() - } -} diff --git a/backend/src/middleware/validate-request.middleware.ts b/backend/src/middleware/validate-request.middleware.ts deleted file mode 100644 index 6403307..0000000 --- a/backend/src/middleware/validate-request.middleware.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { NextFunction, Request, RequestHandler, Response } from "express" -import { ZodError, type ZodTypeAny } from "zod" - -type RequestSchema = { - body?: ZodTypeAny - query?: ZodTypeAny - params?: ZodTypeAny -} - -export function validateRequest(schema: RequestSchema): RequestHandler { - return (req: Request, res: Response, next: NextFunction) => { - try { - if (schema.body) { - req.body = schema.body.parse(req.body) - } - - if (schema.query) { - req.query = schema.query.parse(req.query) - } - - if (schema.params) { - req.params = schema.params.parse(req.params) - } - next() - } catch (error) { - if (error instanceof ZodError) { - res.status(400).json({ - error: "Bad Request", - message: "Validation failed", - details: error.issues.map((issue) => ({ - path: issue.path.join("."), - message: issue.message, - code: issue.code, - })), - }) - return - } - next(error) - } - } -} diff --git a/backend/src/models/cart-item.model.ts b/backend/src/models/cart-item.model.ts deleted file mode 100644 index edbab2d..0000000 --- a/backend/src/models/cart-item.model.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { InferSchemaType, Schema, Types, model } from "mongoose" - -const cartItemSchema = new Schema( - { - user_id: { - type: Types.ObjectId, - required: true, - index: true, - }, - food_id: { - type: Types.ObjectId, - required: true, - index: true, - }, - quantity: { - type: Number, - default: 1, - min: 1, - }, - status: { - type: String, - enum: ["active", "removed", "ordered"], - default: "active", - index: true, - }, - }, - { - timestamps: true, - versionKey: false, - }, -) - -cartItemSchema.index( - { user_id: 1, food_id: 1, status: 1 }, - { unique: true, partialFilterExpression: { status: "active" } }, -) - -export type CartItemDocument = InferSchemaType -export const CartItemModel = model("CartItem", cartItemSchema) diff --git a/backend/src/models/food-item.model.ts b/backend/src/models/food-item.model.ts deleted file mode 100644 index a554aa3..0000000 --- a/backend/src/models/food-item.model.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { InferSchemaType, Schema, Types, model } from "mongoose" - -const foodItemSchema = new Schema( - { - restaurant_id: { - type: Types.ObjectId, - ref: "Restaurant", - required: true, - index: true, - }, - owner_user_id: { - type: Types.ObjectId, - ref: "User", - required: true, - index: true, - }, - name: { - type: String, - required: true, - trim: true, - }, - description: { - type: String, - required: true, - trim: true, - }, - price: { - type: Number, - required: true, - min: 0, - }, - image_url: { - type: String, - required: true, - trim: true, - }, - is_active: { - type: Boolean, - default: true, - index: true, - }, - }, - { - timestamps: true, - versionKey: false, - }, -) - -export type FoodItemDocument = InferSchemaType -export const FoodItemModel = model("FoodItem", foodItemSchema) diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts deleted file mode 100644 index 2851fcc..0000000 --- a/backend/src/models/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { FoodItemModel, type FoodItemDocument } from "./food-item.model.js" -export { UserSwipeModel, type UserSwipeDocument } from "./user-swipe.model.js" -export { CartItemModel, type CartItemDocument } from "./cart-item.model.js" -export { RestaurantModel, type RestaurantDocument } from "./restaurant.model.js" -export { UserModel, type UserDocument, type UserRole, userRoles } from "./user.model.js" diff --git a/backend/src/models/restaurant.model.ts b/backend/src/models/restaurant.model.ts deleted file mode 100644 index aa97bb4..0000000 --- a/backend/src/models/restaurant.model.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { InferSchemaType, Schema, Types, model } from "mongoose" - -const geoPointSchema = new Schema( - { - type: { - type: String, - enum: ["Point"], - default: "Point", - required: true, - }, - coordinates: { - type: [Number], - required: true, - validate: { - validator: (value: number[]) => value.length === 2, - message: "coordinates must be [longitude, latitude]", - }, - }, - }, - { _id: false }, -) - -const restaurantSchema = new Schema( - { - owner_user_id: { - type: Types.ObjectId, - required: true, - index: true, - }, - name: { - type: String, - required: true, - trim: true, - }, - location: { - type: geoPointSchema, - required: true, - }, - stellar_wallet: { - type: String, - default: null, - }, - is_active: { - type: Boolean, - default: true, - }, - }, - { - timestamps: true, - versionKey: false, - }, -) - -restaurantSchema.index({ location: "2dsphere" }) - -export type RestaurantDocument = InferSchemaType -export const RestaurantModel = model("Restaurant", restaurantSchema) diff --git a/backend/src/models/user-swipe.model.ts b/backend/src/models/user-swipe.model.ts deleted file mode 100644 index d37c195..0000000 --- a/backend/src/models/user-swipe.model.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { InferSchemaType, Schema, Types, model } from "mongoose" - -const userSwipeSchema = new Schema( - { - user_id: { - type: Types.ObjectId, - required: true, - index: true, - }, - food_id: { - type: Types.ObjectId, - required: true, - index: true, - }, - action: { - type: String, - enum: ["like", "pass"], - required: true, - }, - timestamp: { - type: Date, - default: Date.now, - required: true, - }, - }, - { - timestamps: true, - versionKey: false, - }, -) - -userSwipeSchema.index({ user_id: 1, food_id: 1 }, { unique: true }) - -export type UserSwipeDocument = InferSchemaType -export const UserSwipeModel = model("UserSwipe", userSwipeSchema) diff --git a/backend/src/models/user.model.ts b/backend/src/models/user.model.ts deleted file mode 100644 index 27c8e13..0000000 --- a/backend/src/models/user.model.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { InferSchemaType, Schema, model } from "mongoose" - -export const userRoles = ["user", "restaurant", "admin"] as const -export type UserRole = (typeof userRoles)[number] - -const userSchema = new Schema( - { - email: { - type: String, - required: true, - unique: true, - trim: true, - lowercase: true, - }, - password_hash: { - type: String, - required: true, - }, - stellar_wallet: { - type: String, - default: null, - }, - role: { - type: String, - enum: userRoles, - default: "user", - }, - }, - { - timestamps: true, - versionKey: false, - }, -) - -export type UserDocument = InferSchemaType -export const UserModel = model("User", userSchema) diff --git a/backend/src/routes/auth.routes.ts b/backend/src/routes/auth.routes.ts deleted file mode 100644 index fb23593..0000000 --- a/backend/src/routes/auth.routes.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { Router } from "express" -import bcrypt from "bcryptjs" -import jwt from "jsonwebtoken" -import { z } from "zod" -import { env } from "../config/env.js" -import { authenticate } from "../middleware/authenticate.middleware.js" -import { requireRole } from "../middleware/require-role.middleware.js" -import { UserModel, userRoles } from "../models/user.model.js" -import type { JwtPayload } from "../types/auth.js" - -const registerSchema = z.object({ - email: z.string().email(), - password: z.string().min(8, "password must be at least 8 characters"), - role: z.enum(userRoles).optional(), - stellar_wallet: z.string().optional(), -}) - -const loginSchema = z.object({ - email: z.string().email(), - password: z.string().min(1), -}) - -export const authRouter = Router() - -authRouter.post("/register", async (req, res, next) => { - try { - const body = registerSchema.parse(req.body) - const existing = await UserModel.findOne({ email: body.email }).lean() - if (existing) { - res.status(409).json({ - error: "Conflict", - message: "User already exists with this email", - }) - return - } - - const password_hash = await bcrypt.hash(body.password, 10) - const user = await UserModel.create({ - email: body.email, - password_hash, - role: body.role ?? "user", - stellar_wallet: body.stellar_wallet ?? null, - }) - - res.status(201).json({ - id: String(user._id), - email: user.email, - role: user.role, - }) - } catch (error) { - if (error instanceof z.ZodError) { - res.status(400).json({ - error: "Bad Request", - message: "Validation failed", - details: error.issues.map((issue) => ({ - path: issue.path.join("."), - message: issue.message, - code: issue.code, - })), - }) - return - } - next(error) - } -}) - -authRouter.post("/login", async (req, res, next) => { - try { - const body = loginSchema.parse(req.body) - const user = await UserModel.findOne({ email: body.email }) - - if (!user) { - res.status(401).json({ - error: "Unauthorized", - message: "Invalid credentials", - }) - return - } - - const passwordMatches = await bcrypt.compare(body.password, user.password_hash) - if (!passwordMatches) { - res.status(401).json({ - error: "Unauthorized", - message: "Invalid credentials", - }) - return - } - - const payload: JwtPayload = { - sub: String(user._id), - role: user.role, - } - - const token = jwt.sign(payload, env.JWT_SECRET, { expiresIn: "1h" }) - - res.status(200).json({ - token, - user: { - id: String(user._id), - email: user.email, - role: user.role, - }, - }) - } catch (error) { - if (error instanceof z.ZodError) { - res.status(400).json({ - error: "Bad Request", - message: "Validation failed", - details: error.issues.map((issue) => ({ - path: issue.path.join("."), - message: issue.message, - code: issue.code, - })), - }) - return - } - next(error) - } -}) - -authRouter.get("/me", authenticate, (req, res) => { - res.status(200).json({ - user: req.user, - }) -}) - -authRouter.get( - "/restaurant-zone", - authenticate, - requireRole(["restaurant", "admin"]), - (_req, res) => { - res.status(200).json({ - ok: true, - scope: "restaurant-zone", - }) - }, -) diff --git a/backend/src/routes/foods.routes.ts b/backend/src/routes/foods.routes.ts deleted file mode 100644 index ffc47c4..0000000 --- a/backend/src/routes/foods.routes.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { Router } from "express" -import { Types, type PipelineStage } from "mongoose" -import { z } from "zod" -import { RestaurantModel, UserSwipeModel } from "../models/index.js" - -const querySchema = z.object({ - longitude: z.coerce.number(), - latitude: z.coerce.number(), - cursor: z.string().optional(), - user_id: z - .string() - .refine((value) => Types.ObjectId.isValid(value), "user_id must be a valid ObjectId") - .optional(), -}) - -const PAGE_SIZE = 10 -const DISCOVERY_RADIUS_METERS = 10_000 - -type DiscoveryRow = { - food_id: unknown - restaurant_id: unknown - food_name: string - description: string - price: number - image_url: string - restaurant_name: string - distance_meters: number -} - -function decodeCursor(cursor?: string): number { - if (!cursor) { - return 0 - } - - try { - const raw = Buffer.from(cursor, "base64").toString("utf8") - const parsed = Number(raw) - if (!Number.isFinite(parsed) || parsed < 0) { - throw new Error("invalid-cursor") - } - return parsed - } catch { - throw new Error("invalid-cursor") - } -} - -function encodeCursor(offset: number): string { - return Buffer.from(String(offset), "utf8").toString("base64") -} - -export const foodsRouter = Router() - -foodsRouter.get("/discover", async (req, res, next) => { - try { - const query = querySchema.parse(req.query) - - let skip = 0 - try { - skip = decodeCursor(query.cursor) - } catch { - res.status(400).json({ - error: "Bad Request", - message: "Invalid cursor", - }) - return - } - - const swipedFoodIds = query.user_id - ? await UserSwipeModel.distinct("food_id", { user_id: new Types.ObjectId(query.user_id) }) - : [] - - const pipeline: PipelineStage[] = [ - { - $geoNear: { - near: { - type: "Point", - coordinates: [query.longitude, query.latitude], - }, - distanceField: "distance_meters", - spherical: true, - maxDistance: DISCOVERY_RADIUS_METERS, - query: { is_active: true }, - }, - }, - { - $lookup: { - from: "fooditems", - localField: "_id", - foreignField: "restaurant_id", - as: "foods", - }, - }, - { $unwind: "$foods" }, - { $match: { "foods.is_active": true } }, - ...(swipedFoodIds.length > 0 - ? ([{ $match: { "foods._id": { $nin: swipedFoodIds } } }] as PipelineStage[]) - : []), - { - $project: { - food_id: "$foods._id", - restaurant_id: "$_id", - food_name: "$foods.name", - description: "$foods.description", - price: "$foods.price", - image_url: "$foods.image_url", - restaurant_name: "$name", - distance_meters: 1, - }, - }, - { $sort: { distance_meters: 1, food_id: 1 } }, - { $skip: skip }, - { $limit: PAGE_SIZE + 1 }, - ] - - const rows = (await RestaurantModel.aggregate(pipeline)) as DiscoveryRow[] - const hasMore = rows.length > PAGE_SIZE - const items = rows.slice(0, PAGE_SIZE).map((row) => ({ - id: String(row.food_id), - restaurantId: String(row.restaurant_id), - name: row.food_name, - description: row.description, - price: row.price, - imageUrl: row.image_url, - restaurantName: row.restaurant_name, - distanceMeters: row.distance_meters, - })) - - res.status(200).json({ - items, - cursor: hasMore ? encodeCursor(skip + PAGE_SIZE) : null, - }) - } catch (error) { - if (error instanceof z.ZodError) { - res.status(400).json({ - error: "Bad Request", - message: "Invalid query parameters", - details: error.issues.map((issue) => ({ - path: issue.path.join("."), - message: issue.message, - code: issue.code, - })), - }) - return - } - next(error) - } -}) diff --git a/backend/src/routes/health.routes.ts b/backend/src/routes/health.routes.ts deleted file mode 100644 index 71b678d..0000000 --- a/backend/src/routes/health.routes.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Router } from "express" - -export const healthRouter = Router() - -healthRouter.get("/", (_req, res) => { - res.json({ ok: true }) -}) diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts deleted file mode 100644 index 9e7b7ff..0000000 --- a/backend/src/routes/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Router } from "express" -import { foodsRouter } from "./foods.routes.js" -import { authRouter } from "./auth.routes.js" -import { healthRouter } from "./health.routes.js" -import { uploadRouter } from "./upload.routes.js" -import { swipeRouter } from "./swipe.routes.js" -import { restaurantFoodsRouter } from "./restaurant-foods.routes.js" -import { restaurantAnalyticsRouter } from "./restaurant-analytics.routes.js" -import { pingRouter } from "./ping.routes.js" - -export const apiRouter = Router() - -apiRouter.use("/health", healthRouter) -apiRouter.use("/foods", foodsRouter) -apiRouter.use("/upload", uploadRouter) -apiRouter.use("/swipe", swipeRouter) -apiRouter.use("/restaurant/foods", restaurantFoodsRouter) -apiRouter.use("/restaurant/analytics", restaurantAnalyticsRouter) -apiRouter.use("/auth", authRouter) -apiRouter.use("/ping", pingRouter) diff --git a/backend/src/routes/ping.routes.ts b/backend/src/routes/ping.routes.ts deleted file mode 100644 index 6c1274d..0000000 --- a/backend/src/routes/ping.routes.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Router } from "express" -import { z } from "zod" -import { validateRequest } from "../middleware/validate-request.middleware.js" - -const pingBodySchema = z.object({ - message: z.string().min(1, "message is required"), - timestamp: z.string().datetime().optional(), -}) - -export const pingRouter = Router() - -pingRouter.post("/", validateRequest({ body: pingBodySchema }), (req, res) => { - res.status(200).json({ - ok: true, - echo: req.body.message, - }) -}) diff --git a/backend/src/routes/restaurant-analytics.routes.ts b/backend/src/routes/restaurant-analytics.routes.ts deleted file mode 100644 index 34fff1f..0000000 --- a/backend/src/routes/restaurant-analytics.routes.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { Router } from "express" -import { Types, type PipelineStage } from "mongoose" -import { authenticate } from "../middleware/authenticate.middleware.js" -import { requireRole } from "../middleware/require-role.middleware.js" -import { UserSwipeModel } from "../models/index.js" - -type AnalyticsRow = { - food_id: Types.ObjectId - food_name: string - total_views: number - likes: number - passes: number - conversion_rate: number -} - -export const restaurantAnalyticsRouter = Router() - -restaurantAnalyticsRouter.use(authenticate) -restaurantAnalyticsRouter.use(requireRole(["restaurant", "admin"])) - -restaurantAnalyticsRouter.get("/", async (req, res, next) => { - try { - const requesterId = req.user?.id - if (!requesterId || !Types.ObjectId.isValid(requesterId)) { - res.status(400).json({ - error: "Bad Request", - message: "Invalid authenticated user id", - }) - return - } - - const ownerUserId = new Types.ObjectId(requesterId) - - const pipeline: PipelineStage[] = [ - { - $lookup: { - from: "fooditems", - localField: "food_id", - foreignField: "_id", - as: "food", - }, - }, - { - $unwind: "$food", - }, - { - $match: { - "food.owner_user_id": ownerUserId, - }, - }, - { - $group: { - _id: "$food_id", - food_name: { $first: "$food.name" }, - total_views: { $sum: 1 }, - likes: { - $sum: { - $cond: [{ $eq: ["$action", "like"] }, 1, 0], - }, - }, - passes: { - $sum: { - $cond: [{ $eq: ["$action", "pass"] }, 1, 0], - }, - }, - }, - }, - { - $addFields: { - conversion_rate: { - $cond: [ - { $eq: ["$total_views", 0] }, - 0, - { - $round: [ - { - $multiply: [{ $divide: ["$likes", "$total_views"] }, 100], - }, - 2, - ], - }, - ], - }, - }, - }, - { - $project: { - _id: 0, - food_id: "$_id", - food_name: 1, - total_views: 1, - likes: 1, - passes: 1, - conversion_rate: 1, - }, - }, - { - $sort: { total_views: -1, food_name: 1 }, - }, - ] - - const rows = (await UserSwipeModel.aggregate(pipeline)) as AnalyticsRow[] - const items = rows.map((row) => ({ - foodId: String(row.food_id), - foodName: row.food_name, - totalViews: row.total_views, - likes: row.likes, - passes: row.passes, - conversionRate: row.conversion_rate, - })) - - res.status(200).json({ items }) - } catch (error) { - next(error) - } -}) diff --git a/backend/src/routes/restaurant-foods.routes.ts b/backend/src/routes/restaurant-foods.routes.ts deleted file mode 100644 index 4542601..0000000 --- a/backend/src/routes/restaurant-foods.routes.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Router } from "express" -import { Types } from "mongoose" -import { z } from "zod" -import { authenticate } from "../middleware/authenticate.middleware.js" -import { requireRole } from "../middleware/require-role.middleware.js" -import { validateRequest } from "../middleware/validate-request.middleware.js" -import { FoodItemModel, RestaurantModel } from "../models/index.js" - -const createFoodSchema = z.object({ - name: z.string().min(1), - price: z.number().positive(), - description: z.string().min(1), - image_url: z.string().url(), - restaurant_id: z.string().optional(), -}) - -const updateFoodSchema = z - .object({ - name: z.string().min(1).optional(), - price: z.number().positive().optional(), - description: z.string().min(1).optional(), - image_url: z.string().url().optional(), - }) - .refine((data) => Object.keys(data).length > 0, { - message: "At least one field is required", - }) - -const idParamSchema = z.object({ - id: z.string().min(1), -}) - -export const restaurantFoodsRouter = Router() - -restaurantFoodsRouter.use(authenticate) -restaurantFoodsRouter.use(requireRole(["restaurant", "admin"])) - -restaurantFoodsRouter.get("/", async (req, res, next) => { - try { - const isAdmin = req.user!.role === "admin" - const filter = isAdmin ? {} : { owner_user_id: req.user!.id } - - const foods = await FoodItemModel.find(filter).sort({ createdAt: -1 }).lean() - - res.status(200).json({ - items: foods.map((item) => ({ - id: String(item._id), - restaurant_id: String(item.restaurant_id), - name: item.name, - price: item.price, - description: item.description, - image_url: item.image_url, - is_active: item.is_active, - })), - }) - } catch (error) { - next(error) - } -}) - -restaurantFoodsRouter.post( - "/", - validateRequest({ body: createFoodSchema }), - async (req, res, next) => { - try { - const body = req.body as z.infer - const requesterId = req.user!.id - const isAdmin = req.user!.role === "admin" - - const restaurantFilter = - isAdmin && body.restaurant_id - ? { _id: body.restaurant_id, is_active: true } - : { owner_user_id: requesterId, is_active: true } - - const restaurant = await RestaurantModel.findOne(restaurantFilter) - if (!restaurant) { - res.status(404).json({ - error: "Not Found", - message: "Restaurant not found for current user", - }) - return - } - - const created = await FoodItemModel.create({ - restaurant_id: restaurant._id, - owner_user_id: restaurant.owner_user_id, - name: body.name, - price: body.price, - description: body.description, - image_url: body.image_url, - is_active: true, - }) - - res.status(201).json({ - id: String(created._id), - restaurant_id: String(created.restaurant_id), - name: created.name, - price: created.price, - description: created.description, - image_url: created.image_url, - is_active: created.is_active, - }) - } catch (error) { - next(error) - } - }, -) - -restaurantFoodsRouter.put( - "/:id", - validateRequest({ params: idParamSchema, body: updateFoodSchema }), - async (req, res, next) => { - try { - const { id } = req.params as z.infer - if (!Types.ObjectId.isValid(id)) { - res.status(404).json({ - error: "Not Found", - message: "Food item not found", - }) - return - } - - const existing = await FoodItemModel.findById(id) - if (!existing || !existing.is_active) { - res.status(404).json({ - error: "Not Found", - message: "Food item not found", - }) - return - } - - const isAdmin = req.user!.role === "admin" - const isOwner = String(existing.owner_user_id) === req.user!.id - if (!isAdmin && !isOwner) { - res.status(403).json({ - error: "Forbidden", - message: "Cannot modify another restaurant's menu item", - }) - return - } - - const update = req.body as z.infer - const updated = await FoodItemModel.findByIdAndUpdate(id, update, { new: true }) - if (!updated) { - res.status(404).json({ - error: "Not Found", - message: "Food item not found", - }) - return - } - - res.status(200).json({ - id: String(updated._id), - name: updated.name, - price: updated.price, - description: updated.description, - image_url: updated.image_url, - is_active: updated.is_active, - }) - } catch (error) { - next(error) - } - }, -) - -restaurantFoodsRouter.delete( - "/:id", - validateRequest({ params: idParamSchema }), - async (req, res, next) => { - try { - const { id } = req.params as z.infer - if (!Types.ObjectId.isValid(id)) { - res.status(404).json({ - error: "Not Found", - message: "Food item not found", - }) - return - } - - const existing = await FoodItemModel.findById(id) - if (!existing || !existing.is_active) { - res.status(404).json({ - error: "Not Found", - message: "Food item not found", - }) - return - } - - const isAdmin = req.user!.role === "admin" - const isOwner = String(existing.owner_user_id) === req.user!.id - if (!isAdmin && !isOwner) { - res.status(403).json({ - error: "Forbidden", - message: "Cannot modify another restaurant's menu item", - }) - return - } - - existing.is_active = false - await existing.save() - - res.status(200).json({ - ok: true, - id: String(existing._id), - is_active: existing.is_active, - }) - } catch (error) { - next(error) - } - }, -) diff --git a/backend/src/routes/swipe.routes.ts b/backend/src/routes/swipe.routes.ts deleted file mode 100644 index 98f844c..0000000 --- a/backend/src/routes/swipe.routes.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Router } from "express" -import { Types } from "mongoose" -import { z } from "zod" -import { validateRequest } from "../middleware/validate-request.middleware.js" -import { CartItemModel, FoodItemModel, UserSwipeModel } from "../models/index.js" - -const swipeSchema = z.object({ - foodId: z.string().min(1), - action: z.enum(["like", "pass"]), -}) - -export const swipeRouter = Router() - -swipeRouter.post("/", validateRequest({ body: swipeSchema }), async (req, res, next) => { - try { - const body = req.body as z.infer - const userIdRaw = req.user?.id ?? req.header("x-user-id") - if (!userIdRaw) { - res.status(401).json({ - error: "Unauthorized", - message: "Missing authenticated user id", - }) - return - } - - if (!Types.ObjectId.isValid(body.foodId)) { - res.status(400).json({ - error: "Bad Request", - message: "Invalid foodId", - }) - return - } - - if (!Types.ObjectId.isValid(userIdRaw)) { - res.status(400).json({ - error: "Bad Request", - message: "Invalid user id", - }) - return - } - - const food = await FoodItemModel.findById(body.foodId) - if (!food) { - res.status(404).json({ - error: "Not Found", - message: "Food item not found", - }) - return - } - - const userId = new Types.ObjectId(userIdRaw) - const foodId = new Types.ObjectId(body.foodId) - - const swipe = await UserSwipeModel.findOneAndUpdate( - { user_id: userId, food_id: foodId }, - { action: body.action, timestamp: new Date() }, - { - upsert: true, - new: true, - setDefaultsOnInsert: true, - }, - ) - - if (body.action === "like") { - await CartItemModel.findOneAndUpdate( - { user_id: userId, food_id: foodId, status: "active" }, - { $setOnInsert: { quantity: 1, status: "active" } }, - { - upsert: true, - new: true, - setDefaultsOnInsert: true, - }, - ) - } - - res.status(201).json({ - ok: true, - swipe: { - id: String(swipe._id), - foodId: body.foodId, - action: body.action, - }, - }) - } catch (error) { - next(error) - } -}) diff --git a/backend/src/routes/upload.routes.ts b/backend/src/routes/upload.routes.ts deleted file mode 100644 index b8471d2..0000000 --- a/backend/src/routes/upload.routes.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Router } from "express" -import multer from "multer" -import { uploadImageToS3 } from "../services/upload.service.js" - -const ALLOWED_IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/webp"]) -const INVALID_UPLOAD_TYPE_MESSAGE = "Unsupported file type. Allowed types: jpeg, png, webp." - -const upload = multer({ - storage: multer.memoryStorage(), - limits: { fileSize: 5 * 1024 * 1024 }, - fileFilter: (_req, file, callback) => { - if (ALLOWED_IMAGE_MIME_TYPES.has(file.mimetype)) { - callback(null, true) - return - } - callback(new Error(INVALID_UPLOAD_TYPE_MESSAGE)) - }, -}) - -export const uploadRouter = Router() - -uploadRouter.post("/", upload.single("file"), async (req, res, next) => { - try { - if (!req.file) { - res.status(400).json({ - error: "Bad Request", - message: "Missing image file in form-data field 'file'", - }) - return - } - - const { url } = await uploadImageToS3(req.file) - - res.status(201).json({ - ok: true, - url, - }) - } catch (error) { - next(error) - } -}) diff --git a/backend/src/services/upload.service.ts b/backend/src/services/upload.service.ts deleted file mode 100644 index 53ba459..0000000 --- a/backend/src/services/upload.service.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { randomUUID } from "node:crypto" -import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3" -import { env } from "../config/env.js" - -function ensureS3Config() { - if ( - !env.AWS_REGION || - !env.AWS_ACCESS_KEY_ID || - !env.AWS_SECRET_ACCESS_KEY || - !env.AWS_S3_BUCKET - ) { - throw new Error( - "S3 config missing. Set AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET.", - ) - } -} - -function getS3Client() { - ensureS3Config() - return new S3Client({ - region: env.AWS_REGION, - credentials: { - accessKeyId: env.AWS_ACCESS_KEY_ID!, - secretAccessKey: env.AWS_SECRET_ACCESS_KEY!, - }, - }) -} - -function getPublicUrl(key: string): string { - if (env.AWS_S3_PUBLIC_BASE_URL) { - return `${env.AWS_S3_PUBLIC_BASE_URL.replace(/\/$/, "")}/${key}` - } - return `https://${env.AWS_S3_BUCKET}.s3.${env.AWS_REGION}.amazonaws.com/${key}` -} - -export async function uploadImageToS3( - file: Express.Multer.File, -): Promise<{ url: string; key: string }> { - const extension = file.originalname.includes(".") ? file.originalname.split(".").pop() : "bin" - const key = `uploads/${new Date().toISOString().slice(0, 10)}/${randomUUID()}.${extension}` - - const client = getS3Client() - const command = new PutObjectCommand({ - Bucket: env.AWS_S3_BUCKET, - Key: key, - Body: file.buffer, - ContentType: file.mimetype, - }) - - await client.send(command) - - return { - url: getPublicUrl(key), - key, - } -} diff --git a/backend/src/types/auth.ts b/backend/src/types/auth.ts deleted file mode 100644 index 01b132d..0000000 --- a/backend/src/types/auth.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const userRoles = ["user", "restaurant", "admin"] as const -export type UserRole = (typeof userRoles)[number] - -export type JwtPayload = { - sub: string - role: UserRole -} - -export type AuthUser = { - id: string - role: UserRole -} diff --git a/backend/src/types/express.d.ts b/backend/src/types/express.d.ts deleted file mode 100644 index a46046a..0000000 --- a/backend/src/types/express.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { AuthUser } from "./auth.js" - -declare global { - namespace Express { - interface Request { - user?: AuthUser - } - } -} - -export {} diff --git a/backend/test/auth-guards.test.ts b/backend/test/auth-guards.test.ts deleted file mode 100644 index c3c13ac..0000000 --- a/backend/test/auth-guards.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import jwt from "jsonwebtoken" -import request from "supertest" -import { createApp } from "../src/app.js" -import { env } from "../src/config/env.js" - -test("GET /api/auth/me returns 401 without bearer token", async () => { - const app = createApp() - const response = await request(app).get("/api/auth/me") - - assert.equal(response.status, 401) - assert.equal(response.body.error, "Unauthorized") -}) - -test("GET /api/auth/restaurant-zone returns 403 for user role", async () => { - const app = createApp() - const token = jwt.sign({ sub: "u1", role: "user" }, env.JWT_SECRET) - const response = await request(app) - .get("/api/auth/restaurant-zone") - .set("Authorization", `Bearer ${token}`) - - assert.equal(response.status, 403) - assert.equal(response.body.error, "Forbidden") -}) - -test("GET /api/auth/restaurant-zone returns 200 for restaurant role", async () => { - const app = createApp() - const token = jwt.sign({ sub: "r1", role: "restaurant" }, env.JWT_SECRET) - const response = await request(app) - .get("/api/auth/restaurant-zone") - .set("Authorization", `Bearer ${token}`) - - assert.equal(response.status, 200) - assert.equal(response.body.ok, true) -}) diff --git a/backend/test/foods-discover-swipe-filter.test.ts b/backend/test/foods-discover-swipe-filter.test.ts deleted file mode 100644 index b9f322d..0000000 --- a/backend/test/foods-discover-swipe-filter.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import request from "supertest" -import { RestaurantModel, UserSwipeModel } from "../src/models/index.js" -import { createApp } from "../src/app.js" - -test("GET /api/foods/discover excludes swiped food ids when user_id is provided", async () => { - const app = createApp() - - const originalDistinct = UserSwipeModel.distinct - const originalAggregate = RestaurantModel.aggregate - - let capturedPipeline: Record[] = [] - - ;(UserSwipeModel.distinct as unknown as (...args: unknown[]) => Promise) = async () => [ - "660000000000000000000500", - ] - ;(RestaurantModel.aggregate as unknown as (...args: unknown[]) => Promise) = async ( - pipeline: Record[], - ) => { - capturedPipeline = pipeline - return [] - } - - const response = await request(app).get( - "/api/foods/discover?longitude=-73.99&latitude=40.73&user_id=660000000000000000000001", - ) - - assert.equal(response.status, 200) - assert.ok( - capturedPipeline.some((stage) => { - const match = stage.$match as Record | undefined - const foods = match?.["foods._id"] as { $nin?: unknown[] } | undefined - return Array.isArray(foods?.$nin) - }), - ) - - UserSwipeModel.distinct = originalDistinct - RestaurantModel.aggregate = originalAggregate -}) - -test("GET /api/foods/discover does not apply swipe exclusion without user_id", async () => { - const app = createApp() - - const originalDistinct = UserSwipeModel.distinct - const originalAggregate = RestaurantModel.aggregate - - let distinctCalls = 0 - let capturedPipeline: Record[] = [] - - ;(UserSwipeModel.distinct as unknown as (...args: unknown[]) => Promise) = async () => { - distinctCalls += 1 - return [] - } - ;(RestaurantModel.aggregate as unknown as (...args: unknown[]) => Promise) = async ( - pipeline: Record[], - ) => { - capturedPipeline = pipeline - return [] - } - - const response = await request(app).get("/api/foods/discover?longitude=-73.99&latitude=40.73") - - assert.equal(response.status, 200) - assert.equal(distinctCalls, 0) - assert.equal( - capturedPipeline.some((stage) => { - const match = stage.$match as Record | undefined - return Boolean(match?.["foods._id"]) - }), - false, - ) - - UserSwipeModel.distinct = originalDistinct - RestaurantModel.aggregate = originalAggregate -}) - -test("GET /api/foods/discover returns 400 when user_id is not a valid ObjectId", async () => { - const app = createApp() - - const response = await request(app).get( - "/api/foods/discover?longitude=-73.99&latitude=40.73&user_id=invalid-id", - ) - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") - assert.equal(response.body.message, "Invalid query parameters") -}) diff --git a/backend/test/foods-discover.test.ts b/backend/test/foods-discover.test.ts deleted file mode 100644 index 9ec9bad..0000000 --- a/backend/test/foods-discover.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import request from "supertest" -import { RestaurantModel } from "../src/models/index.js" -import { createApp } from "../src/app.js" - -test("GET /api/foods/discover returns 400 when coordinates are missing", async () => { - const app = createApp() - - const response = await request(app).get("/api/foods/discover") - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") -}) - -test("GET /api/foods/discover returns paginated data with distance", async () => { - const app = createApp() - const originalAggregate = RestaurantModel.aggregate - let capturedPipeline: Record[] = [] - - ;(RestaurantModel.aggregate as unknown as (...args: unknown[]) => Promise) = async ( - pipeline: Record[], - ) => { - capturedPipeline = pipeline - return [ - { - food_id: "660000000000000000000101", - restaurant_id: "660000000000000000000201", - food_name: "Spicy Bowl", - description: "House special", - price: 12.5, - image_url: "https://example.com/a.png", - restaurant_name: "Demo Restaurant", - distance_meters: 128.4, - }, - ] - } - - const response = await request(app).get("/api/foods/discover?longitude=-73.99&latitude=40.73") - - assert.equal(response.status, 200) - assert.equal(response.body.items.length, 1) - assert.equal(response.body.items[0].name, "Spicy Bowl") - assert.equal(response.body.items[0].distanceMeters, 128.4) - assert.equal(response.body.cursor, null) - assert.ok( - capturedPipeline.some((stage) => { - const sort = stage.$sort as Record | undefined - return sort?.distance_meters === 1 - }), - ) - - RestaurantModel.aggregate = originalAggregate -}) - -test("GET /api/foods/discover returns next cursor when more than one page", async () => { - const app = createApp() - const originalAggregate = RestaurantModel.aggregate - - ;(RestaurantModel.aggregate as unknown as (...args: unknown[]) => Promise) = async () => - Array.from({ length: 11 }, (_, index) => ({ - food_id: `660000000000000000000${index + 100}`, - restaurant_id: "660000000000000000000200", - food_name: `Dish ${index + 1}`, - description: "desc", - price: 10 + index, - image_url: "https://example.com/x.png", - restaurant_name: "Demo Restaurant", - distance_meters: 25 + index, - })) - - const response = await request(app).get("/api/foods/discover?longitude=-73.99&latitude=40.73") - - assert.equal(response.status, 200) - assert.equal(response.body.items.length, 10) - assert.ok(typeof response.body.cursor === "string") - - RestaurantModel.aggregate = originalAggregate -}) - -test("GET /api/foods/discover returns 400 for invalid cursor", async () => { - const app = createApp() - - const response = await request(app).get( - "/api/foods/discover?longitude=-73.99&latitude=40.73&cursor=not-base64", - ) - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") - assert.equal(response.body.message, "Invalid cursor") -}) diff --git a/backend/test/health.test.ts b/backend/test/health.test.ts deleted file mode 100644 index 34ddc61..0000000 --- a/backend/test/health.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import test from "node:test" -import assert from "node:assert/strict" -import request from "supertest" -import { createApp } from "../src/app.js" - -test("GET /api/health returns ok", async () => { - const app = createApp() - const response = await request(app).get("/api/health") - - assert.equal(response.status, 200) - assert.deepEqual(response.body, { ok: true }) -}) diff --git a/backend/test/ping.test.ts b/backend/test/ping.test.ts deleted file mode 100644 index 8ce5cfd..0000000 --- a/backend/test/ping.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import test from "node:test" -import assert from "node:assert/strict" -import request from "supertest" -import { createApp } from "../src/app.js" - -test("POST /api/ping accepts valid payload", async () => { - const app = createApp() - const response = await request(app).post("/api/ping").send({ message: "hello" }) - - assert.equal(response.status, 200) - assert.deepEqual(response.body, { ok: true, echo: "hello" }) -}) - -test("POST /api/ping rejects invalid payload with structured 400", async () => { - const app = createApp() - const response = await request(app).post("/api/ping").send({}) - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") - assert.equal(response.body.message, "Validation failed") - assert.ok(Array.isArray(response.body.details)) - assert.equal(response.body.details[0].path, "message") -}) diff --git a/backend/test/restaurant-analytics.test.ts b/backend/test/restaurant-analytics.test.ts deleted file mode 100644 index a13f21a..0000000 --- a/backend/test/restaurant-analytics.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import jwt from "jsonwebtoken" -import request from "supertest" -import { createApp } from "../src/app.js" -import { env } from "../src/config/env.js" -import { UserSwipeModel } from "../src/models/index.js" - -test("GET /api/restaurant/analytics returns grouped metrics for authenticated restaurant owner", async () => { - const app = createApp() - const token = jwt.sign({ sub: "660000000000000000000001", role: "restaurant" }, env.JWT_SECRET) - const originalAggregate = UserSwipeModel.aggregate - - ;(UserSwipeModel.aggregate as unknown as (...args: unknown[]) => Promise) = async () => [ - { - food_id: "660000000000000000000101", - food_name: "Spicy Bowl", - total_views: 25, - likes: 10, - passes: 15, - conversion_rate: 40, - }, - { - food_id: "660000000000000000000102", - food_name: "Cheese Burger", - total_views: 10, - likes: 3, - passes: 7, - conversion_rate: 30, - }, - ] - - const response = await request(app) - .get("/api/restaurant/analytics") - .set("Authorization", `Bearer ${token}`) - - assert.equal(response.status, 200) - assert.equal(response.body.items.length, 2) - assert.equal(response.body.items[0].foodName, "Spicy Bowl") - assert.equal(response.body.items[0].totalViews, 25) - assert.equal(response.body.items[0].likes, 10) - assert.equal(response.body.items[0].passes, 15) - assert.equal(response.body.items[0].conversionRate, 40) - - UserSwipeModel.aggregate = originalAggregate -}) - -test("GET /api/restaurant/analytics returns 401 without token", async () => { - const app = createApp() - const response = await request(app).get("/api/restaurant/analytics") - - assert.equal(response.status, 401) - assert.equal(response.body.error, "Unauthorized") -}) - -test("GET /api/restaurant/analytics returns 403 for non-restaurant role", async () => { - const app = createApp() - const token = jwt.sign({ sub: "660000000000000000000001", role: "user" }, env.JWT_SECRET) - - const response = await request(app) - .get("/api/restaurant/analytics") - .set("Authorization", `Bearer ${token}`) - - assert.equal(response.status, 403) - assert.equal(response.body.error, "Forbidden") -}) diff --git a/backend/test/restaurant-foods.test.ts b/backend/test/restaurant-foods.test.ts deleted file mode 100644 index 4789393..0000000 --- a/backend/test/restaurant-foods.test.ts +++ /dev/null @@ -1,133 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import jwt from "jsonwebtoken" -import request from "supertest" -import { createApp } from "../src/app.js" -import { env } from "../src/config/env.js" -import { FoodItemModel, RestaurantModel } from "../src/models/index.js" - -test("GET /api/restaurant/foods returns owner-scoped menu items", async () => { - const app = createApp() - const ownerId = "660000000000000000000001" - const token = jwt.sign({ sub: ownerId, role: "restaurant" }, env.JWT_SECRET) - const originalFind = FoodItemModel.find - - ;(FoodItemModel.find as unknown as (...args: unknown[]) => { - sort: (...args: unknown[]) => { lean: () => Promise } - }) = () => ({ - sort: () => ({ - lean: async () => [ - { - _id: "660000000000000000000111", - restaurant_id: "660000000000000000000010", - owner_user_id: ownerId, - name: "Nori Ramen", - description: "Creamy broth", - price: 14, - image_url: "https://example.com/ramen.png", - is_active: true, - }, - ], - }), - }) - - const response = await request(app) - .get("/api/restaurant/foods") - .set("Authorization", `Bearer ${token}`) - - assert.equal(response.status, 200) - assert.equal(response.body.items.length, 1) - assert.equal(response.body.items[0].name, "Nori Ramen") - assert.equal(response.body.items[0].is_active, true) - - FoodItemModel.find = originalFind -}) - -test("PUT /api/restaurant/foods/:id returns 403 when non-owner restaurant tries update", async () => { - const app = createApp() - const token = jwt.sign({ sub: "660000000000000000000001", role: "restaurant" }, env.JWT_SECRET) - - const originalFindById = FoodItemModel.findById - const originalFindByIdAndUpdate = FoodItemModel.findByIdAndUpdate - - ;(FoodItemModel.findById as unknown as (...args: unknown[]) => Promise) = async () => ({ - _id: "660000000000000000000099", - owner_user_id: "660000000000000000000777", - is_active: true, - }) - ;(FoodItemModel.findByIdAndUpdate as unknown as (...args: unknown[]) => Promise) = - async () => null - - const response = await request(app) - .put("/api/restaurant/foods/660000000000000000000099") - .set("Authorization", `Bearer ${token}`) - .send({ name: "Updated Name" }) - - assert.equal(response.status, 403) - assert.equal(response.body.error, "Forbidden") - - FoodItemModel.findById = originalFindById - FoodItemModel.findByIdAndUpdate = originalFindByIdAndUpdate -}) - -test("DELETE /api/restaurant/foods/:id returns 404 when food does not exist", async () => { - const app = createApp() - const token = jwt.sign({ sub: "660000000000000000000001", role: "restaurant" }, env.JWT_SECRET) - const originalFindById = FoodItemModel.findById - - ;(FoodItemModel.findById as unknown as (...args: unknown[]) => Promise) = async () => - null - - const response = await request(app) - .delete("/api/restaurant/foods/660000000000000000000099") - .set("Authorization", `Bearer ${token}`) - - assert.equal(response.status, 404) - assert.equal(response.body.error, "Not Found") - - FoodItemModel.findById = originalFindById -}) - -test("POST /api/restaurant/foods creates item for authenticated restaurant owner", async () => { - const app = createApp() - const ownerId = "660000000000000000000001" - const token = jwt.sign({ sub: ownerId, role: "restaurant" }, env.JWT_SECRET) - - const originalFindOne = RestaurantModel.findOne - const originalCreate = FoodItemModel.create - - ;(RestaurantModel.findOne as unknown as (...args: unknown[]) => Promise) = async () => ({ - _id: "660000000000000000000010", - owner_user_id: ownerId, - is_active: true, - }) - ;(FoodItemModel.create as unknown as (...args: unknown[]) => Promise) = async (payload: { - restaurant_id: string - owner_user_id: string - name: string - price: number - description: string - image_url: string - is_active: boolean - }) => ({ - _id: "660000000000000000000099", - ...payload, - }) - - const response = await request(app) - .post("/api/restaurant/foods") - .set("Authorization", `Bearer ${token}`) - .send({ - name: "Spicy Bowl", - price: 14.5, - description: "Chili crunch bowl", - image_url: "https://example.com/food.png", - }) - - assert.equal(response.status, 201) - assert.equal(response.body.name, "Spicy Bowl") - assert.equal(response.body.is_active, true) - - RestaurantModel.findOne = originalFindOne - FoodItemModel.create = originalCreate -}) diff --git a/backend/test/swipe.test.ts b/backend/test/swipe.test.ts deleted file mode 100644 index 8e02d34..0000000 --- a/backend/test/swipe.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import request from "supertest" -import { CartItemModel, FoodItemModel, UserSwipeModel } from "../src/models/index.js" -import { createApp } from "../src/app.js" - -test("POST /api/swipe with action=pass stores swipe and does not create cart item", async () => { - const app = createApp() - - const originalFindById = FoodItemModel.findById - const originalSwipeUpsert = UserSwipeModel.findOneAndUpdate - const originalCartUpsert = CartItemModel.findOneAndUpdate - - let cartUpsertCalls = 0 - - ;(FoodItemModel.findById as unknown as (...args: unknown[]) => Promise) = async () => ({ - _id: "660000000000000000000100", - }) - ;(UserSwipeModel.findOneAndUpdate as unknown as (...args: unknown[]) => Promise) = - async () => ({ - _id: "770000000000000000000001", - }) - ;(CartItemModel.findOneAndUpdate as unknown as (...args: unknown[]) => Promise) = - async () => { - cartUpsertCalls += 1 - return {} - } - - const response = await request(app) - .post("/api/swipe") - .set("x-user-id", "660000000000000000000001") - .send({ foodId: "660000000000000000000100", action: "pass" }) - - assert.equal(response.status, 201) - assert.equal(response.body.swipe.action, "pass") - assert.equal(cartUpsertCalls, 0) - - FoodItemModel.findById = originalFindById - UserSwipeModel.findOneAndUpdate = originalSwipeUpsert - CartItemModel.findOneAndUpdate = originalCartUpsert -}) - -test("POST /api/swipe with action=like stores swipe and creates cart item", async () => { - const app = createApp() - - const originalFindById = FoodItemModel.findById - const originalSwipeUpsert = UserSwipeModel.findOneAndUpdate - const originalCartUpsert = CartItemModel.findOneAndUpdate - - let cartUpsertCalls = 0 - - ;(FoodItemModel.findById as unknown as (...args: unknown[]) => Promise) = async () => ({ - _id: "660000000000000000000100", - }) - ;(UserSwipeModel.findOneAndUpdate as unknown as (...args: unknown[]) => Promise) = - async () => ({ - _id: "770000000000000000000001", - }) - ;(CartItemModel.findOneAndUpdate as unknown as (...args: unknown[]) => Promise) = - async () => { - cartUpsertCalls += 1 - return {} - } - - const response = await request(app) - .post("/api/swipe") - .set("x-user-id", "660000000000000000000001") - .send({ foodId: "660000000000000000000100", action: "like" }) - - assert.equal(response.status, 201) - assert.equal(response.body.swipe.action, "like") - assert.equal(cartUpsertCalls, 1) - - FoodItemModel.findById = originalFindById - UserSwipeModel.findOneAndUpdate = originalSwipeUpsert - CartItemModel.findOneAndUpdate = originalCartUpsert -}) - -test("POST /api/swipe returns 404 when foodId does not exist", async () => { - const app = createApp() - const originalFindById = FoodItemModel.findById - - ;(FoodItemModel.findById as unknown as (...args: unknown[]) => Promise) = async () => - null - - const response = await request(app) - .post("/api/swipe") - .set("x-user-id", "660000000000000000000001") - .send({ foodId: "660000000000000000000100", action: "like" }) - - assert.equal(response.status, 404) - assert.equal(response.body.error, "Not Found") - - FoodItemModel.findById = originalFindById -}) diff --git a/backend/test/upload.test.ts b/backend/test/upload.test.ts deleted file mode 100644 index a19d96f..0000000 --- a/backend/test/upload.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import assert from "node:assert/strict" -import test from "node:test" -import request from "supertest" -import { createApp } from "../src/app.js" - -test("POST /api/upload returns 400 when file is missing", async () => { - const app = createApp() - - const response = await request(app).post("/api/upload") - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") - assert.equal(response.body.message, "Missing image file in form-data field 'file'") -}) - -test("POST /api/upload returns 400 for unsupported file type", async () => { - const app = createApp() - - const response = await request(app).post("/api/upload").attach("file", Buffer.from("hello"), { - filename: "note.txt", - contentType: "text/plain", - }) - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") - assert.equal(response.body.message, "Unsupported file type. Allowed types: jpeg, png, webp.") -}) - -test("POST /api/upload returns 400 when file exceeds 5MB", async () => { - const app = createApp() - const bigBuffer = Buffer.alloc(5 * 1024 * 1024 + 1, 1) - - const response = await request(app).post("/api/upload").attach("file", bigBuffer, { - filename: "too-large.png", - contentType: "image/png", - }) - - assert.equal(response.status, 400) - assert.equal(response.body.error, "Bad Request") - assert.equal(response.body.message, "File exceeds maximum size of 5MB") -}) diff --git a/backend/tsconfig.json b/backend/tsconfig.json deleted file mode 100644 index 4dcdf88..0000000 --- a/backend/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "strict": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, - "outDir": "dist", - "rootDir": ".", - "types": [ - "node" - ] - }, - "include": [ - "src/**/*.ts", - "scripts/**/*.ts" - ] -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index d841f7a..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - mongo: - image: mongo:7 - container_name: discoverly-mongo - ports: - - "27017:27017" - healthcheck: - test: ["CMD", "mongosh", "--eval", "db.runCommand({ ping: 1 })", "--quiet"] - interval: 5s - timeout: 5s - retries: 12 - volumes: - - mongo_data:/data/db - - backend: - build: - context: ./backend - container_name: discoverly-backend - environment: - NODE_ENV: development - PORT: 5000 - MONGODB_URI: mongodb://mongo:27017/discoverly - JWT_SECRET: replace_me - STELLAR_NETWORK: testnet - ports: - - "5000:5000" - depends_on: - mongo: - condition: service_healthy - -volumes: - mongo_data: diff --git a/docs/adr/0001-stack-and-repo-structure.md b/docs/adr/0001-stack-and-repo-structure.md deleted file mode 100644 index 8ee5ec0..0000000 --- a/docs/adr/0001-stack-and-repo-structure.md +++ /dev/null @@ -1,34 +0,0 @@ -# ADR 0001: Discoverly Stack And Repository Structure - -## Status -Accepted - -## Decision -Discoverly uses: - -- `mobile/`: Expo + React Native + TypeScript -- `backend/`: Node.js + Express + TypeScript + MongoDB (Mongoose) -- `web/`: reserved for future restaurant dashboard work - -Repository shape: - -```text -discoverly/ - backend/ - mobile/ - web/ - docs/ - legacy/ -``` - -The previous NestJS backend is archived in `legacy/nest-backend/` and is not the active implementation target. - -## Rationale -- Phase 1 and Phase 2 issues define MongoDB geospatial and aggregation requirements. -- A clear monorepo split removes ambiguity for contributors and CI. -- Archiving legacy code preserves historical context without blocking new work. - -## Consequences -- All new backend issues target `backend/` only. -- New mobile issues target `mobile/` only. -- Contributors should not add features to `legacy/nest-backend/`. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..752b261 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,100 @@ +# Architecture + +## Monorepo layout + +``` +apps/ + api/ Express + TypeScript backend (modular monolith) + web/ Next.js + TypeScript web app + mobile/ Expo + React Native + TypeScript app + +packages/ + shared/ Cross-app types and validation schemas + stellar/ Scaffold for the future Stellar payment integration +``` + +`apps/*` are deployable applications. `packages/*` are libraries consumed +by one or more apps via npm workspaces (`@discoverly/shared`, +`@discoverly/stellar`). + +## Backend: modular monolith + +`apps/api` is a single deployable Express service, organized around +business modules rather than technical layers: + +``` +apps/api/src/ + modules/ + auth/ # registration, login, JWT issuance, auth middleware + controllers/ + services/ + repositories/ + validators/ + routes/ + middleware/ + types/ + tests/ + users/ # user persistence, supports auth + repositories/ + services/ + types/ + shared/ + config/ # environment validation (zod) + database/ # MongoDB connection + errors/ # AppError + logger/ # JSON logger + middleware/ # error handler, 404 handler + types/ # shared Express types + app.ts # express app wiring + server.ts # process entrypoint +``` + +**Module ownership**: each module owns its controllers, services, +repositories, validators, routes, types, and tests. Business logic lives +inside modules, never in `shared/`. `shared/` contains only cross-cutting +infrastructure (config, database, logging, error handling). + +This structure is intentionally one codebase, one deployment artifact, +one runtime, and one database (MongoDB). There are no microservices, +message brokers, or separate deployable services. Future business +modules (e.g. `restaurants`, `foods`, `orders`, `payments`) can be added +as new folders under `modules/` following the same pattern, without +restructuring existing code. + +## Shared package (`@discoverly/shared`) + +Holds only genuinely shared concerns used by both `apps/api` and +`apps/web`: + +- `types/` — shared TypeScript types (e.g. `AuthUser`, `AuthResponse`) +- `validation/` — zod schemas for request/form validation (e.g. + `registerSchema`, `loginSchema`) + +This keeps validation rules consistent between the API and the web +client without duplicating logic. + +## Stellar package (`@discoverly/stellar`) + +A scaffold only — placeholder types and interfaces for the future Stellar +payment integration. It compiles successfully but contains no blockchain +functionality. When Stellar integration work begins, it should live here +to keep blockchain concerns isolated from core application logic. + +## Web app + +`apps/web` is a Next.js (App Router) application. Currently it implements +only authentication: + +- `/login` and `/register` pages +- `lib/auth-context.tsx` — client-side auth state (user, token), backed + by `localStorage` +- `lib/api-client.ts` — typed client for the auth API +- `components/` — reusable form components + +## Mobile app + +`apps/mobile` is an Expo + React Native + TypeScript project containing +only the foundation: folder structure (`components`, `screens`, +`navigation`, `hooks`, `services`, `utils`, `assets`), linting, +formatting, and testing setup. No screens or authentication are +implemented yet. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..75b90c7 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,56 @@ +# Contributor Guide + +## Project structure + +This is an npm workspaces monorepo: + +- `apps/api` — Express + TypeScript backend (modular monolith) +- `apps/web` — Next.js + TypeScript web app +- `apps/mobile` — Expo + React Native + TypeScript app +- `packages/shared` — shared types and validation schemas +- `packages/stellar` — scaffold for future Stellar integration + +See [architecture.md](architecture.md) for details on how the backend is +organized into modules. + +## Coding standards + +- **TypeScript strict mode** is enabled across all packages — avoid `any` + and unnecessary type assertions. +- **ESLint** and **Prettier** are configured per package; run + `npm run lint -w ` and `npm run format` before committing. +- **Naming**: files use kebab-case (e.g. `auth.service.ts`, + `auth.controller.ts`); React components use PascalCase. +- **Validation**: request/form validation schemas that are needed by both + the API and the web app belong in `@discoverly/shared`. API-only or + web-only validation stays local to that app. +- **Module boundaries** (backend): business logic belongs inside + `apps/api/src/modules/`. Cross-cutting infrastructure + (config, database, logging, error handling) belongs in + `apps/api/src/shared`. Do not put business logic in `shared/`. + +## Development workflow + +1. Install dependencies: `npm install` +2. Build `@discoverly/shared`: `npm run build -w @discoverly/shared` +3. Run the app(s) you're working on (see root [README](../README.md)) +4. Write or update tests alongside your change +5. Run `npm run lint` and `npm run test` for the affected workspace(s) +6. Open a pull request — CI runs lint, test, and build for each affected + package + +## Adding a new backend module + +When adding new business functionality to `apps/api`, create a new folder +under `src/modules//` following the same shape as `modules/auth` +(`controllers/`, `services/`, `repositories/`, `validators/`, `routes/`, +`types/`, `tests/`), and mount its router in `src/app.ts`. Avoid adding +business logic to `src/shared/`. + +## Contribution expectations + +- Keep changes scoped — avoid mixing unrelated changes in one PR. +- Add tests for new behavior; CI will fail on lint, test, or build + errors. +- Update relevant package README/docs when you change setup or + developer-facing behavior. diff --git a/docs/environment.md b/docs/environment.md new file mode 100644 index 0000000..3ede657 --- /dev/null +++ b/docs/environment.md @@ -0,0 +1,39 @@ +# Environment Setup + +Each app has its own `.env.example`. Copy it to `.env` in the same +directory and fill in values for local development. + +## `apps/api/.env` + +| Variable | Description | Default (example) | +| ----------------- | --------------------------------------------- | ------------------------------------------- | +| `NODE_ENV` | `development`, `test`, or `production` | `development` | +| `PORT` | Port the API listens on | `5000` | +| `MONGODB_URI` | MongoDB connection string | `mongodb://localhost:27017/discoverly` | +| `JWT_SECRET` | Secret used to sign auth tokens | `replace_me` | +| `JWT_EXPIRES_IN` | Auth token expiry (jsonwebtoken duration format) | `7d` | + +Tests do **not** read these values from `.env` — they run against an +in-memory MongoDB instance with fixed test values, so the test suite +works immediately after `npm install`. + +## `apps/web/.env` + +| Variable | Description | Default (example) | +| ---------------------- | ---------------------------------- | -------------------------- | +| `NEXT_PUBLIC_API_URL` | Base URL of `apps/api` | `http://localhost:5000` | + +## `apps/mobile/.env` + +| Variable | Description | Default (example) | +| ---------------------- | ---------------------------------- | -------------------------- | +| `EXPO_PUBLIC_API_URL` | Base URL of `apps/api` | `http://localhost:5000` | + +## Secrets handling + +- Never commit `.env` files — they are git-ignored. +- `JWT_SECRET` must be a strong, unique value outside of local + development; treat it as a secret in any deployed environment. +- CI does not require real secrets: the API test suite uses an in-memory + database and a fixed test JWT secret defined in + `apps/api/vitest.config.ts`. diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..d4a4dd7 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,59 @@ +# Testing Guide + +## Running tests + +```bash +# all workspaces +npm run test --workspaces --if-present + +# a single workspace +npm run test -w @discoverly/api +npm run test -w @discoverly/web +npm run test -w @discoverly/mobile +``` + +All test suites run immediately after `npm install` — no external +services (databases, etc.) are required. + +## Test structure + +### `apps/api` + +- Framework: [Vitest](https://vitest.dev/) + [Supertest](https://github.com/ladjs/supertest) +- Database: [`mongodb-memory-server`](https://github.com/typegoose/mongodb-memory-server) + spins up an in-memory MongoDB instance for the test run — no local + MongoDB needed. +- Tests live alongside the module they cover, e.g. + `src/modules/auth/tests/auth.test.ts`. +- Covers: successful registration, duplicate email, invalid input, + successful login, invalid password, and non-existent account. + +### `apps/web` + +- Framework: [Vitest](https://vitest.dev/) + [Testing Library](https://testing-library.com/) (jsdom) +- Tests live in `__tests__/`. +- Covers: login/register pages render correctly and show validation + errors for invalid input. + +### `apps/mobile` + +- Framework: [Jest](https://jestjs.io/) via `jest-expo` + + `@testing-library/react-native` +- Tests live in `__tests__/`. +- Covers: a basic render test verifying the project's test + infrastructure works. No screen or feature tests exist yet because no + screens are implemented. + +## CI expectations + +Each package is validated independently in `.github/workflows/`: + +| Workflow | Steps | +| ----------------- | ------------------------------------- | +| `shared.yml` | install, lint, build (`shared`, `stellar`) | +| `api.yml` | install, build shared, lint, test, build | +| `web.yml` | install, build shared, lint, test, build | +| `mobile.yml` | install, lint, test | + +A failing lint, test, or build step fails the corresponding workflow, +which blocks merging the pull request. diff --git a/legacy/nest-backend/.env.example b/legacy/nest-backend/.env.example deleted file mode 100644 index ce1c712..0000000 --- a/legacy/nest-backend/.env.example +++ /dev/null @@ -1,24 +0,0 @@ -PORT=5000 -MONGO_URI= -STELLAR_NETWORK=testnet -STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org -STELLAR_DESTINATION_ADDRESS= -PAYMENT_RECONCILIATION_INTERVAL_MS=60000 -PAYMENT_RECONCILIATION_BATCH_SIZE=50 -PAYMENT_RECONCILIATION_STALE_MS=30000 -PAYMENT_LISTENER_INTERVAL_MS=20000 -PAYMENT_LISTENER_BATCH_SIZE=50 -STELLAR_PAYMENTS_START_CURSOR=now -PAYMENT_EVENT_PROCESSOR_INTERVAL_MS=5000 -PAYMENT_EVENT_PROCESSOR_BATCH_SIZE=25 -PAYMENT_EVENT_RETRY_BASE_MS=5000 -PAYMENT_EVENT_RETRY_MAX_MS=300000 -SMTP_HOST=your_smtp_host -SMTP_PORT=587 -SMTP_SECURE=true -SMTP_USER=your_smtp_username -SMTP_PASS=your_smtp_password -SMTP_FROM=your_sender_email -SPOTIFY_CLIENT_SECRET=sportify_client_secret -SPOTIFY_CLIENT_ID=sportify_id -SPOTIFY_REDIRECT_URI=http://localhost:5000/api/auth/spotify/callback diff --git a/legacy/nest-backend/.eslintrc.js b/legacy/nest-backend/.eslintrc.js deleted file mode 100644 index 259de13..0000000 --- a/legacy/nest-backend/.eslintrc.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - project: 'tsconfig.json', - tsconfigRootDir: __dirname, - sourceType: 'module', - }, - plugins: ['@typescript-eslint/eslint-plugin'], - extends: [ - 'plugin:@typescript-eslint/recommended', - 'plugin:prettier/recommended', - ], - root: true, - env: { - node: true, - jest: true, - }, - ignorePatterns: ['.eslintrc.js'], - rules: { - '@typescript-eslint/interface-name-prefix': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-explicit-any': 'off', - }, -}; diff --git a/legacy/nest-backend/.prettierrc b/legacy/nest-backend/.prettierrc deleted file mode 100644 index dcb7279..0000000 --- a/legacy/nest-backend/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "trailingComma": "all" -} \ No newline at end of file diff --git a/legacy/nest-backend/nest-cli.json b/legacy/nest-backend/nest-cli.json deleted file mode 100644 index f9aa683..0000000 --- a/legacy/nest-backend/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/legacy/nest-backend/package-lock.json b/legacy/nest-backend/package-lock.json deleted file mode 100644 index 580179c..0000000 --- a/legacy/nest-backend/package-lock.json +++ /dev/null @@ -1,10411 +0,0 @@ -{ - "name": "MixMatch-Backend", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "MixMatch-Backend", - "version": "0.0.1", - "license": "UNLICENSED", - "dependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/config": "^4.0.2", - "@nestjs/core": "^10.0.0", - "@nestjs/jwt": "^11.0.0", - "@nestjs/passport": "^11.0.5", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/typeorm": "^11.0.0", - "bcryptjs": "^3.0.2", - "dotenv": "^17.0.1", - "jsonwebtoken": "^9.0.2", - "passport": "^0.7.0", - "passport-apple": "^2.0.2", - "pg": "^8.16.3", - "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.1", - "typeorm": "^0.3.25" - }, - "devDependencies": { - "@nestjs/cli": "^10.0.0", - "@nestjs/schematics": "^10.0.0", - "@nestjs/testing": "^10.0.0", - "@types/express": "^5.0.0", - "@types/jest": "^29.5.2", - "@types/jsonwebtoken": "^9.0.10", - "@types/node": "^20.3.1", - "@types/passport-apple": "^2.0.3", - "@types/supertest": "^6.0.0", - "@typescript-eslint/eslint-plugin": "^8.0.0", - "@typescript-eslint/parser": "^8.0.0", - "eslint": "^8.0.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "jest": "^29.5.0", - "prettier": "^3.0.0", - "source-map-support": "^0.5.21", - "supertest": "^7.0.0", - "ts-jest": "^29.1.0", - "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "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": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", - "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-17.3.11.tgz", - "integrity": "sha512-kcOMqp+PHAKkqRad7Zd7PbpqJ0LqLaNZdY1+k66lLWmkEBozgq8v4ASn/puPWf9Bo0HpCiK+EzLf0VHE8Z/y6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "ansi-colors": "4.1.3", - "inquirer": "9.2.15", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "bin": { - "schematics": "bin/schematics.js" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@angular-devkit/schematics-cli/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/@angular-devkit/schematics-cli/node_modules/inquirer": { - "version": "9.2.15", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", - "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ljharb/through": "^2.3.12", - "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^3.2.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "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/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", - "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", - "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.27.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "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-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "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/@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/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "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/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "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/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "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/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "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.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "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.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "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.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "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.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "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.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "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/@jest/reporters/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "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/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "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.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@ljharb/through": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.14.tgz", - "integrity": "sha512-ajBvlKpWucBB17FuQYUShqpqy8GRgYEpJW0vWJbUu1CV9lWyrDCapy0lScU8T8Z6qn49sSwJB3+M+evYIdGg+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/@nestjs/cli": { - "version": "10.4.9", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz", - "integrity": "sha512-s8qYd97bggqeK7Op3iD49X2MpFtW4LVNLAwXFkfbRxKME6IYT7X0muNTJ2+QfI8hpbNx9isWkrLWIp+g5FOhiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "@angular-devkit/schematics-cli": "17.3.11", - "@nestjs/schematics": "^10.0.1", - "chalk": "4.1.2", - "chokidar": "3.6.0", - "cli-table3": "0.6.5", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "9.0.2", - "glob": "10.4.5", - "inquirer": "8.2.6", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "tree-kill": "1.2.2", - "tsconfig-paths": "4.2.0", - "tsconfig-paths-webpack-plugin": "4.2.0", - "typescript": "5.7.2", - "webpack": "5.97.1", - "webpack-node-externals": "3.0.0" - }, - "bin": { - "nest": "bin/nest.js" - }, - "engines": { - "node": ">= 16.14" - }, - "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0", - "@swc/core": "^1.3.62" - }, - "peerDependenciesMeta": { - "@swc/cli": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } - }, - "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/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@nestjs/cli/node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "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.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "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": "10.4.18", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.18.tgz", - "integrity": "sha512-9SrTth6YJJ9CjVnekw9WP8kaiwh+tSgR0KIrPYV/aWgF9D0175uDJUglzbiCfUbkPyHN8jcKXUXd3EVPDY6BNA==", - "license": "MIT", - "dependencies": { - "file-type": "20.4.1", - "iterare": "1.2.1", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": "*", - "class-validator": "*", - "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.2", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.2.tgz", - "integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==", - "license": "MIT", - "dependencies": { - "dotenv": "16.4.7", - "dotenv-expand": "12.0.1", - "lodash": "4.17.21" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "rxjs": "^7.1.0" - } - }, - "node_modules/@nestjs/config/node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/@nestjs/core": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.18.tgz", - "integrity": "sha512-+cs96rnpHIfkn9o0DLZFKuE1RZ3FFQkpkzz+DY2U8C3Wn3VX5fQaO4YuabweLIhUKTLr9DMxPycA5qk5rAPFBw==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@nuxtjs/opencollective": "0.3.2", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "3.3.0", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/microservices": "^10.0.0", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/websockets": "^10.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.0", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.0.tgz", - "integrity": "sha512-v7YRsW3Xi8HNTsO+jeHSEEqelX37TVWgwt+BcxtkG/OfXJEOs6GZdbdza200d6KqId1pJQZ6UPj1F0M6E+mxaA==", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "9.0.7", - "jsonwebtoken": "9.0.2" - }, - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" - } - }, - "node_modules/@nestjs/jwt/node_modules/@types/jsonwebtoken": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", - "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "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": "10.4.18", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.18.tgz", - "integrity": "sha512-v+W+Pu5NOVK/bSG5A5mOnXyoVwN5mJUe4o0j0UJ9Ig9JMmjVxg+Zw2ydTfpOQ+R82lRYWJUjjv3dvqKaFW2z7w==", - "license": "MIT", - "dependencies": { - "body-parser": "1.20.3", - "cors": "2.8.5", - "express": "4.21.2", - "multer": "2.0.0", - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0" - } - }, - "node_modules/@nestjs/schematics": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", - "integrity": "sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "comment-json": "4.2.5", - "jsonc-parser": "3.3.1", - "pluralize": "8.0.0" - }, - "peerDependencies": { - "typescript": ">=4.8.2" - } - }, - "node_modules/@nestjs/schematics/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/@nestjs/testing": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.18.tgz", - "integrity": "sha512-oTeGjnh1qeSZMFqze1Rz5lEtAOBRDApulWDoLYSyzh+8/jFflhCYAGeOHncItkGq9wBRe1R1Ct2GyRFdTmpcjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0", - "@nestjs/microservices": "^10.0.0", - "@nestjs/platform-express": "^10.0.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - } - } - }, - "node_modules/@nestjs/typeorm": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", - "integrity": "sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==", - "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" - } - }, - "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/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nuxtjs/opencollective": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", - "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", - "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.2.7", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", - "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "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": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.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.2.7", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", - "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "fflate": "^0.8.2", - "token-types": "^6.0.0" - }, - "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.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "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/@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.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "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==", - "dev": true, - "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==", - "dev": true, - "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.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "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==", - "dev": true, - "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": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.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==", - "dev": true, - "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/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", - "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/oauth": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.6.tgz", - "integrity": "sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/passport-apple": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/passport-apple/-/passport-apple-2.0.3.tgz", - "integrity": "sha512-XncceSuR57+/tz3PmjDG3Tm6+mW7qTYxbILnNRo6Wu5wIZ60Yvlp/LYR6GAMWhWcGtdkXDSAzAYmuBD+duve2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/passport-oauth2": "*" - } - }, - "node_modules/@types/passport-oauth2": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.8.0.tgz", - "integrity": "sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/oauth": "*", - "@types/passport": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "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.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "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": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", - "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "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.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.33.1.tgz", - "integrity": "sha512-TDCXj+YxLgtvxvFlAvpoRv9MAncDLBV2oT9Bd7YBGC/b/sEURoOYuIwLI99rjWOfY3QtDzO+mk0n4AmdFExW8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/type-utils": "8.33.1", - "@typescript-eslint/utils": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.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.33.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.33.1.tgz", - "integrity": "sha512-qwxv6dq682yVvgKKp2qWwLgRbscDAYktPptK4JPojCwwi3R9cwrvIxS4lvBpzmcqzR4bdn54Z0IG1uHFskW4dA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", - "debug": "^4.3.4" - }, - "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", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.33.1.tgz", - "integrity": "sha512-DZR0efeNklDIHHGRpMpR5gJITQpu6tLr9lDJnKdONTC7vvzOlLAG/wcfxcdxEWrbiZApcoBCzXqU/Z458Za5Iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.33.1", - "@typescript-eslint/types": "^8.33.1", - "debug": "^4.3.4" - }, - "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 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.33.1.tgz", - "integrity": "sha512-dM4UBtgmzHR9bS0Rv09JST0RcHYearoEoo3pG5B6GoTR9XcyeqX87FEhPo+5kTvVfKCvfHaHrcgeJQc6mrDKrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1" - }, - "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.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.33.1.tgz", - "integrity": "sha512-STAQsGYbHCF0/e+ShUQ4EatXQ7ceh3fBCXkNU7/MZVKulrlq1usH7t2FhxvCpuCi5O5oi1vmVaAjrGeL71OK1g==", - "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 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.33.1.tgz", - "integrity": "sha512-1cG37d9xOkhlykom55WVwG2QRNC7YXlxMaMzqw2uPeJixBFfKWZgaP/hjAObqMN/u3fr5BrTwTnc31/L9jQ2ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.33.1", - "@typescript-eslint/utils": "8.33.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.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", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.33.1.tgz", - "integrity": "sha512-xid1WfizGhy/TKMTwhtVOgalHwPtV8T32MS9MaH50Cwvz6x6YqRIPdD2WvW0XaqOzTV9p5xdLY0h/ZusU5Lokg==", - "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.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.33.1.tgz", - "integrity": "sha512-+s9LYcT8LWjdYWu7IWs7FvUxpQ/DGkdjZeE/GGulHvv8rvYwQvVaUZ6DE+j5x/prADUgSbbCWZ2nPI3usuVeOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.33.1", - "@typescript-eslint/tsconfig-utils": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/visitor-keys": "8.33.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.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 <5.9.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.33.1.tgz", - "integrity": "sha512-52HaBiEQUaRYqAXpfzWSR2U3gxk92Kw006+xZpElaPMg3C4PgM+A5LqwoQI1f9E5aZ/qlxAZxzm42WX+vn92SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.33.1", - "@typescript-eslint/types": "8.33.1", - "@typescript-eslint/typescript-estree": "8.33.1" - }, - "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", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.33.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.33.1.tgz", - "integrity": "sha512-3i8NrFcZeeDHJ+7ZUuDkGT+UHq+XoFGsymNK2jZCOHcfEzRQ0BdpRtdpSx/Iyf3MHLWIcLS0COuOPibKQboIiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.33.1", - "eslint-visitor-keys": "^4.2.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": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "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/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "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": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "devOptional": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.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.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "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/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/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-escapes/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/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": "3.17.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", - "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", - "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.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "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-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "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/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "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": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/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-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "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" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "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/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bcryptjs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", - "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", - "license": "BSD-3-Clause", - "bin": { - "bcrypt": "bin/bcrypt" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/bl/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==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "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.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "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": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.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.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "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.30001721", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz", - "integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==", - "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==", - "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": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "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": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "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/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.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "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": "4.2.5", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", - "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.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": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "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/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "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-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "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/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "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.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dotenv": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.0.1.tgz", - "integrity": "sha512-GLjkduuAL7IMJg/ZnOPm9AnWKJ82mSE2tzXLaJ/6hD6DhwGfZaXG77oB8qbReyiczNxnbxQKyh0OE5mXq0bAHA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz", - "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", - "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/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.165", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz", - "integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==", - "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.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "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": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "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": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.4.1.tgz", - "integrity": "sha512-9dF+KuU/Ilkq27A8idRP7N2DH8iUR6qXcjF3FR2wETY21PZdBrIjwCau8oboyGj9b7etWmTGEeM8e7oOed6ZWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.11.7" - }, - "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": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "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/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/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/eslint/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/eslint/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/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.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.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "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": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "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-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.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/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "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/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-type": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", - "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.2.6", - "strtok3": "^10.2.0", - "token-types": "^6.0.0", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "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": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "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": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "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.0.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz", - "integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "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": ">=12.13.0", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "typescript": ">3.6.0", - "webpack": "^5.11.0" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "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/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": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "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.0.6", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", - "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", - "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": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "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/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "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/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "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/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "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.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "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.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "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.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "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": "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/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/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "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-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "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-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "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": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "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/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jake/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "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/jest-config/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.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": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.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": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "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/jest-runtime/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.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": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.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.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "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": "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/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.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "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": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.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/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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/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/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "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.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "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.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "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": "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/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": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "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/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "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.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "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/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/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": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "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.0.0", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.0.tgz", - "integrity": "sha512-bS8rPZurbAuHGAnApbM9d4h1wSoYqrOqkE+6a64KLMK9yWU7gJXBDDVklKQ3TPi9DRb85cRs6yXaC0+cjxRtRg==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true, - "license": "ISC" - }, - "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": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "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-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-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "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.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "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/oauth": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.2.tgz", - "integrity": "sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==", - "license": "MIT" - }, - "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==", - "dev": true, - "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/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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-apple": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/passport-apple/-/passport-apple-2.0.2.tgz", - "integrity": "sha512-JRXomYvirWeIq11pa/SwhXXxekFWoukMcQu45BDl3Kw5WobtWF0iw99vpkBwPEpdaou0DDSq4udxR34T6eZkdw==", - "license": "MIT", - "dependencies": { - "jsonwebtoken": "^9.0.0", - "passport-oauth2": "^1.6.1" - } - }, - "node_modules/passport-oauth2": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.8.0.tgz", - "integrity": "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==", - "license": "MIT", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.10.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "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-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-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "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/path-scurry/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/path-to-regexp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" - }, - "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/peek-readable": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", - "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "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.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "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.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", - "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.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "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.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "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.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.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/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "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": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "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.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "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": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/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/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "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/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "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": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "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/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "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/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "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": { - "queue-microtask": "^1.2.2" - } - }, - "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/schema-utils/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "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/schema-utils/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/schema-utils/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/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "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/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "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.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "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.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "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/strtok3": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", - "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", - "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/superagent": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", - "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.4", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/superagent/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/supertest": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", - "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^10.2.1" - }, - "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==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.41.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz", - "integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.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.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "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/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "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/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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": "Glob versions prior to v9 are no longer supported", - "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/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", - "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", - "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-buffer/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/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.0.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", - "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", - "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-jest": { - "version": "29.3.4", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.3.4.tgz", - "integrity": "sha512-Iqbrm8IXOmV+ggWHOTEbjwyCf2xZlUMv5npExksXohL+tk8va4Fjhb+X2+Rt9NBmgO7bJ8WpnMLOwih/DnMlFA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "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", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "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.5.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", - "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", - "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": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "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.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/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.25", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.25.tgz", - "integrity": "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==", - "license": "MIT", - "dependencies": { - "@sqltools/formatter": "^1.2.5", - "ansis": "^3.17.0", - "app-root-path": "^3.1.0", - "buffer": "^6.0.3", - "dayjs": "^1.11.13", - "debug": "^4.4.0", - "dedent": "^1.6.0", - "dotenv": "^16.4.7", - "glob": "^10.4.5", - "sha.js": "^2.4.11", - "sql-highlight": "^6.0.0", - "tslib": "^2.8.1", - "uuid": "^11.1.0", - "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", - "@sap/hana-client": "^2.12.25", - "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "hdb-pool": "^0.1.6", - "ioredis": "^5.0.4", - "mongodb": "^5.8.0 || ^6.0.0", - "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", - "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", - "reflect-metadata": "^0.1.14 || ^0.2.0", - "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 - }, - "hdb-pool": { - "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/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/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "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/uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==", - "license": "MIT" - }, - "node_modules/uint8array-extras": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", - "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "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/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "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.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "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/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.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", - "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/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/webpack": { - "version": "5.99.9", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", - "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@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.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "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.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "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.3.2", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.2.tgz", - "integrity": "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "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/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "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/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "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.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "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/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==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/write-file-atomic/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/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" - } - } - } -} diff --git a/legacy/nest-backend/package.json b/legacy/nest-backend/package.json deleted file mode 100644 index a141011..0000000 --- a/legacy/nest-backend/package.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "MixMatch-Backend", - "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": { - "@nestjs/common": "^10.0.0", - "@nestjs/config": "^4.0.2", - "@nestjs/core": "^10.0.0", - "@nestjs/jwt": "^11.0.0", - "@nestjs/passport": "^11.0.5", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/typeorm": "^11.0.0", - "bcryptjs": "^3.0.2", - "dotenv": "^17.0.1", - "jsonwebtoken": "^9.0.2", - "passport": "^0.7.0", - "passport-apple": "^2.0.2", - "pg": "^8.16.3", - "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.1", - "typeorm": "^0.3.25" - }, - "devDependencies": { - "@nestjs/cli": "^10.0.0", - "@nestjs/schematics": "^10.0.0", - "@nestjs/testing": "^10.0.0", - "@types/express": "^5.0.0", - "@types/jest": "^29.5.2", - "@types/jsonwebtoken": "^9.0.10", - "@types/node": "^20.3.1", - "@types/passport-apple": "^2.0.3", - "@types/supertest": "^6.0.0", - "@typescript-eslint/eslint-plugin": "^8.0.0", - "@typescript-eslint/parser": "^8.0.0", - "eslint": "^8.0.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-prettier": "^5.0.0", - "jest": "^29.5.0", - "prettier": "^3.0.0", - "source-map-support": "^0.5.21", - "supertest": "^7.0.0", - "ts-jest": "^29.1.0", - "ts-loader": "^9.4.3", - "ts-node": "^10.9.1", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3" - }, - "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/legacy/nest-backend/src/app.controller.spec.ts b/legacy/nest-backend/src/app.controller.spec.ts deleted file mode 100644 index d22f389..0000000 --- a/legacy/nest-backend/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -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/legacy/nest-backend/src/app.controller.ts b/legacy/nest-backend/src/app.controller.ts deleted file mode 100644 index 7b53c2b..0000000 --- a/legacy/nest-backend/src/app.controller.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Controller, Get, Post, UseInterceptors, UploadedFile } from '@nestjs/common'; -import { AppService } from './app.service'; -import { FileInterceptor } from '@nestjs/platform-express'; -import { diskStorage } from 'multer'; -import { extname } from 'path'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } - - @Post('upload') - @UseInterceptors(FileInterceptor('file', { - storage: diskStorage({ - destination: './uploads', - filename: (req, file, cb) => { - const randomName = Array(32).fill(null).map(() => (Math.round(Math.random() * 16)).toString(16)).join(''); - cb(null, `${randomName}${extname(file.originalname)}`); - }, - }), - })) - uploadFile(@UploadedFile() file: Express.Multer.File) { - return { url: `http://localhost:3000/uploads/${file.filename}` }; - } -} diff --git a/legacy/nest-backend/src/app.module.ts b/legacy/nest-backend/src/app.module.ts deleted file mode 100644 index 3acb633..0000000 --- a/legacy/nest-backend/src/app.module.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { ConfigModule } from '@nestjs/config'; -import { APP_GUARD } from '@nestjs/core'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { AuthModule } from './auth/auth.module'; -import { JwtAuthGuard } from './auth/guards/jwt-auth.guard'; -import { DatabaseModule } from "./database/database.module" -import { UsersModule } from "./users/users.module" -import { PaymentsModule } from "./payments/payments.module" -import { PaymentEvent } from "./payments/entities/payment-event.entity" -import { PaymentStreamCursor } from "./payments/entities/payment-stream-cursor.entity" -import { User } from './users/entities/user.entity'; -import { PaymentTransaction } from "./payments/entities/payment-transaction.entity" -import { RestaurantModule } from './restaurant/restaurant.module'; - -@Module({ - imports: [ - ConfigModule.forRoot({ - isGlobal: true, - envFilePath: '.env', - }), - TypeOrmModule.forRoot({ - type: 'postgres', - url: process.env.DATABASE_URL, - entities: [User, PaymentTransaction, PaymentEvent, PaymentStreamCursor], - synchronize: true, - }), - AuthModule, - DatabaseModule, - UsersModule, - PaymentsModule, - RestaurantModule, - ], - providers: [ - { - provide: APP_GUARD, - useClass: JwtAuthGuard, - }, - AppService, - ], - controllers: [AppController], -}) -export class AppModule {} diff --git a/legacy/nest-backend/src/app.service.ts b/legacy/nest-backend/src/app.service.ts deleted file mode 100644 index 927d7cc..0000000 --- a/legacy/nest-backend/src/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AppService { - getHello(): string { - return 'Hello World!'; - } -} diff --git a/legacy/nest-backend/src/auth/apple/apple-auth.service.spec.ts b/legacy/nest-backend/src/auth/apple/apple-auth.service.spec.ts deleted file mode 100644 index c09cf48..0000000 --- a/legacy/nest-backend/src/auth/apple/apple-auth.service.spec.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ConfigService } from '@nestjs/config'; -import { AppleAuthService } from './apple-auth.service'; -import * as jwt from 'jsonwebtoken'; -import * as fs from 'fs'; -import * as appleSignin from 'apple-signin'; -import { - InternalServerErrorException, - UnauthorizedException, -} from '@nestjs/common'; - -// Mock the external libraries -jest.mock('jsonwebtoken'); -jest.mock('fs'); -jest.mock('apple-signin'); - -describe('AppleAuthService', () => { - let service: AppleAuthService; - let configService: ConfigService; - - const mockMusicKitPrivateKey = 'MOCK_MUSICKIT_PRIVATE_KEY'; - const mockSignInPrivateKey = 'MOCK_SIGN_IN_PRIVATE_KEY'; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - AppleAuthService, - { - provide: ConfigService, - useValue: { - get: jest.fn((key: string) => { - switch (key) { - case 'app.apple.musicKitKeyId': - return 'MUSICKIT_KEY_ID'; - case 'app.apple.teamId': - return 'TEAM_ID'; - case 'app.apple.musicKitPrivateKeyPath': - return '/path/to/musickit.p8'; - case 'app.apple.signInClientId': - return 'SIGN_IN_CLIENT_ID'; - case 'app.apple.signInKeyId': - return 'SIGN_IN_KEY_ID'; - case 'app.apple.signInPrivateKeyPath': - return '/path/to/signin.p8'; - case 'app.apple.signInRedirectUri': - return 'https://your-domain.com/auth/apple/callback'; - default: - return null; - } - }), - }, - }, - ], - }).compile(); - - service = module.get(AppleAuthService); - configService = module.get(ConfigService); - - // Reset mocks before each test - (jwt.sign as jest.Mock).mockReset(); - (fs.readFileSync as jest.Mock).mockReset(); - (appleSignin.getClientSecret as jest.Mock).mockReset(); - (appleSignin.get.authorizationToken as jest.Mock).mockReset(); - (appleSignin.verifyIdToken as jest.Mock).mockReset(); - (appleSignin.get.refreshToken as jest.Mock).mockReset(); - - // Mock fs.readFileSync to return the private keys - (fs.readFileSync as jest.Mock).mockImplementation((path: string) => { - if (path === '/path/to/musickit.p8') { - return mockMusicKitPrivateKey; - } - if (path === '/path/to/signin.p8') { - return mockSignInPrivateKey; - } - return ''; // Default for other paths - }); - }); - - afterEach(() => { - jest.clearAllTimers(); // Clear timers set by scheduleDeveloperTokenRefresh - }); - - describe('getMusicKitDeveloperToken', () => { - it('should generate and return a developer token', async () => { - const mockToken = 'mock-developer-token'; - (jwt.sign as jest.Mock).mockReturnValue(mockToken); - - const token = await service.getMusicKitDeveloperToken(); - expect(token).toBe(mockToken); - expect(fs.readFileSync).toHaveBeenCalledWith( - '/path/to/musickit.p8', - 'utf8', - ); - expect(jwt.sign).toHaveBeenCalledWith( - {}, - mockMusicKitPrivateKey, - expect.objectContaining({ - algorithm: 'ES256', - issuer: 'TEAM_ID', - header: { kid: 'MUSICKIT_KEY_ID' }, - }), - ); - }); - - it('should return cached token if not expired', async () => { - const mockToken = 'cached-developer-token'; - // Manually set a cached token that's still valid - (service as any).musicKitDeveloperToken = mockToken; - (service as any).musicKitDeveloperTokenExpiry = new Date( - Date.now() + 1000 * 60 * 60, - ); // 1 hour from now - - const token = await service.getMusicKitDeveloperToken(); - expect(token).toBe(mockToken); - expect(jwt.sign).not.toHaveBeenCalled(); // Should not regenerate - }); - - it('should regenerate token if cached token is expired', async () => { - jest.useFakeTimers(); // Use fake timers to control Date.now() - const mockOldToken = 'old-developer-token'; - const mockNewToken = 'new-developer-token'; - - (service as any).musicKitDeveloperToken = mockOldToken; - (service as any).musicKitDeveloperTokenExpiry = new Date( - Date.now() - 1000, - ); // 1 second ago - - (jwt.sign as jest.Mock).mockReturnValue(mockNewToken); - - const token = await service.getMusicKitDeveloperToken(); - expect(token).toBe(mockNewToken); - expect(jwt.sign).toHaveBeenCalledTimes(1); // Should regenerate - }); - - it('should throw InternalServerErrorException if configuration is missing', async () => { - (configService.get as jest.Mock).mockReturnValueOnce(null); // Simulate missing key ID - - await expect(service.getMusicKitDeveloperToken()).rejects.toThrow( - InternalServerErrorException, - ); - }); - - it('should throw InternalServerErrorException if private key read fails', async () => { - (fs.readFileSync as jest.Mock).mockImplementation(() => { - throw new Error('File not found'); - }); - - await expect(service.getMusicKitDeveloperToken()).rejects.toThrow( - InternalServerErrorException, - ); - }); - }); - - describe('validateSignInWithAppleCode', () => { - const mockAuthCode = 'mock-auth-code'; - const mockClientSecret = 'mock-client-secret'; - const mockAppleResponse = { - id_token: 'mock-id-token', - refresh_token: 'mock-refresh-token', - access_token: 'mock-access-token', - }; - const mockDecodedIdToken = { - sub: 'mock_apple_user_id', - email: 'test@example.com', - email_verified: true, - is_private_email: false, - }; - - beforeEach(() => { - (appleSignin.getClientSecret as jest.Mock).mockReturnValue( - mockClientSecret, - ); - (appleSignin.get.authorizationToken as jest.Mock).mockResolvedValue( - mockAppleResponse, - ); - (appleSignin.verifyIdToken as jest.Mock).mockResolvedValue( - mockDecodedIdToken, - ); - }); - - it('should validate code and return user data', async () => { - const result = await service.validateSignInWithAppleCode(mockAuthCode); - - expect(appleSignin.getClientSecret).toHaveBeenCalledTimes(1); - expect(appleSignin.get.authorizationToken).toHaveBeenCalledWith( - mockAuthCode, - expect.objectContaining({ - clientID: 'SIGN_IN_CLIENT_ID', - clientSecret: mockClientSecret, - redirectUri: 'https://your-domain.com/auth/apple/callback', - }), - ); - expect(appleSignin.verifyIdToken).toHaveBeenCalledWith( - mockAppleResponse.id_token, - expect.any(Object), - ); - expect(result).toEqual({ - appleUserId: 'mock_apple_user_id', - email: 'test@example.com', - emailVerified: true, - isPrivateEmail: false, - appleRefreshToken: 'mock-refresh-token', - }); - }); - - it('should throw UnauthorizedException if getClientSecret fails', async () => { - (appleSignin.getClientSecret as jest.Mock).mockImplementation(() => { - throw new Error('Secret error'); - }); - await expect( - service.validateSignInWithAppleCode(mockAuthCode), - ).rejects.toThrow(UnauthorizedException); - }); - - it('should throw UnauthorizedException if authorization token cannot be obtained', async () => { - (appleSignin.get.authorizationToken as jest.Mock).mockResolvedValue(null); - await expect( - service.validateSignInWithAppleCode(mockAuthCode), - ).rejects.toThrow(UnauthorizedException); - }); - - it('should throw UnauthorizedException if id_token verification fails', async () => { - (appleSignin.verifyIdToken as jest.Mock).mockRejectedValue( - new Error('Invalid ID token'), - ); - await expect( - service.validateSignInWithAppleCode(mockAuthCode), - ).rejects.toThrow(UnauthorizedException); - }); - }); - - describe('refreshSignInWithAppleToken', () => { - const mockRefreshToken = 'old-refresh-token'; - const mockClientSecret = 'mock-client-secret-refresh'; - const mockAppleRefreshResponse = { - access_token: 'new-access-token', - refresh_token: 'potentially-new-refresh-token', - id_token: 'new-id-token', - }; - - beforeEach(() => { - (appleSignin.getClientSecret as jest.Mock).mockReturnValue( - mockClientSecret, - ); - (appleSignin.get.refreshToken as jest.Mock).mockResolvedValue( - mockAppleRefreshResponse, - ); - }); - - it('should refresh token and return new tokens', async () => { - const result = - await service.refreshSignInWithAppleToken(mockRefreshToken); - - expect(appleSignin.getClientSecret).toHaveBeenCalledTimes(1); - expect(appleSignin.get.refreshToken).toHaveBeenCalledWith( - mockRefreshToken, - expect.objectContaining({ - clientID: 'SIGN_IN_CLIENT_ID', - clientSecret: mockClientSecret, - }), - ); - expect(result).toEqual({ - accessToken: 'new-access-token', - refreshToken: 'potentially-new-refresh-token', - idToken: 'new-id-token', - }); - }); - - it('should return original refresh token if Apple does not provide a new one', async () => { - (appleSignin.get.refreshToken as jest.Mock).mockResolvedValue({ - access_token: 'new-access-token', - id_token: 'new-id-token', - }); // No refresh_token in response - - const result = - await service.refreshSignInWithAppleToken(mockRefreshToken); - expect(result.refreshToken).toBe(mockRefreshToken); - }); - - it('should throw UnauthorizedException if refresh token fails', async () => { - (appleSignin.get.refreshToken as jest.Mock).mockResolvedValue(null); - await expect( - service.refreshSignInWithAppleToken(mockRefreshToken), - ).rejects.toThrow(UnauthorizedException); - }); - }); -}); diff --git a/legacy/nest-backend/src/auth/apple/apple-auth.service.ts b/legacy/nest-backend/src/auth/apple/apple-auth.service.ts deleted file mode 100644 index 5efbcc9..0000000 --- a/legacy/nest-backend/src/auth/apple/apple-auth.service.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { Injectable, InternalServerErrorException, Logger, UnauthorizedException } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import * as jwt from 'jsonwebtoken'; -import * as fs from 'fs'; -import * as appleSignin from 'apple-signin'; // Good library for Apple Sign-In verification - -@Injectable() -export class AppleAuthService { - private readonly logger = new Logger(AppleAuthService.name); - private musicKitDeveloperToken: string | null = null; - private musicKitDeveloperTokenExpiry: Date | null = null; - - constructor(private configService: ConfigService) { - this.scheduleDeveloperTokenRefresh(); - } - - /** - * Generates and caches the Apple Music Developer Token. - */ - public async getMusicKitDeveloperToken(): Promise { - const musicKitKeyId = this.configService.get('app.apple.musicKitKeyId'); - const teamId = this.configService.get('app.apple.teamId'); - const privateKeyPath = this.configService.get('app.apple.musicKitPrivateKeyPath'); - - if (!musicKitKeyId || !teamId || !privateKeyPath) { - throw new InternalServerErrorException('Apple MusicKit configuration is missing.'); - } - - - if (this.musicKitDeveloperToken && this.musicKitDeveloperTokenExpiry && this.musicKitDeveloperTokenExpiry > new Date()) { - return this.musicKitDeveloperToken; - } - - try { - const privateKey = fs.readFileSync(privateKeyPath, 'utf8'); - const now = Math.floor(Date.now() / 1000); - const expiresIn = 15777000; - const exp = now + expiresIn; - - const token = jwt.sign( - {}, - privateKey, - { - algorithm: 'ES256', - expiresIn: expiresIn, - issuer: teamId, - header: { - kid: musicKitKeyId, - }, - } - ); - - this.musicKitDeveloperToken = token; - this.musicKitDeveloperTokenExpiry = new Date((exp - 600) * 1000); // Set expiry slightly before actual expiry for proactive refresh - this.logger.log('New Apple Music Developer Token generated successfully.'); - return token; - } catch (error) { - this.logger.error('Error generating Apple Music Developer Token:', error.message, error.stack); - throw new InternalServerErrorException('Failed to generate Apple Music Developer Token.'); - } - } - - private scheduleDeveloperTokenRefresh() { - - setInterval(async () => { - if (!this.musicKitDeveloperToken || (this.musicKitDeveloperTokenExpiry && this.musicKitDeveloperTokenExpiry <= new Date())) { - await this.getMusicKitDeveloperToken(); - } - }, 1000 * 60 * 60 * 24 * 30 * 5); - } - - /** - * Validates the authorization code from Sign in with Apple and returns user data. - */ - public async validateSignInWithAppleCode(authorizationCode: string): Promise { - const clientId = this.configService.get('app.apple.signInClientId'); - const keyId = this.configService.get('app.apple.signInKeyId'); - const teamId = this.configService.get('app.apple.teamId'); - const privateKeyPath = this.configService.get('app.apple.signInPrivateKeyPath'); - const redirectUri = this.configService.get('app.apple.signInRedirectUri'); - - if (!clientId || !keyId || !teamId || !privateKeyPath || !redirectUri) { - throw new InternalServerErrorException('Sign in with Apple configuration is missing.'); - } - - try { - const privateKey = fs.readFileSync(privateKeyPath, 'utf8'); - - // Use apple-signin library for convenience - const clientSecret = appleSignin.getClientSecret({ - clientID: clientId, - teamID: teamId, - keyIdentifier: keyId, - privateKey, - }); - - const response = await appleSignin.get - .authorizationToken(authorizationCode, { - clientID: clientId, - clientSecret, - redirectUri, - }); - - if (!response || !response.id_token) { - throw new UnauthorizedException('Failed to get id_token from Apple.'); - } - - - const decodedToken = await appleSignin.verifyIdToken(response.id_token, { - clientID: clientId, - nonce: '', - }); - - return { - appleUserId: decodedToken.sub, - email: decodedToken.email, - emailVerified: decodedToken.email_verified, - isPrivateEmail: decodedToken.is_private_email, - appleRefreshToken: response.refresh_token, - - - }; - } catch (error) { - this.logger.error('Error validating Sign in with Apple code:', error.message, error.stack); - throw new UnauthorizedException('Invalid Apple Sign-In authorization code.'); - } - } - - /** - * Refreshes the Sign in with Apple access token using the refresh token. - */ - public async refreshSignInWithAppleToken(refreshToken: string): Promise { - const clientId = this.configService.get('app.apple.signInClientId'); - const keyId = this.configService.get('app.apple.signInKeyId'); - const teamId = this.configService.get('app.apple.teamId'); - const privateKeyPath = this.configService.get('app.apple.signInPrivateKeyPath'); - - if (!clientId || !keyId || !teamId || !privateKeyPath) { - throw new InternalServerErrorException('Sign in with Apple configuration is missing for refresh.'); - } - - try { - const privateKey = fs.readFileSync(privateKeyPath, 'utf8'); - - const clientSecret = appleSignin.getClientSecret({ - clientID: clientId, - teamID: teamId, - keyIdentifier: keyId, - privateKey, - }); - - const response = await appleSignin.get - .refreshToken(refreshToken, { - clientID: clientId, - clientSecret, - }); - - if (!response || !response.access_token) { - throw new UnauthorizedException('Failed to refresh Apple access token.'); - } - - return { - accessToken: response.access_token, - refreshToken: response.refresh_token || refreshToken, - idToken: response.id_token, - }; - } catch (error) { - this.logger.error('Error refreshing Sign in with Apple token:', error.message, error.stack); - throw new UnauthorizedException('Failed to refresh Apple Sign-In token.'); - } - } -} \ No newline at end of file diff --git a/legacy/nest-backend/src/auth/auth.controller.spec.ts b/legacy/nest-backend/src/auth/auth.controller.spec.ts deleted file mode 100644 index 5a446e0..0000000 --- a/legacy/nest-backend/src/auth/auth.controller.spec.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthController } from './auth.controller'; -import { AppleAuthService } from './apple/apple-auth.service'; -import { Repository } from 'typeorm'; -import { User } from '../entities/user.entity'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { JwtService } from '@nestjs/jwt'; -import { UnauthorizedException } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; - -// Mock the dependencies -const mockAppleAuthService = { - getMusicKitDeveloperToken: jest.fn(), - validateSignInWithAppleCode: jest.fn(), -}; - -const mockUserRepository = { - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), -}; - -const mockJwtService = { - sign: jest.fn(), -}; - -const mockConfigService = { - get: jest.fn(), -}; - -describe('AuthController', () => { - let controller: AuthController; - let appleAuthService: AppleAuthService; - let usersRepository: Repository; - let jwtService: JwtService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - providers: [ - { provide: AppleAuthService, useValue: mockAppleAuthService }, - { provide: getRepositoryToken(User), useValue: mockUserRepository }, - { provide: JwtService, useValue: mockJwtService }, - { provide: ConfigService, useValue: mockConfigService }, - ], - }).compile(); - - controller = module.get(AuthController); - appleAuthService = module.get(AppleAuthService); - usersRepository = module.get>(getRepositoryToken(User)); - jwtService = module.get(JwtService); - - // Reset mocks before each test - jest.clearAllMocks(); - }); - - describe('getDeveloperToken', () => { - it('should return a developer token', async () => { - const mockToken = 'test-developer-token'; - ( - mockAppleAuthService.getMusicKitDeveloperToken as jest.Mock - ).mockResolvedValue(mockToken); - - const result = await controller.getDeveloperToken(); - expect(result).toEqual({ developerToken: mockToken }); - expect( - mockAppleAuthService.getMusicKitDeveloperToken, - ).toHaveBeenCalledTimes(1); - }); - }); - - describe('signInWithAppleCallback', () => { - const mockCode = 'mock-auth-code'; - const mockAppleAuthData = { - appleUserId: 'apple_user_id_123', - email: 'user@example.com', - appleRefreshToken: 'refresh_token_abc', - }; - const mockUser = { - id: 'uuid-123', - appleUserId: 'apple_user_id_123', - email: 'user@example.com', - appleRefreshToken: 'refresh_token_abc', - } as User; - const mockResponse = { - json: jest.fn(), - status: jest.fn().mockReturnThis(), - } as any; - - it('should create a new user and return auth token if user does not exist', async () => { - ( - mockAppleAuthService.validateSignInWithAppleCode as jest.Mock - ).mockResolvedValue(mockAppleAuthData); - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); - (mockUserRepository.create as jest.Mock).mockReturnValue(mockUser); - (mockUserRepository.save as jest.Mock).mockResolvedValue(mockUser); - (mockJwtService.sign as jest.Mock).mockReturnValue('app-jwt-token'); - - await controller.signInWithAppleCallback( - { - code: mockCode, - user: { - email: 'user@example.com', - name: { firstName: 'John', lastName: 'Doe' }, - }, - }, - mockResponse, - ); - - expect( - mockAppleAuthService.validateSignInWithAppleCode, - ).toHaveBeenCalledWith(mockCode); - expect(mockUserRepository.findOne).toHaveBeenCalledWith({ - where: { appleUserId: 'apple_user_id_123' }, - }); - expect(mockUserRepository.create).toHaveBeenCalledWith( - expect.objectContaining({ - appleUserId: 'apple_user_id_123', - email: 'user@example.com', - firstName: 'John', - }), - ); - expect(mockUserRepository.save).toHaveBeenCalledWith(mockUser); - expect(mockJwtService.sign).toHaveBeenCalledWith({ - userId: mockUser.id, - appleUserId: mockUser.appleUserId, - email: mockUser.email, - }); - expect(mockResponse.json).toHaveBeenCalledWith({ - message: 'Login successful', - user: mockUser, - token: 'app-jwt-token', - }); - }); - - it('should log in existing user and return auth token', async () => { - ( - mockAppleAuthService.validateSignInWithAppleCode as jest.Mock - ).mockResolvedValue(mockAppleAuthData); - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(mockUser); // User already exists - (mockUserRepository.save as jest.Mock).mockResolvedValue(mockUser); - (mockJwtService.sign as jest.Mock).mockReturnValue('app-jwt-token'); - - await controller.signInWithAppleCallback( - { code: mockCode }, - mockResponse, - ); - - expect(mockUserRepository.findOne).toHaveBeenCalledWith({ - where: { appleUserId: 'apple_user_id_123' }, - }); - expect(mockUserRepository.create).not.toHaveBeenCalled(); // Should not create new user - expect(mockUserRepository.save).toHaveBeenCalledWith(mockUser); // Should save (even if no change, for consistency or update logic) - expect(mockResponse.json).toHaveBeenCalledWith({ - message: 'Login successful', - user: mockUser, - token: 'app-jwt-token', - }); - }); - - it('should handle authentication failure and return 401', async () => { - ( - mockAppleAuthService.validateSignInWithAppleCode as jest.Mock - ).mockRejectedValue(new UnauthorizedException('Invalid code')); - - await controller.signInWithAppleCallback( - { code: mockCode }, - mockResponse, - ); - - expect(mockResponse.status).toHaveBeenCalledWith(401); - expect(mockResponse.json).toHaveBeenCalledWith({ - message: 'Authentication failed', - error: 'Invalid code', - }); - }); - }); - - describe('connectMusicAccount', () => { - const mockMusicUserToken = 'mut_token_123'; - const mockStorefrontId = 'us'; - const mockUserId = 'uuid-123'; - const mockUserRequest = { - user: { userId: mockUserId }, // Simulates req.user from your AuthGuard - } as any; - const existingUser = { - id: mockUserId, - appleUserId: 'apple_user_id_123', - email: 'user@example.com', - musicUserToken: null, // Initially not linked - } as User; - - beforeEach(() => { - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(existingUser); - (mockUserRepository.save as jest.Mock).mockResolvedValue(existingUser); - }); - - it('should link Apple Music account for a user', async () => { - const result = await controller.connectMusicAccount( - mockUserRequest, - mockMusicUserToken, - mockStorefrontId, - ); - - expect(mockUserRepository.findOne).toHaveBeenCalledWith({ - where: { id: mockUserId }, - }); - expect(mockUserRepository.save).toHaveBeenCalledWith( - expect.objectContaining({ - musicUserToken: mockMusicUserToken, - musicStorefrontId: mockStorefrontId, - }), - ); - expect(result).toEqual({ - message: 'Apple Music account linked successfully!', - }); - }); - - it('should throw UnauthorizedException if musicUserToken is missing', async () => { - await expect( - controller.connectMusicAccount(mockUserRequest, null, mockStorefrontId), - ).rejects.toThrow(UnauthorizedException); - expect(mockUserRepository.save).not.toHaveBeenCalled(); - }); - - it('should throw UnauthorizedException if user is not found', async () => { - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); // User not found - await expect( - controller.connectMusicAccount( - mockUserRequest, - mockMusicUserToken, - mockStorefrontId, - ), - ).rejects.toThrow(UnauthorizedException); - expect(mockUserRepository.save).not.toHaveBeenCalled(); - }); - }); - - describe('getMusicStatus', () => { - const mockUserId = 'uuid-123'; - const mockUserRequest = { - user: { userId: mockUserId }, - } as any; - const linkedUser = { - id: mockUserId, - musicUserToken: 'linked_mut', - musicStorefrontId: 'gb', - } as User; - const unlinkedUser = { - id: mockUserId, - musicUserToken: null, - } as User; - - it('should return true and storefrontId if music is linked', async () => { - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(linkedUser); - const result = await controller.getMusicStatus(mockUserRequest); - expect(result).toEqual({ isMusicKitLinked: true, storefrontId: 'gb' }); - }); - - it('should return false if music is not linked', async () => { - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(unlinkedUser); - const result = await controller.getMusicStatus(mockUserRequest); - expect(result).toEqual({ - isMusicKitLinked: false, - storefrontId: undefined, - }); - }); - - it('should throw UnauthorizedException if user is not found', async () => { - (mockUserRepository.findOne as jest.Mock).mockResolvedValue(null); - await expect(controller.getMusicStatus(mockUserRequest)).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/legacy/nest-backend/src/auth/auth.controller.ts b/legacy/nest-backend/src/auth/auth.controller.ts deleted file mode 100644 index 43f6d51..0000000 --- a/legacy/nest-backend/src/auth/auth.controller.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Controller, Get, Post, Body, Res, Req, UseGuards, Query } from '@nestjs/common'; -import { AppleAuthService } from './apple/apple-auth.service'; -import { Repository } from 'typeorm'; -import { User } from '../entities/user.entity'; -import { InjectRepository } from '@nestjs/typeorm'; -import { JwtService } from '@nestjs/jwt'; -import { Response, Request } from 'express'; -import { ConfigService } from '@nestjs/config'; - -@Controller('auth/apple') -export class AuthController { - constructor( - private readonly appleAuthService: AppleAuthService, - @InjectRepository(User) - private usersRepository: Repository, - private jwtService: JwtService, - private configService: ConfigService, - ) {} - - // - @Get('developer-token') - async getDeveloperToken() { - const developerToken = await this.appleAuthService.getMusicKitDeveloperToken(); - return { developerToken }; - } - - - @Post('callback') - async signInWithAppleCallback(@Body() body: { code: string; id_token?: string; user?: { email: string; name: { firstName: string; lastName: string } } }, @Res() res: Response) { - const { code, user: appleProvidedUser } = body; - - try { - const appleAuthData = await this.appleAuthService.validateSignInWithAppleCode(code); - - let user = await this.usersRepository.findOne({ where: { appleUserId: appleAuthData.appleUserId } }); - - if (!user) { - - user = this.usersRepository.create({ - appleUserId: appleAuthData.appleUserId, - email: appleAuthData.email || appleProvidedUser?.email, - firstName: appleProvidedUser?.name?.firstName, - lastName: appleProvidedUser?.name?.lastName, - - appleRefreshToken: appleAuthData.appleRefreshToken, - }); - await this.usersRepository.save(user); - console.log(`New user created for Apple ID: ${user.appleUserId}`); - } else { - - if (appleAuthData.appleRefreshToken && user.appleRefreshToken !== appleAuthData.appleRefreshToken) { - user.appleRefreshToken = appleAuthData.appleRefreshToken; - await this.usersRepository.save(user); - } - console.log(`User logged in with Apple ID: ${user.appleUserId}`); - } - - - const payload = { userId: user.id, appleUserId: user.appleUserId, email: user.email }; - const authToken = this.jwtService.sign(payload); - - - - - return res.json({ message: 'Login successful', user, token: authToken }); - - } catch (error) { - console.error('Apple Sign-In callback error:', error); - return res.status(401).json({ message: 'Authentication failed', error: error.message }); - } - } - - - @Post('connect-music') - @UseGuards( /* Your AuthGuard for your internal app JWT */ ) - async connectMusicAccount(@Req() req: Request, @Body('musicUserToken') musicUserToken: string, @Body('storefrontId') storefrontId?: string) { - - const userId = req.user['userId']; - - if (!musicUserToken) { - throw new UnauthorizedException('Music User Token is required.'); - } - - const user = await this.usersRepository.findOne({ where: { id: userId } }); - - if (!user) { - throw new UnauthorizedException('User not found.'); - } - - - user.musicUserToken = musicUserToken; - user.musicStorefrontId = storefrontId; - user.musicUserTokenExpiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 180); // Music User Token validity can vary, roughly 6 months - - await this.usersRepository.save(user); - - return { message: 'Apple Music account linked successfully!' }; - } - - - @Get('music-status') - @UseGuards( /* Your AuthGuard for your internal app JWT */ ) - async getMusicStatus(@Req() req: Request) { - const userId = req.user['userId']; - const user = await this.usersRepository.findOne({ where: { id: userId } }); - - if (!user) { - throw new UnauthorizedException('User not found.'); - } - - return { - isMusicKitLinked: !!user.musicUserToken, - storefrontId: user.musicStorefrontId, - - - - }; - } -} \ No newline at end of file diff --git a/legacy/nest-backend/src/auth/auth.module.ts b/legacy/nest-backend/src/auth/auth.module.ts deleted file mode 100644 index 0830b0d..0000000 --- a/legacy/nest-backend/src/auth/auth.module.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Module } from "@nestjs/common" -import { JwtModule } from "@nestjs/jwt" -import { PassportModule } from "@nestjs/passport" -import { ConfigModule, ConfigService } from "@nestjs/config" -import { AuthService } from "./auth.service" -import { JwtStrategy } from "./strategies/jwt.strategy" -import { UsersModule } from "../users/users.module" -import { AuthController } from "./auth.controller" -import { User } from "src/users/entities/user.entity" -import { AppleAuthService } from "./apple/apple-auth.service" -import { TypeOrmModule } from '@nestjs/typeorm'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([User]), - UsersModule, - PassportModule, - JwtModule.registerAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: (configService: ConfigService) => ({ - secret: configService.get("JWT_SECRET"), - signOptions: { - expiresIn: "1h", - }, - }), - }), - ], - providers: [AuthService, JwtStrategy, AppleAuthService], - exports: [AuthService], - controllers: [AuthController], -}) -export class AuthModule {} diff --git a/legacy/nest-backend/src/auth/auth.service.ts b/legacy/nest-backend/src/auth/auth.service.ts deleted file mode 100644 index ecd57df..0000000 --- a/legacy/nest-backend/src/auth/auth.service.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Injectable, UnauthorizedException } from "@nestjs/common" -import type { JwtService } from "@nestjs/jwt" -import type { UsersService } from "../users/users.service" -import type { User } from "../users/entities/user.entity" -import type { JwtPayload } from "./interfaces/jwt-payload.interface" -import type { RefreshTokenDto } from "./dto/refresh-token.dto" - -@Injectable() -export class AuthService { - constructor( - private readonly usersService: UsersService, - private readonly jwtService: JwtService, - ) {} - - /** - * Generate access token for a user - */ - async generateAccessToken(user: User): Promise { - const payload: JwtPayload = { - sub: user.id, - provider: user.musicProvider, - } - - return this.jwtService.sign(payload) - } - - /** - * Generate refresh token for a user (with longer expiry) - */ - async generateRefreshToken(user: User): Promise { - const payload: JwtPayload = { - sub: user.id, - provider: user.musicProvider, - tokenType: "refresh", - } - - // Refresh token lasts longer (7 days) - return this.jwtService.sign(payload, { expiresIn: "7d" }) - } - - /** - * Generate both access and refresh tokens - */ - async generateTokens(user: User): Promise<{ accessToken: string; refreshToken: string }> { - const [accessToken, refreshToken] = await Promise.all([ - this.generateAccessToken(user), - this.generateRefreshToken(user), - ]) - - return { - accessToken, - refreshToken, - } - } - - /** - * Validate a user by JWT payload - */ - async validateUser(payload: JwtPayload): Promise { - const user = await this.usersService.findOne(payload.sub) - - if (!user) { - throw new UnauthorizedException("User not found") - } - - return user - } - - /** - * Refresh access token using refresh token - */ - async refreshToken(refreshTokenDto: RefreshTokenDto): Promise<{ accessToken: string }> { - try { - // Verify the refresh token - const payload = this.jwtService.verify(refreshTokenDto.refreshToken) - - // Check if it's actually a refresh token - if (payload.tokenType !== "refresh") { - throw new UnauthorizedException("Invalid refresh token") - } - - // Get the user - const user = await this.usersService.findOne(payload.sub) - - // Generate a new access token - const accessToken = await this.generateAccessToken(user) - - return { accessToken } - } catch (error) { - throw new UnauthorizedException("Invalid refresh token") - } - } -} diff --git a/legacy/nest-backend/src/auth/decorators/current-user.decorator.ts b/legacy/nest-backend/src/auth/decorators/current-user.decorator.ts deleted file mode 100644 index e0fa413..0000000 --- a/legacy/nest-backend/src/auth/decorators/current-user.decorator.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createParamDecorator, type ExecutionContext } from "@nestjs/common" - -export const CurrentUser = createParamDecorator((data: unknown, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest() - return request.user -}) diff --git a/legacy/nest-backend/src/auth/decorators/public.decorator.ts b/legacy/nest-backend/src/auth/decorators/public.decorator.ts deleted file mode 100644 index 4e198b2..0000000 --- a/legacy/nest-backend/src/auth/decorators/public.decorator.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { SetMetadata } from "@nestjs/common" - -export const IS_PUBLIC_KEY = "isPublic" -export const Public = () => SetMetadata(IS_PUBLIC_KEY, true) diff --git a/legacy/nest-backend/src/auth/dto/refresh-token.dto.ts b/legacy/nest-backend/src/auth/dto/refresh-token.dto.ts deleted file mode 100644 index 6fbb4f9..0000000 --- a/legacy/nest-backend/src/auth/dto/refresh-token.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IsNotEmpty, IsString } from "class-validator" - -export class RefreshTokenDto { - @IsNotEmpty() - @IsString() - refreshToken: string -} diff --git a/legacy/nest-backend/src/auth/guards/jwt-auth.guard.ts b/legacy/nest-backend/src/auth/guards/jwt-auth.guard.ts deleted file mode 100644 index 58167a9..0000000 --- a/legacy/nest-backend/src/auth/guards/jwt-auth.guard.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Injectable } from "@nestjs/common" -import { AuthGuard } from "@nestjs/passport" -import { Reflector } from "@nestjs/core" -import type { ExecutionContext } from "@nestjs/common" -import { IS_PUBLIC_KEY } from "../decorators/public.decorator" - -@Injectable() -export class JwtAuthGuard extends AuthGuard("jwt") { - constructor(private readonly reflector: Reflector) { - super() - } - - canActivate(context: ExecutionContext) { - const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ - context.getHandler(), - context.getClass(), - ]) - - if (isPublic) { - return true - } - - return super.canActivate(context) - } -} diff --git a/legacy/nest-backend/src/auth/interfaces/jwt-payload.interface.ts b/legacy/nest-backend/src/auth/interfaces/jwt-payload.interface.ts deleted file mode 100644 index 0b655e9..0000000 --- a/legacy/nest-backend/src/auth/interfaces/jwt-payload.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { MusicProvider } from "@prisma/client" - -export interface JwtPayload { - sub: string // User ID - provider: MusicProvider // Music provider - tokenType?: string // Optional token type (for refresh tokens) - iat?: number // Issued at (automatically added by JWT) - exp?: number // Expiration time (automatically added by JWT) -} diff --git a/legacy/nest-backend/src/auth/services/crypto.service.ts b/legacy/nest-backend/src/auth/services/crypto.service.ts deleted file mode 100644 index 2b6c50d..0000000 --- a/legacy/nest-backend/src/auth/services/crypto.service.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Injectable } from "@nestjs/common" -import type { ConfigService } from "@nestjs/config" -import * as crypto from "crypto" - -@Injectable() -export class CryptoService { - private readonly algorithm = "aes-256-gcm" - private readonly secretKey: Buffer - - constructor(private configService: ConfigService) { - const key = this.configService.get("ENCRYPTION_KEY") - if (!key || key.length !== 32) { - throw new Error("ENCRYPTION_KEY must be exactly 32 characters long") - } - this.secretKey = Buffer.from(key, "utf8") - } - - /** - * Encrypt a string value - */ - encrypt(text: string): string { - const iv = crypto.randomBytes(16) - const cipher = crypto.createCipher(this.algorithm, this.secretKey) - cipher.setAAD(Buffer.from("mixmatch-auth", "utf8")) - - let encrypted = cipher.update(text, "utf8", "hex") - encrypted += cipher.final("hex") - - const authTag = cipher.getAuthTag() - - // Combine iv, authTag, and encrypted data - return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}` - } - - /** - * Decrypt a string value - */ - decrypt(encryptedData: string): string { - const [ivHex, authTagHex, encrypted] = encryptedData.split(":") - - const iv = Buffer.from(ivHex, "hex") - const authTag = Buffer.from(authTagHex, "hex") - - const decipher = crypto.createDecipher(this.algorithm, this.secretKey) - decipher.setAAD(Buffer.from("mixmatch-auth", "utf8")) - decipher.setAuthTag(authTag) - - let decrypted = decipher.update(encrypted, "hex", "utf8") - decrypted += decipher.final("utf8") - - return decrypted - } - - /** - * Generate a secure random state for OAuth - */ - generateState(): string { - return crypto.randomBytes(32).toString("hex") - } -} diff --git a/legacy/nest-backend/src/auth/spotify/dto/spotify-callback.dto.ts b/legacy/nest-backend/src/auth/spotify/dto/spotify-callback.dto.ts deleted file mode 100644 index 5640b01..0000000 --- a/legacy/nest-backend/src/auth/spotify/dto/spotify-callback.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IsNotEmpty, IsOptional, IsString } from "class-validator" - -export class SpotifyCallbackDto { - @IsNotEmpty() - @IsString() - code: string - - @IsOptional() - @IsString() - state?: string - - @IsOptional() - @IsString() - error?: string -} diff --git a/legacy/nest-backend/src/auth/spotify/interfaces/spotify-user.interface.ts b/legacy/nest-backend/src/auth/spotify/interfaces/spotify-user.interface.ts deleted file mode 100644 index 2f6ee4b..0000000 --- a/legacy/nest-backend/src/auth/spotify/interfaces/spotify-user.interface.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface SpotifyUser { - id: string - display_name: string - email?: string - images?: Array<{ - url: string - height: number - width: number - }> - followers?: { - total: number - } - country?: string - product?: string -} - -export interface SpotifyTokenResponse { - access_token: string - token_type: string - scope: string - expires_in: number - refresh_token: string -} diff --git a/legacy/nest-backend/src/auth/spotify/spotify-oauth.controller.ts b/legacy/nest-backend/src/auth/spotify/spotify-oauth.controller.ts deleted file mode 100644 index 1f986a4..0000000 --- a/legacy/nest-backend/src/auth/spotify/spotify-oauth.controller.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { Controller, Get, Query, HttpStatus } from "@nestjs/common" -import type { Response } from "express" -import type { SpotifyOAuthService } from "./spotify-oauth.service" -import type { SpotifyCallbackDto } from "./dto/spotify-callback.dto" - -@Controller("auth/spotify") -export class SpotifyOAuthController { - constructor(private readonly spotifyOAuthService: SpotifyOAuthService) {} - - /** - * Initiate Spotify OAuth flow - * GET /auth/spotify - */ - @Get() - async initiateAuth(res: Response) { - const { url, state } = this.spotifyOAuthService.getAuthorizationUrl() - - // Store state in session/cookie for validation (optional but recommended) - res.cookie("spotify_oauth_state", state, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - maxAge: 10 * 60 * 1000, // 10 minutes - }) - - return res.redirect(url) - } - - /** - * Handle Spotify OAuth callback - * GET /auth/spotify/callback - */ - @Get("callback") - async handleCallback(query: SpotifyCallbackDto, res: Response) { - try { - const result = await this.spotifyOAuthService.handleCallback(query) - - // Set JWT tokens as HTTP-only cookies - res.cookie("access_token", result.tokens.accessToken, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - maxAge: 60 * 60 * 1000, // 1 hour - }) - - res.cookie("refresh_token", result.tokens.refreshToken, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days - }) - - // Redirect to frontend success page - const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000" - return res.redirect(`${frontendUrl}/auth/success`) - } catch (error) { - // Redirect to frontend error page - const frontendUrl = process.env.FRONTEND_URL || "http://localhost:3000" - return res.redirect(`${frontendUrl}/auth/error?message=${encodeURIComponent(error.message)}`) - } - } - - /** - * Get current user's Spotify profile (protected route example) - */ - @Get("profile") - async getProfile(@Query("userId") userId: string) { - try { - const accessToken = await this.spotifyOAuthService.getDecryptedAccessToken(userId) - - const response = await fetch("https://api.spotify.com/v1/me", { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - - if (!response.ok) { - // Try to refresh token if expired - const newAccessToken = await this.spotifyOAuthService.refreshSpotifyToken(userId) - const retryResponse = await fetch("https://api.spotify.com/v1/me", { - headers: { - Authorization: `Bearer ${newAccessToken}`, - }, - }) - - if (!retryResponse.ok) { - throw new Error("Failed to fetch Spotify profile") - } - - return await retryResponse.json() - } - - return await response.json() - } catch (error) { - return { - error: error.message, - status: HttpStatus.INTERNAL_SERVER_ERROR, - } - } - } -} diff --git a/legacy/nest-backend/src/auth/spotify/spotify-oauth.module.ts b/legacy/nest-backend/src/auth/spotify/spotify-oauth.module.ts deleted file mode 100644 index d127e10..0000000 --- a/legacy/nest-backend/src/auth/spotify/spotify-oauth.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Module } from "@nestjs/common" -import { ConfigModule } from "@nestjs/config" -import { SpotifyOAuthService } from "./spotify-oauth.service" -import { SpotifyOAuthController } from "./spotify-oauth.controller" -import { UsersModule } from "../../users/users.module" -import { AuthModule } from "../auth.module" -import { CryptoService } from "../services/crypto.service" - -@Module({ - imports: [ConfigModule, UsersModule, AuthModule], - providers: [SpotifyOAuthService, CryptoService], - controllers: [SpotifyOAuthController], - exports: [SpotifyOAuthService], -}) -export class SpotifyOAuthModule {} diff --git a/legacy/nest-backend/src/auth/spotify/spotify-oauth.service.ts b/legacy/nest-backend/src/auth/spotify/spotify-oauth.service.ts deleted file mode 100644 index e9673f0..0000000 --- a/legacy/nest-backend/src/auth/spotify/spotify-oauth.service.ts +++ /dev/null @@ -1,228 +0,0 @@ -import { Injectable, BadRequestException, InternalServerErrorException } from "@nestjs/common" -import type { ConfigService } from "@nestjs/config" -import type { UsersService } from "../../users/users.service" -import type { AuthService } from "../auth.service" -import type { CryptoService } from "../services/crypto.service" -import type { SpotifyUser, SpotifyTokenResponse } from "./interfaces/spotify-user.interface" -import type { SpotifyCallbackDto } from "./dto/spotify-callback.dto" -import { MusicProvider } from "@prisma/client" - -@Injectable() -export class SpotifyOAuthService { - private readonly spotifyClientId: string - private readonly spotifyClientSecret: string - private readonly spotifyRedirectUri: string - private readonly spotifyAuthUrl = "https://accounts.spotify.com/authorize" - private readonly spotifyTokenUrl = "https://accounts.spotify.com/api/token" - private readonly spotifyApiUrl = "https://api.spotify.com/v1" - - constructor( - private readonly configService: ConfigService, - private readonly usersService: UsersService, - private readonly authService: AuthService, - private readonly cryptoService: CryptoService, - ) { - this.spotifyClientId = this.configService.get("SPOTIFY_CLIENT_ID")! - this.spotifyClientSecret = this.configService.get("SPOTIFY_CLIENT_SECRET")! - this.spotifyRedirectUri = this.configService.get("SPOTIFY_REDIRECT_URI")! - - if (!this.spotifyClientId || !this.spotifyClientSecret || !this.spotifyRedirectUri) { - throw new Error("Missing required Spotify OAuth configuration") - } - } - - /** - * Generate Spotify OAuth authorization URL - */ - getAuthorizationUrl(): { url: string; state: string } { - const state = this.cryptoService.generateState() - const scopes = [ - "user-read-private", - "user-read-email", - "user-library-read", - "user-top-read", - "playlist-read-private", - "playlist-read-collaborative", - ].join(" ") - - const params = new URLSearchParams({ - response_type: "code", - client_id: this.spotifyClientId, - scope: scopes, - redirect_uri: this.spotifyRedirectUri, - state, - show_dialog: "true", // Force user to approve app each time - }) - - const url = `${this.spotifyAuthUrl}?${params.toString()}` - - return { url, state } - } - - /** - * Exchange authorization code for access and refresh tokens - */ - private async exchangeCodeForTokens(code: string): Promise { - const params = new URLSearchParams({ - grant_type: "authorization_code", - code, - redirect_uri: this.spotifyRedirectUri, - }) - - const authHeader = Buffer.from(`${this.spotifyClientId}:${this.spotifyClientSecret}`).toString("base64") - - try { - const response = await fetch(this.spotifyTokenUrl, { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Authorization: `Basic ${authHeader}`, - }, - body: params.toString(), - }) - - if (!response.ok) { - const error = await response.text() - throw new Error(`Spotify token exchange failed: ${error}`) - } - - return await response.json() - } catch (error) { - throw new InternalServerErrorException(`Failed to exchange code for tokens: ${error.message}`) - } - } - - /** - * Fetch user profile from Spotify API - */ - private async fetchSpotifyUserProfile(accessToken: string): Promise { - try { - const response = await fetch(`${this.spotifyApiUrl}/me`, { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }) - - if (!response.ok) { - const error = await response.text() - throw new Error(`Spotify API request failed: ${error}`) - } - - return await response.json() - } catch (error) { - throw new InternalServerErrorException(`Failed to fetch user profile: ${error.message}`) - } - } - - /** - * Handle Spotify OAuth callback - */ - async handleCallback(callbackDto: SpotifyCallbackDto): Promise<{ - user: any - tokens: { accessToken: string; refreshToken: string } - }> { - if (callbackDto.error) { - throw new BadRequestException(`Spotify OAuth error: ${callbackDto.error}`) - } - - // Exchange code for tokens - const tokenResponse = await this.exchangeCodeForTokens(callbackDto.code) - - // Fetch user profile - const spotifyUser = await this.fetchSpotifyUserProfile(tokenResponse.access_token) - - // Check if user already exists - let user = await this.usersService.findByMusicId(spotifyUser.id) - - if (user) { - // Update existing user's tokens - user = await this.usersService.update(user.id, { - accessToken: this.cryptoService.encrypt(tokenResponse.access_token), - refreshToken: this.cryptoService.encrypt(tokenResponse.refresh_token), - displayName: spotifyUser.display_name || user.displayName, - email: spotifyUser.email || user.email, - }) - } else { - // Create new user - user = await this.usersService.create({ - email: spotifyUser.email, - displayName: spotifyUser.display_name || `Spotify User ${spotifyUser.id}`, - musicProvider: MusicProvider.SPOTIFY, - musicId: spotifyUser.id, - accessToken: this.cryptoService.encrypt(tokenResponse.access_token), - refreshToken: this.cryptoService.encrypt(tokenResponse.refresh_token), - }) - } - - // Generate JWT tokens for our app - const jwtTokens = await this.authService.generateTokens(user) - - return { - user: user.toPublic(), - tokens: jwtTokens, - } - } - - /** - * Refresh Spotify access token - */ - async refreshSpotifyToken(userId: string): Promise { - const user = await this.usersService.findOne(userId) - - if (user.musicProvider !== MusicProvider.SPOTIFY) { - throw new BadRequestException("User is not a Spotify user") - } - - const decryptedRefreshToken = this.cryptoService.decrypt(user.refreshToken) - - const params = new URLSearchParams({ - grant_type: "refresh_token", - refresh_token: decryptedRefreshToken, - }) - - const authHeader = Buffer.from(`${this.spotifyClientId}:${this.spotifyClientSecret}`).toString("base64") - - try { - const response = await fetch(this.spotifyTokenUrl, { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - Authorization: `Basic ${authHeader}`, - }, - body: params.toString(), - }) - - if (!response.ok) { - const error = await response.text() - throw new Error(`Spotify token refresh failed: ${error}`) - } - - const tokenResponse: SpotifyTokenResponse = await response.json() - - // Update user's access token (and refresh token if provided) - await this.usersService.update(userId, { - accessToken: this.cryptoService.encrypt(tokenResponse.access_token), - ...(tokenResponse.refresh_token && { - refreshToken: this.cryptoService.encrypt(tokenResponse.refresh_token), - }), - }) - - return tokenResponse.access_token - } catch (error) { - throw new InternalServerErrorException(`Failed to refresh Spotify token: ${error.message}`) - } - } - - /** - * Get decrypted Spotify access token for a user - */ - async getDecryptedAccessToken(userId: string): Promise { - const user = await this.usersService.findOne(userId) - - if (user.musicProvider !== MusicProvider.SPOTIFY) { - throw new BadRequestException("User is not a Spotify user") - } - - return this.cryptoService.decrypt(user.accessToken) - } -} diff --git a/legacy/nest-backend/src/auth/strategies/jwt.strategy.ts b/legacy/nest-backend/src/auth/strategies/jwt.strategy.ts deleted file mode 100644 index fbb0d74..0000000 --- a/legacy/nest-backend/src/auth/strategies/jwt.strategy.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Injectable, UnauthorizedException } from "@nestjs/common" -import { PassportStrategy } from "@nestjs/passport" -import { ExtractJwt, Strategy } from "passport-jwt" -import type { ConfigService } from "@nestjs/config" -import type { AuthService } from "../auth.service" -import type { JwtPayload } from "../interfaces/jwt-payload.interface" -import type { User } from "../../users/entities/user.entity" - -@Injectable() -export class JwtStrategy extends PassportStrategy(Strategy) { - constructor( - private readonly configService: ConfigService, - private readonly authService: AuthService, - ) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: configService.get("JWT_SECRET"), - }) - } - - /** - * Validate the JWT payload and return the user - */ - async validate(payload: JwtPayload): Promise { - // Reject refresh tokens when used for access - if (payload.tokenType === "refresh") { - throw new UnauthorizedException("Invalid token type") - } - - return this.authService.validateUser(payload) - } -} diff --git a/legacy/nest-backend/src/common/encryption/encryption.service.ts b/legacy/nest-backend/src/common/encryption/encryption.service.ts deleted file mode 100644 index f678331..0000000 --- a/legacy/nest-backend/src/common/encryption/encryption.service.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Injectable, InternalServerErrorException } from '@nestjs/common'; -import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto'; -import { ConfigService } from '@nestjs/config'; - -@Injectable() -export class EncryptionService { - private readonly algorithm = 'aes-256-gcm'; - private readonly key: Buffer; - - constructor(private configService: ConfigService) { - const encryptionKey = this.configService.get('ENCRYPTION_KEY'); // A strong, long key for encryption - if (!encryptionKey) { - throw new InternalServerErrorException('ENCRYPTION_KEY is not defined.'); - } - this.key = scryptSync(encryptionKey, 'salt', 32); // Derive a 32-byte key - } - - encrypt(text: string): string { - const iv = randomBytes(16); // Initialization vector - const cipher = createCipheriv(this.algorithm, this.key, iv); - let encrypted = cipher.update(text, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - const tag = cipher.getAuthTag().toString('hex'); - return `${iv.toString('hex')}:${encrypted}:${tag}`; - } - - decrypt(encryptedText: string): string { - const [ivHex, encryptedHex, tagHex] = encryptedText.split(':'); - const iv = Buffer.from(ivHex, 'hex'); - const encrypted = Buffer.from(encryptedHex, 'hex'); - const tag = Buffer.from(tagHex, 'hex'); - - const decipher = createDecipheriv(this.algorithm, this.key, iv); - decipher.setAuthTag(tag); - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); - return decrypted; - } -} \ No newline at end of file diff --git a/legacy/nest-backend/src/database/database.module.ts b/legacy/nest-backend/src/database/database.module.ts deleted file mode 100644 index 824b9f6..0000000 --- a/legacy/nest-backend/src/database/database.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Global, Module } from "@nestjs/common" -import { PrismaService } from "./prisma.service" - -@Global() -@Module({ - providers: [PrismaService], - exports: [PrismaService], -}) -export class DatabaseModule {} diff --git a/legacy/nest-backend/src/database/prisma.service.ts b/legacy/nest-backend/src/database/prisma.service.ts deleted file mode 100644 index cefdd98..0000000 --- a/legacy/nest-backend/src/database/prisma.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable, type OnModuleInit, type OnModuleDestroy } from "@nestjs/common" -import { PrismaClient } from "@prisma/client" - -@Injectable() -export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { - async onModuleInit() { - await this.$connect() - } - - async onModuleDestroy() { - await this.$disconnect() - } -} diff --git a/legacy/nest-backend/src/payments/constants/payment.constants.ts b/legacy/nest-backend/src/payments/constants/payment.constants.ts deleted file mode 100644 index 4e393be..0000000 --- a/legacy/nest-backend/src/payments/constants/payment.constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const PAYMENT_PROVIDER = Symbol("PAYMENT_PROVIDER") diff --git a/legacy/nest-backend/src/payments/dto/stellar-event.dto.ts b/legacy/nest-backend/src/payments/dto/stellar-event.dto.ts deleted file mode 100644 index e6ae3e9..0000000 --- a/legacy/nest-backend/src/payments/dto/stellar-event.dto.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface StellarEventDto { - eventId: string - txHash?: string - orderId?: string - destinationAddress?: string - amount?: string - memo?: string - payload?: Record -} diff --git a/legacy/nest-backend/src/payments/entities/payment-event.entity.ts b/legacy/nest-backend/src/payments/entities/payment-event.entity.ts deleted file mode 100644 index ed171d9..0000000 --- a/legacy/nest-backend/src/payments/entities/payment-event.entity.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - Index, - PrimaryGeneratedColumn, - Unique, - UpdateDateColumn, -} from "typeorm" - -export type PaymentEventStatus = "received" | "processing" | "processed" | "failed" - -@Entity({ name: "payment_events" }) -@Unique(["provider", "eventId"]) -export class PaymentEvent { - @PrimaryGeneratedColumn("uuid") - id: string - - @Index() - @Column() - provider: string - - @Index() - @Column() - eventId: string - - @Index() - @Column({ nullable: true }) - txHash?: string - - @Column({ nullable: true }) - orderId?: string - - @Column({ nullable: true }) - destinationAddress?: string - - @Column({ nullable: true }) - amount?: string - - @Column({ nullable: true }) - memo?: string - - @Column({ type: "simple-json", nullable: true }) - payload?: Record - - @Column({ type: "varchar", default: "received" }) - status: PaymentEventStatus - - @Column({ type: "int", default: 0 }) - retries: number - - @Column({ type: "timestamp", nullable: true }) - nextAttemptAt?: Date - - @Column({ type: "text", nullable: true }) - lastError?: string - - @Column({ type: "timestamp", nullable: true }) - processedAt?: Date - - @CreateDateColumn() - createdAt: Date - - @UpdateDateColumn() - updatedAt: Date -} diff --git a/legacy/nest-backend/src/payments/entities/payment-stream-cursor.entity.ts b/legacy/nest-backend/src/payments/entities/payment-stream-cursor.entity.ts deleted file mode 100644 index ce990fb..0000000 --- a/legacy/nest-backend/src/payments/entities/payment-stream-cursor.entity.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, Unique, UpdateDateColumn } from "typeorm" - -@Entity({ name: "payment_stream_cursors" }) -@Unique(["provider", "streamKey"]) -export class PaymentStreamCursor { - @PrimaryGeneratedColumn("uuid") - id: string - - @Column() - provider: string - - @Column() - streamKey: string - - @Column({ type: "text", nullable: true }) - cursor?: string - - @CreateDateColumn() - createdAt: Date - - @UpdateDateColumn() - updatedAt: Date -} diff --git a/legacy/nest-backend/src/payments/entities/payment-transaction.entity.ts b/legacy/nest-backend/src/payments/entities/payment-transaction.entity.ts deleted file mode 100644 index 97e7884..0000000 --- a/legacy/nest-backend/src/payments/entities/payment-transaction.entity.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - Index, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "typeorm" - -export type PaymentTransactionStatus = "submitted" | "pending" | "confirmed" | "failed" - -@Entity({ name: "payment_transactions" }) -export class PaymentTransaction { - @PrimaryGeneratedColumn("uuid") - id: string - - @Column() - provider: string - - @Index({ unique: true }) - @Column() - txHash: string - - @Index() - @Column({ nullable: true }) - orderId?: string - - @Column({ nullable: true }) - destinationAddress?: string - - @Column({ nullable: true }) - asset?: string - - @Column({ nullable: true }) - memo?: string - - @Column({ type: "decimal", precision: 20, scale: 7, nullable: true }) - amount?: string - - @Column({ type: "varchar", default: "pending" }) - status: PaymentTransactionStatus - - @Column({ nullable: true }) - ledger?: string - - @Column({ type: "int", default: 0 }) - retries: number - - @Column({ type: "text", nullable: true }) - failureReason?: string - - @Column({ type: "timestamp", nullable: true }) - lastCheckedAt?: Date - - @CreateDateColumn() - createdAt: Date - - @UpdateDateColumn() - updatedAt: Date -} diff --git a/legacy/nest-backend/src/payments/interfaces/payment-provider.interface.ts b/legacy/nest-backend/src/payments/interfaces/payment-provider.interface.ts deleted file mode 100644 index 05bb3a8..0000000 --- a/legacy/nest-backend/src/payments/interfaces/payment-provider.interface.ts +++ /dev/null @@ -1,63 +0,0 @@ -export type PaymentProviderId = "stellar" - -export type PaymentStatus = - | "created" - | "submitted" - | "pending" - | "confirmed" - | "failed" - -export interface CreatePaymentIntentInput { - orderId: string - amount: string - currency: string - destinationAddress?: string -} - -export interface PaymentIntent { - provider: PaymentProviderId - reference: string - amount: string - currency: string - destinationAddress: string - memo?: string -} - -export interface SubmitPaymentInput { - signedPayload: string - orderId?: string - amount?: string - destinationAddress?: string - memo?: string - asset?: string -} - -export interface SubmittedPayment { - provider: PaymentProviderId - txHash: string - status: PaymentStatus -} - -export interface VerifyPaymentInput { - txHash: string - orderId: string - expectedAmount: string - expectedDestinationAddress: string -} - -export interface VerifiedPayment { - provider: PaymentProviderId - txHash: string - status: PaymentStatus - confirmed: boolean - ledger?: string -} - -export interface PaymentProvider { - readonly id: PaymentProviderId - - createIntent(input: CreatePaymentIntentInput): Promise - submit(input: SubmitPaymentInput): Promise - verify(input: VerifyPaymentInput): Promise - getStatus(txHash: string): Promise -} diff --git a/legacy/nest-backend/src/payments/payment-events.service.ts b/legacy/nest-backend/src/payments/payment-events.service.ts deleted file mode 100644 index 8e12a50..0000000 --- a/legacy/nest-backend/src/payments/payment-events.service.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Injectable } from "@nestjs/common" -import { InjectRepository } from "@nestjs/typeorm" -import { LessThanOrEqual, Repository } from "typeorm" -import { type PaymentEventStatus, PaymentEvent } from "./entities/payment-event.entity" - -interface EnqueuePaymentEventInput { - provider: string - eventId: string - txHash?: string - orderId?: string - destinationAddress?: string - amount?: string - memo?: string - payload?: Record -} - -@Injectable() -export class PaymentEventsService { - constructor( - @InjectRepository(PaymentEvent) - private readonly repository: Repository, - ) {} - - async enqueue(input: EnqueuePaymentEventInput): Promise<{ event: PaymentEvent; created: boolean }> { - const existing = await this.repository.findOne({ - where: { - provider: input.provider, - eventId: input.eventId, - }, - }) - - if (existing) { - return { event: existing, created: false } - } - - const created = this.repository.create({ - provider: input.provider, - eventId: input.eventId, - txHash: input.txHash, - orderId: input.orderId, - destinationAddress: input.destinationAddress, - amount: input.amount, - memo: input.memo, - payload: input.payload, - status: "received", - retries: 0, - nextAttemptAt: new Date(), - }) - - return { event: await this.repository.save(created), created: true } - } - - findPending(limit: number): Promise { - const now = new Date() - return this.repository.find({ - where: [ - { status: "received", nextAttemptAt: LessThanOrEqual(now) }, - { status: "failed", nextAttemptAt: LessThanOrEqual(now) }, - ], - order: { createdAt: "ASC" }, - take: limit, - }) - } - - async markProcessing(event: PaymentEvent): Promise { - event.status = "processing" - return this.repository.save(event) - } - - async markProcessed(event: PaymentEvent): Promise { - event.status = "processed" - event.processedAt = new Date() - event.lastError = undefined - event.nextAttemptAt = undefined - return this.repository.save(event) - } - - async markFailed(event: PaymentEvent, lastError: string, nextAttemptAt: Date): Promise { - event.status = "failed" - event.retries += 1 - event.lastError = lastError - event.nextAttemptAt = nextAttemptAt - return this.repository.save(event) - } - - async markRequeued(event: PaymentEvent): Promise { - event.status = "received" - event.nextAttemptAt = new Date() - return this.repository.save(event) - } - - async updateStatusByTxHash( - provider: string, - txHash: string, - status: PaymentEventStatus, - ): Promise { - await this.repository.update( - { - provider, - txHash, - }, - { - status, - processedAt: status === "processed" ? new Date() : undefined, - }, - ) - } -} diff --git a/legacy/nest-backend/src/payments/payment-stream-cursors.service.ts b/legacy/nest-backend/src/payments/payment-stream-cursors.service.ts deleted file mode 100644 index b266843..0000000 --- a/legacy/nest-backend/src/payments/payment-stream-cursors.service.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Injectable } from "@nestjs/common" -import { InjectRepository } from "@nestjs/typeorm" -import { Repository } from "typeorm" -import { PaymentStreamCursor } from "./entities/payment-stream-cursor.entity" - -@Injectable() -export class PaymentStreamCursorsService { - constructor( - @InjectRepository(PaymentStreamCursor) - private readonly repository: Repository, - ) {} - - async getCursor(provider: string, streamKey: string): Promise { - const record = await this.repository.findOne({ - where: { provider, streamKey }, - }) - - return record?.cursor - } - - async upsertCursor(provider: string, streamKey: string, cursor: string): Promise { - const existing = await this.repository.findOne({ - where: { provider, streamKey }, - }) - - if (!existing) { - const created = this.repository.create({ - provider, - streamKey, - cursor, - }) - return this.repository.save(created) - } - - existing.cursor = cursor - return this.repository.save(existing) - } -} diff --git a/legacy/nest-backend/src/payments/payment-transactions.service.ts b/legacy/nest-backend/src/payments/payment-transactions.service.ts deleted file mode 100644 index 024e333..0000000 --- a/legacy/nest-backend/src/payments/payment-transactions.service.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { Injectable } from "@nestjs/common" -import { InjectRepository } from "@nestjs/typeorm" -import { In, IsNull, LessThanOrEqual, Repository } from "typeorm" -import { - type PaymentTransactionStatus, - PaymentTransaction, -} from "./entities/payment-transaction.entity" - -interface UpsertPaymentTransactionInput { - provider: string - txHash: string - orderId?: string - destinationAddress?: string - asset?: string - memo?: string - amount?: string - status?: PaymentTransactionStatus - ledger?: string - failureReason?: string - incrementRetries?: boolean -} - -interface ReconciliationCandidatesOptions { - limit: number - staleBefore: Date -} - -@Injectable() -export class PaymentTransactionsService { - constructor( - @InjectRepository(PaymentTransaction) - private readonly repository: Repository, - ) {} - - async upsertByTxHash(input: UpsertPaymentTransactionInput): Promise { - const existing = await this.repository.findOne({ where: { txHash: input.txHash } }) - - if (!existing) { - const created = this.repository.create({ - provider: input.provider, - txHash: input.txHash, - orderId: input.orderId, - destinationAddress: input.destinationAddress, - asset: input.asset, - memo: input.memo, - amount: input.amount, - status: input.status ?? "pending", - ledger: input.ledger, - failureReason: input.failureReason, - retries: input.incrementRetries ? 1 : 0, - lastCheckedAt: new Date(), - }) - return this.repository.save(created) - } - - existing.provider = input.provider ?? existing.provider - existing.orderId = input.orderId ?? existing.orderId - existing.destinationAddress = input.destinationAddress ?? existing.destinationAddress - existing.asset = input.asset ?? existing.asset - existing.memo = input.memo ?? existing.memo - existing.amount = input.amount ?? existing.amount - existing.status = this.resolveNextStatus(existing.status, input.status) - existing.ledger = input.ledger ?? existing.ledger - existing.failureReason = input.failureReason ?? existing.failureReason - existing.lastCheckedAt = new Date() - if (input.incrementRetries) { - existing.retries += 1 - } - - return this.repository.save(existing) - } - - findReconciliationCandidates( - options: ReconciliationCandidatesOptions, - ): Promise { - return this.repository.find({ - where: [ - { - status: In(["pending", "submitted"]), - lastCheckedAt: IsNull(), - }, - { - status: In(["pending", "submitted"]), - lastCheckedAt: LessThanOrEqual(options.staleBefore), - }, - ], - order: { updatedAt: "ASC" }, - take: options.limit, - }) - } - - findByOrderId(orderId: string): Promise { - return this.repository.find({ - where: { orderId }, - order: { createdAt: "DESC" }, - }) - } - - findByTxHash(txHash: string): Promise { - return this.repository.findOne({ where: { txHash } }) - } - - private resolveNextStatus( - current: PaymentTransactionStatus, - incoming?: PaymentTransactionStatus, - ): PaymentTransactionStatus { - if (!incoming) { - return current - } - - if (current === "confirmed") { - return "confirmed" - } - - if (incoming === "confirmed") { - return "confirmed" - } - - if (current === "failed" && incoming !== "confirmed") { - return "failed" - } - - return incoming - } -} diff --git a/legacy/nest-backend/src/payments/payments-events-processor.service.ts b/legacy/nest-backend/src/payments/payments-events-processor.service.ts deleted file mode 100644 index ad20ec4..0000000 --- a/legacy/nest-backend/src/payments/payments-events-processor.service.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common" -import { ConfigService } from "@nestjs/config" -import { PaymentEventsService } from "./payment-events.service" -import { PaymentTransactionsService } from "./payment-transactions.service" -import { PaymentsService } from "./payments.service" - -@Injectable() -export class PaymentsEventsProcessorService implements OnModuleInit, OnModuleDestroy { - private readonly logger = new Logger(PaymentsEventsProcessorService.name) - private timer?: NodeJS.Timeout - private running = false - - constructor( - private readonly configService: ConfigService, - private readonly paymentsService: PaymentsService, - private readonly paymentEventsService: PaymentEventsService, - private readonly paymentTransactionsService: PaymentTransactionsService, - ) {} - - onModuleInit(): void { - const intervalMs = this.getNumberConfig("PAYMENT_EVENT_PROCESSOR_INTERVAL_MS", 5_000) - this.timer = setInterval(() => { - void this.processPendingEvents() - }, intervalMs) - } - - onModuleDestroy(): void { - if (this.timer) { - clearInterval(this.timer) - } - } - - async processPendingEvents(): Promise { - if (this.running || this.paymentsService.providerId !== "stellar") { - return - } - - this.running = true - const batchSize = this.getNumberConfig("PAYMENT_EVENT_PROCESSOR_BATCH_SIZE", 25) - const candidates = await this.paymentEventsService.findPending(batchSize) - - try { - for (const event of candidates) { - await this.paymentEventsService.markProcessing(event) - - try { - if (!event.txHash || !event.destinationAddress || !event.amount) { - await this.paymentEventsService.markProcessed(event) - continue - } - - const existingTx = await this.paymentTransactionsService.findByTxHash(event.txHash) - if (existingTx?.status === "confirmed") { - await this.paymentEventsService.markProcessed(event) - continue - } - - const orderId = event.orderId ?? event.memo - if (!orderId) { - await this.paymentEventsService.markProcessed(event) - continue - } - - const verification = await this.paymentsService.verify({ - txHash: event.txHash, - orderId, - expectedAmount: event.amount, - expectedDestinationAddress: event.destinationAddress, - }) - - if (!verification.confirmed) { - throw new Error("transaction not confirmed yet") - } - - await this.paymentEventsService.markProcessed(event) - } catch (error) { - const lastError = error instanceof Error ? error.message : "unknown event processing failure" - const nextAttemptAt = this.computeBackoffDate(event.retries + 1) - await this.paymentEventsService.markFailed(event, lastError, nextAttemptAt) - } - } - } catch (error) { - const message = error instanceof Error ? error.message : "unknown processor failure" - this.logger.error(`Payment event processing batch failed: ${message}`) - } finally { - this.running = false - } - } - - private computeBackoffDate(retries: number): Date { - const baseMs = this.getNumberConfig("PAYMENT_EVENT_RETRY_BASE_MS", 5_000) - const maxMs = this.getNumberConfig("PAYMENT_EVENT_RETRY_MAX_MS", 300_000) - const backoffMs = Math.min(maxMs, baseMs * 2 ** Math.max(0, retries - 1)) - return new Date(Date.now() + backoffMs) - } - - private getNumberConfig(key: string, fallback: number): number { - const raw = this.configService.get(key) - if (raw === undefined || raw === null || raw === "") { - return fallback - } - const parsed = Number(raw) - if (!Number.isFinite(parsed) || parsed <= 0) { - return fallback - } - return parsed - } -} diff --git a/legacy/nest-backend/src/payments/payments-listener.service.ts b/legacy/nest-backend/src/payments/payments-listener.service.ts deleted file mode 100644 index 41e9a56..0000000 --- a/legacy/nest-backend/src/payments/payments-listener.service.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common" -import { ConfigService } from "@nestjs/config" -import { PaymentEventsService } from "./payment-events.service" -import { PaymentsService } from "./payments.service" -import { PaymentStreamCursorsService } from "./payment-stream-cursors.service" - -interface StellarPaymentsPage { - _embedded?: { - records?: Array<{ - paging_token?: string - transaction_hash?: string - amount?: string - to?: string - memo?: string - transaction_successful?: boolean - }> - } -} - -@Injectable() -export class PaymentsListenerService implements OnModuleInit, OnModuleDestroy { - private readonly logger = new Logger(PaymentsListenerService.name) - private timer?: NodeJS.Timeout - private running = false - - constructor( - private readonly configService: ConfigService, - private readonly paymentsService: PaymentsService, - private readonly paymentEventsService: PaymentEventsService, - private readonly paymentStreamCursorsService: PaymentStreamCursorsService, - ) {} - - onModuleInit(): void { - const intervalMs = this.getNumberConfig("PAYMENT_LISTENER_INTERVAL_MS", 20_000) - this.timer = setInterval(() => { - void this.pollStellarPayments() - }, intervalMs) - } - - onModuleDestroy(): void { - if (this.timer) { - clearInterval(this.timer) - } - } - - async pollStellarPayments(): Promise { - if (this.running || this.paymentsService.providerId !== "stellar") { - return - } - - const destinationAddress = this.configService.get("STELLAR_DESTINATION_ADDRESS") - if (!destinationAddress) { - return - } - - this.running = true - - try { - const streamKey = `account:${destinationAddress}` - const initialCursor = this.configService.get("STELLAR_PAYMENTS_START_CURSOR") ?? "now" - const currentCursor = - (await this.paymentStreamCursorsService.getCursor("stellar", streamKey)) ?? initialCursor - const limit = this.getNumberConfig("PAYMENT_LISTENER_BATCH_SIZE", 50) - const horizonUrl = this.readHorizonUrl() - - const url = `${horizonUrl}/accounts/${encodeURIComponent( - destinationAddress, - )}/payments?order=asc&limit=${limit}&cursor=${encodeURIComponent(currentCursor)}` - const page = await this.requestJson(url) - const records = page._embedded?.records ?? [] - - let latestCursor = currentCursor - for (const record of records) { - if (!record.paging_token || !record.transaction_hash) { - continue - } - - const eventId = `stellar:${record.paging_token}` - await this.paymentEventsService.enqueue({ - provider: "stellar", - eventId, - txHash: record.transaction_hash, - destinationAddress: record.to, - amount: record.amount, - memo: record.memo, - payload: record as unknown as Record, - }) - - latestCursor = record.paging_token - } - - if (latestCursor !== currentCursor) { - await this.paymentStreamCursorsService.upsertCursor("stellar", streamKey, latestCursor) - } - } catch (error) { - const message = error instanceof Error ? error.message : "unknown listener failure" - this.logger.warn(`Stellar payment listener poll failed: ${message}`) - } finally { - this.running = false - } - } - - private readHorizonUrl(): string { - const configuredUrl = this.configService.get("STELLAR_HORIZON_URL") - if (configuredUrl?.trim()) { - return configuredUrl - } - - const network = this.configService.get("STELLAR_NETWORK") ?? "testnet" - return network === "public" - ? "https://horizon.stellar.org" - : "https://horizon-testnet.stellar.org" - } - - private getNumberConfig(key: string, fallback: number): number { - const raw = this.configService.get(key) - if (raw === undefined || raw === null || raw === "") { - return fallback - } - const parsed = Number(raw) - if (!Number.isFinite(parsed) || parsed <= 0) { - return fallback - } - return parsed - } - - private async requestJson(url: string): Promise { - const response = await fetch(url) - if (!response.ok) { - throw new Error(`Horizon request failed: ${response.status}`) - } - return (await response.json()) as T - } -} diff --git a/legacy/nest-backend/src/payments/payments-reconciliation.service.ts b/legacy/nest-backend/src/payments/payments-reconciliation.service.ts deleted file mode 100644 index 78c971c..0000000 --- a/legacy/nest-backend/src/payments/payments-reconciliation.service.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from "@nestjs/common" -import { ConfigService } from "@nestjs/config" -import { PaymentTransactionsService } from "./payment-transactions.service" -import { PaymentsService } from "./payments.service" - -@Injectable() -export class PaymentsReconciliationService implements OnModuleInit, OnModuleDestroy { - private readonly logger = new Logger(PaymentsReconciliationService.name) - private timer?: NodeJS.Timeout - private running = false - - constructor( - private readonly configService: ConfigService, - private readonly paymentsService: PaymentsService, - private readonly paymentTransactionsService: PaymentTransactionsService, - ) {} - - onModuleInit(): void { - const intervalMs = this.getNumberConfig("PAYMENT_RECONCILIATION_INTERVAL_MS", 60_000) - this.timer = setInterval(() => { - void this.reconcilePendingTransactions() - }, intervalMs) - } - - onModuleDestroy(): void { - if (this.timer) { - clearInterval(this.timer) - } - } - - async reconcilePendingTransactions(): Promise { - if (this.running) { - return - } - - if (this.paymentsService.providerId !== "stellar") { - return - } - - this.running = true - const batchSize = this.getNumberConfig("PAYMENT_RECONCILIATION_BATCH_SIZE", 50) - const staleThresholdMs = this.getNumberConfig("PAYMENT_RECONCILIATION_STALE_MS", 30_000) - const staleBefore = new Date(Date.now() - staleThresholdMs) - - try { - const candidates = await this.paymentTransactionsService.findReconciliationCandidates({ - limit: batchSize, - staleBefore, - }) - - for (const tx of candidates) { - try { - const status = await this.paymentsService.getStatus(tx.txHash) - await this.paymentTransactionsService.upsertByTxHash({ - provider: tx.provider, - txHash: tx.txHash, - status, - orderId: tx.orderId, - destinationAddress: tx.destinationAddress, - amount: tx.amount, - memo: tx.memo, - asset: tx.asset, - ledger: tx.ledger, - }) - } catch (error) { - const reason = error instanceof Error ? error.message : "Unknown reconciliation failure" - await this.paymentTransactionsService.upsertByTxHash({ - provider: tx.provider, - txHash: tx.txHash, - status: "pending", - orderId: tx.orderId, - destinationAddress: tx.destinationAddress, - amount: tx.amount, - memo: tx.memo, - asset: tx.asset, - failureReason: reason, - incrementRetries: true, - }) - } - } - } catch (error) { - const message = error instanceof Error ? error.message : "Unknown batch reconciliation failure" - this.logger.error(`Payment reconciliation batch failed: ${message}`) - } finally { - this.running = false - } - } - - private getNumberConfig(key: string, fallback: number): number { - const raw = this.configService.get(key) - if (raw === undefined || raw === null || raw === "") { - return fallback - } - - const parsed = Number(raw) - if (!Number.isFinite(parsed) || parsed <= 0) { - return fallback - } - - return parsed - } -} diff --git a/legacy/nest-backend/src/payments/payments-webhook.controller.ts b/legacy/nest-backend/src/payments/payments-webhook.controller.ts deleted file mode 100644 index b23ace4..0000000 --- a/legacy/nest-backend/src/payments/payments-webhook.controller.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { BadRequestException, Body, Controller, Post } from "@nestjs/common" -import { Public } from "../auth/decorators/public.decorator" -import type { StellarEventDto } from "./dto/stellar-event.dto" -import { PaymentEventsService } from "./payment-events.service" - -@Controller("payments/webhooks") -export class PaymentsWebhookController { - constructor(private readonly paymentEventsService: PaymentEventsService) {} - - @Public() - @Post("stellar") - async receiveStellarEvent(@Body() body: StellarEventDto) { - if (!body.eventId?.trim()) { - throw new BadRequestException("eventId is required") - } - - const event = await this.paymentEventsService.enqueue({ - provider: "stellar", - eventId: body.eventId, - txHash: body.txHash, - orderId: body.orderId, - destinationAddress: body.destinationAddress, - amount: body.amount, - memo: body.memo, - payload: body.payload, - }) - - return { - accepted: true, - duplicate: !event.created, - eventId: body.eventId, - } - } -} diff --git a/legacy/nest-backend/src/payments/payments.module.ts b/legacy/nest-backend/src/payments/payments.module.ts deleted file mode 100644 index d6b4b91..0000000 --- a/legacy/nest-backend/src/payments/payments.module.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Module } from "@nestjs/common" -import { TypeOrmModule } from "@nestjs/typeorm" -import { PAYMENT_PROVIDER } from "./constants/payment.constants" -import { PaymentEvent } from "./entities/payment-event.entity" -import { PaymentStreamCursor } from "./entities/payment-stream-cursor.entity" -import { PaymentTransaction } from "./entities/payment-transaction.entity" -import { PaymentEventsService } from "./payment-events.service" -import { PaymentsEventsProcessorService } from "./payments-events-processor.service" -import { PaymentsListenerService } from "./payments-listener.service" -import { PaymentsWebhookController } from "./payments-webhook.controller" -import { PaymentTransactionsService } from "./payment-transactions.service" -import { PaymentStreamCursorsService } from "./payment-stream-cursors.service" -import { PaymentsReconciliationService } from "./payments-reconciliation.service" -import { StellarPaymentProvider } from "./providers/stellar-payment.provider" -import { PaymentsService } from "./payments.service" - -@Module({ - imports: [TypeOrmModule.forFeature([PaymentTransaction, PaymentEvent, PaymentStreamCursor])], - providers: [ - StellarPaymentProvider, - { - provide: PAYMENT_PROVIDER, - useExisting: StellarPaymentProvider, - }, - PaymentTransactionsService, - PaymentEventsService, - PaymentStreamCursorsService, - PaymentsService, - PaymentsReconciliationService, - PaymentsListenerService, - PaymentsEventsProcessorService, - ], - controllers: [PaymentsWebhookController], - exports: [PAYMENT_PROVIDER, PaymentsService, PaymentTransactionsService], -}) -export class PaymentsModule {} diff --git a/legacy/nest-backend/src/payments/payments.service.ts b/legacy/nest-backend/src/payments/payments.service.ts deleted file mode 100644 index 2e64057..0000000 --- a/legacy/nest-backend/src/payments/payments.service.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Inject, Injectable } from "@nestjs/common" -import { PAYMENT_PROVIDER } from "./constants/payment.constants" -import type { - CreatePaymentIntentInput, - PaymentProvider, - PaymentStatus, - PaymentIntent, - SubmitPaymentInput, - SubmittedPayment, - VerifyPaymentInput, - VerifiedPayment, -} from "./interfaces/payment-provider.interface" -import { PaymentTransactionsService } from "./payment-transactions.service" - -@Injectable() -export class PaymentsService { - constructor( - @Inject(PAYMENT_PROVIDER) private readonly provider: PaymentProvider, - private readonly paymentTransactionsService: PaymentTransactionsService, - ) {} - - get providerId() { - return this.provider.id - } - - createIntent(input: CreatePaymentIntentInput): Promise { - return this.provider.createIntent(input) - } - - async submit(input: SubmitPaymentInput): Promise { - const submitted = await this.provider.submit(input) - - await this.paymentTransactionsService.upsertByTxHash({ - provider: submitted.provider, - txHash: submitted.txHash, - orderId: input.orderId, - amount: input.amount, - destinationAddress: input.destinationAddress, - memo: input.memo, - asset: input.asset, - status: submitted.status === "failed" ? "failed" : "submitted", - }) - - return submitted - } - - async verify(input: VerifyPaymentInput): Promise { - const verified = await this.provider.verify(input) - - await this.paymentTransactionsService.upsertByTxHash({ - provider: verified.provider, - txHash: verified.txHash, - orderId: input.orderId, - amount: input.expectedAmount, - destinationAddress: input.expectedDestinationAddress, - memo: input.orderId, - status: verified.confirmed ? "confirmed" : verified.status === "failed" ? "failed" : "pending", - ledger: verified.ledger, - }) - - return verified - } - - async getStatus(txHash: string): Promise { - const status = await this.provider.getStatus(txHash) - await this.paymentTransactionsService.upsertByTxHash({ - provider: this.provider.id, - txHash, - status: status === "failed" ? "failed" : status === "confirmed" ? "confirmed" : "pending", - }) - return status - } -} diff --git a/legacy/nest-backend/src/payments/providers/stellar-payment.provider.ts b/legacy/nest-backend/src/payments/providers/stellar-payment.provider.ts deleted file mode 100644 index 6aa7366..0000000 --- a/legacy/nest-backend/src/payments/providers/stellar-payment.provider.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { - BadRequestException, - Injectable, - ServiceUnavailableException, -} from "@nestjs/common" -import { ConfigService } from "@nestjs/config" -import type { - CreatePaymentIntentInput, - PaymentProvider, - PaymentProviderId, - PaymentStatus, - PaymentIntent, - SubmitPaymentInput, - SubmittedPayment, - VerifyPaymentInput, - VerifiedPayment, -} from "../interfaces/payment-provider.interface" - -@Injectable() -export class StellarPaymentProvider implements PaymentProvider { - readonly id: PaymentProviderId = "stellar" - - private readonly horizonUrl: string - private readonly defaultDestinationAddress?: string - private readonly network: string - - constructor(private readonly configService: ConfigService) { - this.network = this.configService.get("STELLAR_NETWORK") ?? "testnet" - this.horizonUrl = this.readHorizonUrl() - this.defaultDestinationAddress = this.configService.get("STELLAR_DESTINATION_ADDRESS") - } - - async createIntent(input: CreatePaymentIntentInput): Promise { - const destinationAddress = input.destinationAddress || this.defaultDestinationAddress - - if (!destinationAddress) { - throw new BadRequestException( - "Missing Stellar destination address in request and STELLAR_DESTINATION_ADDRESS env var", - ) - } - - return { - provider: this.id, - reference: `stellar-${input.orderId}-${Date.now()}`, - amount: this.normalizeAmount(input.amount), - currency: input.currency, - destinationAddress, - memo: input.orderId, - } - } - - async submit(input: SubmitPaymentInput): Promise { - if (!input.signedPayload?.trim()) { - throw new BadRequestException("signedPayload is required") - } - - const response = await this.requestJson( - `${this.horizonUrl}/transactions`, - { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: new URLSearchParams({ tx: input.signedPayload }).toString(), - }, - ) - - return { - provider: this.id, - txHash: response.hash, - status: response.successful ? "submitted" : "failed", - } - } - - async verify(input: VerifyPaymentInput): Promise { - const tx = await this.requestJson( - `${this.horizonUrl}/transactions/${encodeURIComponent(input.txHash)}`, - ) - const operations = await this.requestJson( - `${this.horizonUrl}/transactions/${encodeURIComponent(input.txHash)}/operations?limit=200`, - ) - - const matchedPayment = operations?._embedded?.records?.find((operation) => { - if (operation.type !== "payment") { - return false - } - - if (!operation.to || !operation.amount) { - return false - } - - return ( - operation.to === input.expectedDestinationAddress && - this.normalizeAmount(operation.amount) === this.normalizeAmount(input.expectedAmount) - ) - }) - - const memoMatches = String(tx.memo ?? "") === input.orderId - const confirmed = Boolean(tx.successful && memoMatches && matchedPayment) - - return { - provider: this.id, - txHash: tx.hash, - status: tx.successful ? "confirmed" : "failed", - confirmed, - ledger: tx.ledger ? String(tx.ledger) : undefined, - } - } - - async getStatus(txHash: string): Promise { - if (!txHash?.trim()) { - throw new BadRequestException("txHash is required") - } - - try { - const tx = await this.requestJson( - `${this.horizonUrl}/transactions/${encodeURIComponent(txHash)}`, - ) - - if (tx.successful) { - return "confirmed" - } - - return "failed" - } catch (error) { - if (this.isNotFoundError(error)) { - return "pending" - } - - throw error - } - } - - private readHorizonUrl(): string { - const configuredUrl = this.configService.get("STELLAR_HORIZON_URL") - if (configuredUrl?.trim()) { - return configuredUrl - } - - return this.network === "public" - ? "https://horizon.stellar.org" - : "https://horizon-testnet.stellar.org" - } - - private normalizeAmount(rawAmount: string): string { - const parsed = Number(rawAmount) - if (!Number.isFinite(parsed) || parsed <= 0) { - throw new BadRequestException(`Invalid amount: ${rawAmount}`) - } - - return parsed.toFixed(7) - } - - private isNotFoundError(error: unknown): boolean { - return Boolean( - typeof error === "object" && - error && - "status" in error && - (error as { status?: number }).status === 404, - ) - } - - private async requestJson(url: string, init?: RequestInit): Promise { - let response: Response - - try { - response = await fetch(url, init) - } catch { - throw new ServiceUnavailableException("Stellar Horizon is unreachable") - } - - const payload = await response.json().catch(() => ({})) - - if (!response.ok) { - const message = - (payload as { detail?: string; extras?: { result_codes?: unknown } }).detail || - "Stellar Horizon request failed" - throw new HorizonRequestError(response.status, message) - } - - return payload as T - } -} - -class HorizonRequestError extends Error { - constructor(readonly status: number, message: string) { - super(message) - this.name = "HorizonRequestError" - } -} - -interface StellarSubmittedTransaction { - hash: string - successful: boolean -} - -interface StellarTransaction { - hash: string - successful: boolean - memo?: string - ledger?: number -} - -interface StellarPaymentOperation { - type: string - to?: string - amount?: string -} - -interface StellarOperationsPage { - _embedded?: { - records?: StellarPaymentOperation[] - } -} diff --git a/legacy/nest-backend/src/users/dto/create-user.dto.ts b/legacy/nest-backend/src/users/dto/create-user.dto.ts deleted file mode 100644 index 13cb9a0..0000000 --- a/legacy/nest-backend/src/users/dto/create-user.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IsEmail, IsEnum, IsNotEmpty, IsOptional, IsString } from "class-validator" -import { MusicProvider } from "@prisma/client" - -export class CreateUserDto { - @IsOptional() - @IsEmail() - email?: string - - @IsNotEmpty() - @IsString() - displayName: string - - @IsEnum(MusicProvider) - musicProvider: MusicProvider - - @IsNotEmpty() - @IsString() - musicId: string - - @IsNotEmpty() - @IsString() - accessToken: string - - @IsNotEmpty() - @IsString() - refreshToken: string -} diff --git a/legacy/nest-backend/src/users/dto/update-user.dto.ts b/legacy/nest-backend/src/users/dto/update-user.dto.ts deleted file mode 100644 index 6f7410d..0000000 --- a/legacy/nest-backend/src/users/dto/update-user.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from "@nestjs/mapped-types" -import { CreateUserDto } from "./create-user.dto" - -export class UpdateUserDto extends PartialType(CreateUserDto) {} diff --git a/legacy/nest-backend/src/users/entities/user.entity.ts b/legacy/nest-backend/src/users/entities/user.entity.ts deleted file mode 100644 index d529305..0000000 --- a/legacy/nest-backend/src/users/entities/user.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, Unique } from 'typeorm'; - -@Entity() -@Unique(['appleUserId']) -export class User { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column({ nullable: true }) - email?: string; - - @Column({ nullable: true }) - firstName?: string; - - @Column({ nullable: true }) - lastName?: string; - - @Column({ nullable: true }) - appleUserId?: string; - - @Column({ type: 'text', nullable: true }) - appleRefreshToken?: string; - - @Column({ type: 'text', nullable: true }) - musicUserToken?: string; - - @Column({ nullable: true }) - musicStorefrontId?: string; - - @Column({ type: 'timestamp', nullable: true }) - musicUserTokenExpiresAt?: Date; -} \ No newline at end of file diff --git a/legacy/nest-backend/src/users/users.controller.ts b/legacy/nest-backend/src/users/users.controller.ts deleted file mode 100644 index 6a91f44..0000000 --- a/legacy/nest-backend/src/users/users.controller.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Controller, Get, UseGuards } from "@nestjs/common" -import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard" -import type { User } from "./entities/user.entity" -import type { UsersService } from "./users.service" - -@Controller("users") -export class UsersController { - constructor(private readonly usersService: UsersService) {} - - @UseGuards(JwtAuthGuard) - @Get("me") - async getProfile(user: User) { - return user.toPublic() - } - - @UseGuards(JwtAuthGuard) - @Get() - async findAll() { - const users = await this.usersService.findAll() - return users.map((user) => user.toPublic()) - } -} diff --git a/legacy/nest-backend/src/users/users.module.ts b/legacy/nest-backend/src/users/users.module.ts deleted file mode 100644 index a198411..0000000 --- a/legacy/nest-backend/src/users/users.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from "@nestjs/common" -import { UsersService } from "./users.service" -import { UsersRepository } from "./users.repository" -import { UsersController } from "./users.controller" - -@Module({ - providers: [UsersService, UsersRepository], - exports: [UsersService], - controllers: [UsersController], -}) -export class UsersModule {} diff --git a/legacy/nest-backend/src/users/users.repository.ts b/legacy/nest-backend/src/users/users.repository.ts deleted file mode 100644 index 7280b0a..0000000 --- a/legacy/nest-backend/src/users/users.repository.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Injectable } from "@nestjs/common" -import type { PrismaService } from "../database/prisma.service" -import type { CreateUserDto } from "./dto/create-user.dto" -import type { UpdateUserDto } from "./dto/update-user.dto" -import { User } from "./entities/user.entity" - -@Injectable() -export class UsersRepository { - constructor(private prisma: PrismaService) {} - - async create(createUserDto: CreateUserDto): Promise { - const user = await this.prisma.user.create({ - data: createUserDto, - }) - return new User(user) - } - - async findAll(): Promise { - const users = await this.prisma.user.findMany() - return users.map((user) => new User(user)) - } - - async findOne(id: string): Promise { - const user = await this.prisma.user.findUnique({ - where: { id }, - }) - return user ? new User(user) : null - } - - async findByEmail(email: string): Promise { - const user = await this.prisma.user.findUnique({ - where: { email }, - }) - return user ? new User(user) : null - } - - async findByMusicId(musicId: string): Promise { - const user = await this.prisma.user.findUnique({ - where: { musicId }, - }) - return user ? new User(user) : null - } - - async update(id: string, updateUserDto: UpdateUserDto): Promise { - const user = await this.prisma.user.update({ - where: { id }, - data: updateUserDto, - }) - return new User(user) - } - - async remove(id: string): Promise { - const user = await this.prisma.user.delete({ - where: { id }, - }) - return new User(user) - } -} diff --git a/legacy/nest-backend/src/users/users.service.ts b/legacy/nest-backend/src/users/users.service.ts deleted file mode 100644 index 8d66f0c..0000000 --- a/legacy/nest-backend/src/users/users.service.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Injectable, NotFoundException, ConflictException } from "@nestjs/common" -import type { UsersRepository } from "./users.repository" -import type { CreateUserDto } from "./dto/create-user.dto" -import type { UpdateUserDto } from "./dto/update-user.dto" -import type { User } from "./entities/user.entity" - -@Injectable() -export class UsersService { - constructor(private readonly usersRepository: UsersRepository) {} - - async create(createUserDto: CreateUserDto): Promise { - // Check if user already exists with this email or musicId - if (createUserDto.email) { - const existingUserByEmail = await this.usersRepository.findByEmail(createUserDto.email) - if (existingUserByEmail) { - throw new ConflictException("User with this email already exists") - } - } - - const existingUserByMusicId = await this.usersRepository.findByMusicId(createUserDto.musicId) - if (existingUserByMusicId) { - throw new ConflictException("User with this music ID already exists") - } - - return this.usersRepository.create(createUserDto) - } - - async findAll(): Promise { - return this.usersRepository.findAll() - } - - async findOne(id: string): Promise { - const user = await this.usersRepository.findOne(id) - if (!user) { - throw new NotFoundException(`User with ID ${id} not found`) - } - return user - } - - async findByEmail(email: string): Promise { - return this.usersRepository.findByEmail(email) - } - - async findByMusicId(musicId: string): Promise { - return this.usersRepository.findByMusicId(musicId) - } - - async update(id: string, updateUserDto: UpdateUserDto): Promise { - const existingUser = await this.findOne(id) - - // Check for conflicts if updating email or musicId - if (updateUserDto.email && updateUserDto.email !== existingUser.email) { - const userWithEmail = await this.usersRepository.findByEmail(updateUserDto.email) - if (userWithEmail && userWithEmail.id !== id) { - throw new ConflictException("User with this email already exists") - } - } - - if (updateUserDto.musicId && updateUserDto.musicId !== existingUser.musicId) { - const userWithMusicId = await this.usersRepository.findByMusicId(updateUserDto.musicId) - if (userWithMusicId && userWithMusicId.id !== id) { - throw new ConflictException("User with this music ID already exists") - } - } - - return this.usersRepository.update(id, updateUserDto) - } - - async remove(id: string): Promise { - await this.findOne(id) // This will throw if user doesn't exist - return this.usersRepository.remove(id) - } -} diff --git a/legacy/nest-backend/test/app.e2e-spec.ts b/legacy/nest-backend/test/app.e2e-spec.ts deleted file mode 100644 index 50cda62..0000000 --- a/legacy/nest-backend/test/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; -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!'); - }); -}); diff --git a/legacy/nest-backend/test/jest-e2e.json b/legacy/nest-backend/test/jest-e2e.json deleted file mode 100644 index e9d912f..0000000 --- a/legacy/nest-backend/test/jest-e2e.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - } -} diff --git a/legacy/nest-backend/tsconfig.build.json b/legacy/nest-backend/tsconfig.build.json deleted file mode 100644 index 64f86c6..0000000 --- a/legacy/nest-backend/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/legacy/nest-backend/tsconfig.json b/legacy/nest-backend/tsconfig.json deleted file mode 100644 index 95f5641..0000000 --- a/legacy/nest-backend/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false - } -} diff --git a/mobile/.env.example b/mobile/.env.example deleted file mode 100644 index 25bfdc2..0000000 --- a/mobile/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -EXPO_PUBLIC_API_BASE_URL=http://localhost:5000 -EXPO_PUBLIC_DEMO_USER_ID=660000000000000000000001 diff --git a/mobile/.eslintrc.cjs b/mobile/.eslintrc.cjs deleted file mode 100644 index 464a1b9..0000000 --- a/mobile/.eslintrc.cjs +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - root: true, - extends: ["universe/native", "prettier"], -} diff --git a/mobile/README.md b/mobile/README.md deleted file mode 100644 index b2d81e4..0000000 --- a/mobile/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Discoverly Mobile - -Active mobile stack for contributors: - -- Expo -- React Native -- TypeScript -- Expo Router -- Zustand - -## Quick Start - -1. Copy `.env.example` to `.env` -2. Install dependencies: - -```bash -npm install -``` - -3. Start app: - -```bash -npm run start -``` - -## Notes - -- Deep link scheme is `discoverly`. -- EAS profiles are defined in `eas.json`. -- Discover screen now includes a food detail modal (bottom sheet). -- Modal actions trigger the same swipe API flow as main pass/like buttons. -- Discovery tab pulls from `GET /api/foods/discover`. -- Swipe actions post to `POST /api/swipe`. -- Feed prefetch starts when only 3 cards remain. -- `expo-image` is used for image caching/prefetch to reduce flicker. -- Do not hardcode secrets in source files. Keep sensitive values in EAS secrets - or CI environment variables. -- Only `EXPO_PUBLIC_*` variables should be exposed to client runtime code. diff --git a/mobile/app.json b/mobile/app.json deleted file mode 100644 index 64945eb..0000000 --- a/mobile/app.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "expo": { - "name": "Discoverly", - "slug": "discoverly", - "scheme": "discoverly", - "version": "0.1.0", - "orientation": "portrait", - "userInterfaceStyle": "light", - "assetBundlePatterns": [ - "**/*" - ], - "ios": { - "supportsTablet": false, - "bundleIdentifier": "com.discoverly.app" - }, - "android": { - "package": "com.discoverly.app", - "intentFilters": [ - { - "action": "VIEW", - "data": [ - { - "scheme": "discoverly" - } - ], - "category": [ - "BROWSABLE", - "DEFAULT" - ] - } - ] - }, - "web": { - "bundler": "metro" - }, - "plugins": [ - "expo-router" - ], - "experiments": { - "typedRoutes": true - } - } -} diff --git a/mobile/app/(auth)/_layout.tsx b/mobile/app/(auth)/_layout.tsx deleted file mode 100644 index 25fb542..0000000 --- a/mobile/app/(auth)/_layout.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Redirect, Stack } from "expo-router"; - -import { useAuthStore } from "../../src/store/useAuthStore"; - -export default function AuthLayout() { - const token = useAuthStore((state) => state.token); - - if (token) { - return ; - } - - return ( - - - - - ); -} diff --git a/mobile/app/(auth)/login.tsx b/mobile/app/(auth)/login.tsx deleted file mode 100644 index d476295..0000000 --- a/mobile/app/(auth)/login.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { Link, useRouter } from "expo-router"; -import { Controller, useForm } from "react-hook-form"; -import { View } from "react-native"; - -import { Button, Input, Typography } from "../../src/components"; -import { useAuthStore } from "../../src/store/useAuthStore"; -import { colors, spacing } from "../../src/theme/tokens"; -import { type LoginFormValues, loginSchema } from "../../src/validation/auth"; - -export default function LoginScreen() { - const router = useRouter(); - const login = useAuthStore((state) => state.login); - - const { - control, - handleSubmit, - formState: { errors, isSubmitting }, - } = useForm({ - resolver: zodResolver(loginSchema), - defaultValues: { - email: "", - password: "", - }, - mode: "onBlur", - }); - - const onSubmit = async ({ email }: LoginFormValues) => { - const token = `session_${email.toLowerCase()}`; - await login({ - token, - user: { - id: "660000000000000000000001", - email, - role: "user", - }, - }); - router.replace("/(tabs)/discover"); - }; - - return ( - - Welcome Back - - Sign in to continue matching with dishes around you. - - - ( - - )} - /> - - ( - - )} - /> - - - - - - - - -
-
-
-

Overview

-

Menu Builder

-
-
- - Wallet Connected -
-
- -
- - setToken(event.target.value)} - /> -
- -
-
-

{isEditing ? "Edit food item" : "Add food item"}

-
void onSubmit(event)}> -
- - setForm((prev) => ({ ...prev, name: event.target.value }))} - placeholder="Spicy Chicken Burger" - required - /> -
- -
- - setForm((prev) => ({ ...prev, price: event.target.value }))} - placeholder="12.99" - required - /> -
- -
- -