diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..06e60fa --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +NODE_ENV="" +DATABASE_URL="" +JWT_REFRESH_SECRET="" +JWT_SECRET="" +PORT="" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6367a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env +dist diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5339207 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Use Node.js as the base image +FROM node:20.14.0 + +# Set the working directory +WORKDIR /app + +# Copy package.json and package-lock.json +COPY package*.json ./ + +# Install dependencies +RUN yarn install + +# Copy the rest of the application code +COPY . . + +# Generate Prisma Client +RUN npm run db:init + +# Build the TypeScript project +RUN npm run build + +# Declare a build-time variable +ARG DATABASE_URL + +# Set it as an environment variable +ENV DATABASE_URL=${DATABASE_URL} + +# Run migrations +RUN npm run db:deploy:prod + +# Expose the application port +EXPOSE 3000 + +# Start the application +CMD ["node", "dist/server.js"] \ No newline at end of file diff --git a/USAGE.md b/USAGE.md new file mode 100644 index 0000000..94ee5e1 --- /dev/null +++ b/USAGE.md @@ -0,0 +1,111 @@ + + +# Paritie Skill Test โ€“ Wallet & Donation Service + +This project implements a simple wallet and donation API with built-in transaction safety, pagination, and authentication. + +--- + +## ๐Ÿš€ Features + +* **Default Wallet Balance**: Every user automatically gets a wallet with a starting balance of **100,000 units**. +* **Send Funds by Email**: Users can send funds to beneficiaries using their registered email address. +* **Automatic Transaction Rollback**: Built-in safety mechanism in the **create donation** endpoint ensures that if any part of the process fails, funds are **automatically reversed**. +* **Pagination Support**: All resource fetch endpoints (users, donations, wallets) include pagination for efficient data retrieval. +* **Authentication**: Secure login & signup flow. Every request to protected endpoints requires a valid JWT access token. + +--- + +## ๐Ÿ› ๏ธ Getting Started + +Follow these steps to set up the project locally: + +```bash +# 1. Clone the repository +git clone https://github.com/Dunsin-cyber/backend-test/tree/main + +# 2. Install dependencies +yarn install + +# 3. Copy environment variables +cp .env.example .env +``` + +### โš™๏ธ Environment Setup + +You need a PostgreSQL database connection string. +Create a database from any **Postgres provider** and copy the connection URL. +It should look like this: + +``` +postgresql://USER:PASSWORD@HOST:PORT/DATABASE +``` + +Set this value in your `.env` file as `DATABASE_URL`. +Also fill in other required values like: + +* `NODE_ENV` +* `JWT_SECRET` +* `PORT` etc + +--- + + + +## โ–ถ๏ธ Running the Project + +Start the development server with: + +```bash +yarn dev +``` + +Run the seed data +`yarn run db:seed` to create a paritie system account + +If everything is working, youโ€™ll see logs like this: + +```bash +[nodemon] starting `ts-node -r tsconfig-paths/register src/server.ts` +Application started with config Loaded upโœ… +Server running on port [PORT] +API documentation available at ๐Ÿ“๐Ÿ“š http://localhost:[PORT]/api-docs +``` + +Now you can open the documentation link in your browser to test the APIs. + +--- + +## ๐ŸŒ Deployed Service + +A hosted version of this service is available at: + +* **Backend URL** โ†’ [https://backend-test-21ij.onrender.com](https://backend-test-21ij.onrender.com) +* **API Docs** โ†’ http://localhost:[PORT]/api-docs + +โš ๏ธ **Note**: + +* The hosting and database are on **free plans** and may be slow or unavailable. +* The database linked to this project will **expire on September 13, 2025**. + +--- + +## ๐Ÿ”‘ Authentication + +Most endpoints are **protected**. To access them: + +1. **Create an account** or **login** to get an `accessToken`. +2. The token is valid for **15 minutes**. +3. In the API docs (`/api-docs`), click the **Authorize** button (top right). +4. Paste the token into the input field and click **Authorize**. +5. You can now access protected routes directly from the Swagger UI. + +--- + +## โœ… Summary + +* Every user starts with **100,000 units** in their wallet. +* Transactions are **safe with auto-reversal** on failure. +* **Pagination** makes large data queries efficient. +* Fully documented and easy to test using Swagger. + diff --git a/module-alias.js b/module-alias.js new file mode 100644 index 0000000..9951b90 --- /dev/null +++ b/module-alias.js @@ -0,0 +1,7 @@ +const moduleAlias = require("module-alias"); +const path = require("path"); + +const isProd = process.env.NODE_ENV === "production"; +const basePath = isProd ? "dist" : "src"; + +moduleAlias.addAlias("@", path.join(__dirname, basePath)); diff --git a/package.json b/package.json new file mode 100644 index 0000000..37cab2a --- /dev/null +++ b/package.json @@ -0,0 +1,71 @@ +{ + "name": "backend-test", + "version": "1.0.0", + "description": "backend-coding-exercise", + "main": "src/server.ts", + "repository": "https://github.com/Dunsin-cyber/backend-test.git", + "author": "Dunsin ", + "license": "MIT", + "private": false, + "scripts": { + "clean": "rimraf dist", + "prestart": "node dist/constants/index.js", + "dev": "nodemon --legacy-watch -r tsconfig-paths/register src/server.ts", + "build": "npm run clean && tsc && cp -r src/prisma/ dist/prisma/", + "db:deploy": "cd ./src && npx prisma migrate deploy", + "db:reset": "cd ./src && npx prisma migrate reset --force", + "db:migrate": "cd ./src && npx prisma migrate dev --name rename_field_to_transactionEntries", + "db:init": "cd ./src && npx prisma generate", + "start": "node dist/server.js", + "start:prod": "npm run db:deploy:prod && npm run start", + "db:deploy:prod": "cd ./dist && npx prisma migrate deploy", + "db:seed": "npx prisma db seed --schema ./src/prisma/schema.prisma" + }, + "prisma": { + "seed": "ts-node ./src/prisma/seed.ts" + }, + "_moduleAliases": { + "@": "./src" + }, + "dependencies": { + "@prisma/client": "^6.14.0", + "@prisma/extension-accelerate": "^2.0.2", + "bcryptjs": "^3.0.2", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "crypto": "^1.0.1", + "dotenv": "^16.4.7", + "express": "^4.21.2", + "express-session": "^1.18.1", + "jsonwebtoken": "^9.0.2", + "module-alias": "^2.2.3", + "morgan": "^1.10.0", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.1", + "tsconfig-paths": "^4.2.0", + "ulid": "^3.0.0", + "uuid": "^11.1.0" + }, + "devDependencies": { + "@types/cookie-parser": "^1.4.8", + "@types/cors": "^2.8.17", + "@types/express": "^5.0.0", + "@types/express-session": "^1.18.1", + "@types/jsonwebtoken": "^9.0.9", + "@types/module-alias": "^2.0.4", + "@types/morgan": "^1.9.9", + "@types/node": "^22.13.10", + "@types/swagger-jsdoc": "^6.0.4", + "@types/swagger-ui-express": "^4.1.8", + "@types/yamljs": "^0.2.34", + "nodemon": "^3.1.10", + "prisma": "^6.14.0", + "rimraf": "^6.0.1", + "ts-node": "^10.9.2", + "typescript": "^5.8.2" + }, + "engines": { + "node": "^22.14.0", + "yarn": "^1.22.19" + } +} \ No newline at end of file diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..126419d --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,5 @@ +node_modules +# Keep environment variables out of version control +.env + +/src/generated/prisma diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000..d1ebedc --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,39 @@ +import dotenv from 'dotenv'; +// import { CipherKey } from 'crypto'; +dotenv.config(); + + +// MAKES SURE THAT ALL VARIABLES ARE IN ENV BEFPRE APP STARTS +// ? DATABASE_URL won't be exported because the new format has a "postgres" prifix which +// ? doesnt parse wellif imported through this format +const requiredEnvVars = [ + 'NODE_ENV', + 'JWT_REFRESH_SECRET', + 'JWT_SECRET' +]; + +const missingEnvVars = requiredEnvVars.filter(envVar => !process.env[envVar]); + +if (missingEnvVars.length > 0) { + console.error(`โŒ Missing required environment variables: ${missingEnvVars.join(', ')}`); + process.exit(1); +} + + + +// ? SYSTEM CREDENTIALS +const NODE_ENV = process.env.NODE_ENV +const PORT = process.env.PORT || 3000; + +// ? JWT CREDENTIALS +const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET; +const JWT_SECRET = process.env.JWT_SECRET; + + +export const config = { + NODE_ENV, + PORT, + JWT_REFRESH_SECRET, + JWT_SECRET + +} \ No newline at end of file diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts new file mode 100644 index 0000000..a839333 --- /dev/null +++ b/src/controllers/auth.controller.ts @@ -0,0 +1,118 @@ +import { asyncHandler } from '@/middlewares/asyncHandler'; +import { ApiResponse } from '@/utils/ApiResponse'; +import { AppError } from '@/utils/AppError'; +import { NextFunction, Request, Response } from 'express'; +import { createUser, getUserByEmail, getUserPrivateFn } from '@/services/auth.service'; +import utils from '@/utils/index'; +import jwt from 'jsonwebtoken'; +import { config } from '@/constants'; + + +export const handleCreateAcc = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const validInput = utils.validateCreateUserInput(req.body); + if (!validInput) { + throw (new AppError("Invalid input data", 400)); + } + const { email, password, name } = req.body; + + + const emailExists = await getUserByEmail(email); + if (emailExists) { + throw new AppError("Email already exists", 409); + } + + + const user = await createUser({ password, name, email: utils.formatEmail(email) }); + if (!user) { + throw new AppError("Error creating an Account, please try again", 500); + } + + + const accessToken = jwt.sign( + { userId: user.id }, + config.JWT_SECRET!, + { expiresIn: "7d" } + ); + + const refreshToken = jwt.sign( + { userId: user.id }, + config.JWT_REFRESH_SECRET!, + { expiresIn: "7d" } + ); + + // Set refresh token as an HTTP-only cookie + res.cookie("refreshToken", refreshToken, { + httpOnly: true, + secure: config.NODE_ENV === "production", + sameSite: "strict", + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days + }); + + return res.status(200).json(new ApiResponse("success", { user, accessToken })); + +}) + + +export const handleLoginAcc = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + //TODO: check if both params are wrong too + const { email, password } = req.body + if (!utils.validPassword(password) || !utils.validEmail(email)) { + throw (new AppError("Invalid input data", 400)); + } + + const user = await getUserPrivateFn(utils.formatEmail(email)) + if (!user) { + throw new AppError("Check login Credentials", 404); + } + + const isMatch = await utils.decryptPassword(user.password, password); + if (!isMatch) { + throw new AppError("Invalid email or password", 400); + } + + const safeUser = await getUserByEmail(utils.formatEmail(email)) + + + const accessToken = jwt.sign( + { userId: user.id }, + config.JWT_SECRET!, + { expiresIn: "1d" } + ); + + const refreshToken = jwt.sign( + { userId: user.id }, + config.JWT_REFRESH_SECRET!, + { expiresIn: "7d" } + ); + + console.log(refreshToken); + // Set refresh token as an HTTP-only cookie + res.cookie("refreshToken", refreshToken, { + httpOnly: true, + secure: config.NODE_ENV === "production", + sameSite: "strict", + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days + }); + + return res.status(200).json(new ApiResponse("success", { user: safeUser, accessToken })); + +}) + + +export const handleRefreshToken = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const refreshToken = req.cookies.refreshToken; + if (!refreshToken) throw new AppError("Refresh token missing", 401); + const decoded = jwt.verify(refreshToken, config.JWT_REFRESH_SECRET!) as { userId: string };; + console.log(decoded) + const newAccessToken = jwt.sign({ userId: decoded.userId }, config.JWT_SECRET!, { expiresIn: "1d" }); + + res.status(200).json(new ApiResponse("success", { accessToken: newAccessToken })); + +}) + + + +export const handleLogout = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + res.clearCookie("refreshToken"); + res.status(200).json(new ApiResponse("success", "Logged out")); +}) \ No newline at end of file diff --git a/src/controllers/donation.controller.ts b/src/controllers/donation.controller.ts new file mode 100644 index 0000000..1695f41 --- /dev/null +++ b/src/controllers/donation.controller.ts @@ -0,0 +1,103 @@ +import { asyncHandler } from '@/middlewares/asyncHandler'; +import { ApiResponse } from '@/utils/ApiResponse'; +import { AppError } from '@/utils/AppError'; +import { NextFunction, Request, Response } from 'express'; +import { createDonation, donationsInPeriod, getDonationById } from '@/services/donation.service'; +import { getUserPrivateFn } from '@/services/auth.service'; +import { validate as uuidValidate } from 'uuid'; +import utils from '@/utils/index'; +import { User } from '@prisma/client'; + + + +export const handleCreateDonation = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + //TODO: check if both params are wrong too + const { amount, beneficiaryEmail, transactionPin } = req.body + if ((typeof amount != "number") || !utils.validEmail(beneficiaryEmail)) { + throw (new AppError("Invalid input data", 400)); + } + + const user = (req as Request & { user?: User }).user! + if (!user.transactionPIN) { + throw new AppError("Please set a Transaction PIN first", 400); + } + if (!transactionPin || !utils.validPIN(transactionPin)) { + throw (new AppError("Invalid transaction PIN", 401)); + } + + const beneficiary = await getUserPrivateFn(utils.formatEmail(beneficiaryEmail)); + + if (!beneficiary) { + throw new AppError("Beneficiary does not exist", 404); + } + + + + if (utils.formatEmail(beneficiaryEmail) === user.email) { + throw new AppError("You cannot Donate to Self", 401); + } + + const donation = await createDonation(user, beneficiary.id, amount, transactionPin); + + + return res.status(201).json(new ApiResponse("success", donation)); + +}) + +//! DEPRECATED TO FAVOUR handleFilterDonations +export const handleGetUserDonations = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { +}) + + +export const handleFilterDonations = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const user = (req as Request & { user?: User }).user! + + const { from, to, limit, page } = req.query; + let startDate + let endDate; + + if (from) { + startDate = utils.dataParser(from as string) + } + if (to) { + endDate = utils.dataParser(to as string) + } + + if ((startDate && !endDate) || (!startDate && endDate)) { + throw new AppError("There must be both start and end date", 401); + } + + const date = { start: startDate?.start, end: endDate?.end } + + + + const donations = await donationsInPeriod(user.id, date, page as string, limit as string); + + if (!donations) { + throw new AppError("No donations found in this period", 404); + + } + + + return res.status(200).json(new ApiResponse("success", donations)); +} +) + + +export const handleDonationDetails = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const donationId = req.params.id; + if (!donationId) { + throw new AppError("Donation ID is required", 400); + } + const validUUID = uuidValidate(donationId); + if (!validUUID) { + throw new AppError("Invalid donation ID format", 400); + } + const donation = await getDonationById(donationId); + + if (!donation) { + throw new AppError("Donation not found", 404); + } + + return res.status(200).json(new ApiResponse("success", donation)); +}); \ No newline at end of file diff --git a/src/controllers/transaction.controller.ts b/src/controllers/transaction.controller.ts new file mode 100644 index 0000000..e6886a3 --- /dev/null +++ b/src/controllers/transaction.controller.ts @@ -0,0 +1,84 @@ +import { asyncHandler } from "@/middlewares/asyncHandler"; +import { setTransactionPIN, getUserTransactions, getBalanceFromTxs, getATransaction } from "@/services/transaction.service"; +import utils from "@/utils"; +import { ApiResponse } from "@/utils/ApiResponse"; +import { AppError } from "@/utils/AppError"; +import { User } from "@prisma/client"; +import { NextFunction, Request, Response } from "express"; +import { validate as uuidValidate } from 'uuid'; + + + + + +export const handleCreateTxPIN = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const validInput = utils.validPIN(req.body.pin); + if (!validInput) { + throw (new AppError("Invalid pin format", 400)); + } + + const user = (req as Request & { user?: User }).user! + await setTransactionPIN(user.id, req.body.pin); + + + return res.status(201).json(new ApiResponse("success", "Pin created successfully")); + +}); + + +export const handleGetUserTransactions = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + + const user = (req as Request & { user?: User }).user! + + const { from, to, limit, page } = req.query; + let startDate + let endDate; + + if (from) { + startDate = utils.dataParser(from as string) + } + if (to) { + endDate = utils.dataParser(to as string) + } + + if ((startDate && !endDate) || (!startDate && endDate)) { + throw new AppError("There must be both start and end date", 401); + } + + const date = { start: startDate?.start, end: endDate?.end } + + + const txs = await getUserTransactions(user.id, date, page as string, limit as string); + + return res.status(200).json(new ApiResponse("success", txs)); + + +}) + +export const handleGetUserBalance = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + + const user = (req as Request & { user?: User }).user! + const txs = await getBalanceFromTxs(user.id); + + + return res.status(200).json(new ApiResponse("success", txs)); +}) + +export const handleGetATransaction = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + + const txId = req.params.txId + + const validTxId = uuidValidate(txId); + if (!validTxId) { + throw new AppError("Invalid transaction ID format", 400); + } + + const tx = await getATransaction(txId); + + if (!tx) { + throw new AppError("Transaction Entry not found", 404); + } + + return res.status(200).json(new ApiResponse("success", tx)); + +}) \ No newline at end of file diff --git a/src/controllers/user.controller.ts b/src/controllers/user.controller.ts new file mode 100644 index 0000000..5193ba3 --- /dev/null +++ b/src/controllers/user.controller.ts @@ -0,0 +1,15 @@ +import { asyncHandler } from '@/middlewares/asyncHandler'; +import { ApiResponse } from '@/utils/ApiResponse'; +import { NextFunction, Request, Response } from 'express'; +import { User } from '@prisma/client'; +import { getUserByEmail } from '@/services/auth.service'; + + + +export const handleGetUser = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const user = (req as Request & { user?: User }).user! + + const fulUserDetails = await getUserByEmail(user.email) + return res.status(200).json(new ApiResponse("success", fulUserDetails)); + +}) diff --git a/src/middlewares/asyncHandler.ts b/src/middlewares/asyncHandler.ts new file mode 100644 index 0000000..a9a6a3a --- /dev/null +++ b/src/middlewares/asyncHandler.ts @@ -0,0 +1,6 @@ +import { NextFunction, Request, Response } from "express"; + +export const asyncHandler = (fn: Function) => + (req: Request, res: Response, next: NextFunction) => { + Promise.resolve(fn(req, res, next)).catch(next); + }; diff --git a/src/middlewares/errorHandler.ts b/src/middlewares/errorHandler.ts new file mode 100644 index 0000000..3540340 --- /dev/null +++ b/src/middlewares/errorHandler.ts @@ -0,0 +1,29 @@ +import { Request, Response, NextFunction } from "express"; +import { ApiResponse } from "@/utils/ApiResponse"; +import { AppError } from "@/utils/AppError"; +import { Prisma } from "@prisma/client"; +import { prismaErrorMap } from "@/middlewares/prismaErrors"; + +export const errorHandler = ( + err: AppError | Error, + req: Request, + res: Response, + next: NextFunction +) => { + let statusCode = (err as AppError).statusCode || 500; + let message = err.message; + console.log(err.message) + + if (err instanceof Prisma.PrismaClientKnownRequestError) { + // If Prisma gives us a code, check our map + message = prismaErrorMap[err.code] || "Database error occurred."; + } else if ( + err instanceof Prisma.PrismaClientUnknownRequestError || + err instanceof Prisma.PrismaClientInitializationError || + err instanceof Prisma.PrismaClientRustPanicError + ) { + message = "Internal database error. Please try again later."; + } + + res.status(statusCode).json(new ApiResponse("fail", message)); +}; diff --git a/src/middlewares/index.ts b/src/middlewares/index.ts new file mode 100644 index 0000000..6abe629 --- /dev/null +++ b/src/middlewares/index.ts @@ -0,0 +1,28 @@ +import { AppError } from "@/utils/AppError"; +import { NextFunction, Request, Response } from "express"; +import { asyncHandler } from "./asyncHandler"; +import jwt from "jsonwebtoken"; +import { config } from "@/constants"; +import { getUserById } from "@/services/auth.service"; +import { User } from '@prisma/client'; + + +export const ensureAuthenticated = asyncHandler(async (req: Request, res: Response, next: NextFunction) => { + const token = req.headers.authorization?.split(" ")[1]; + console.log("token", token) + + if (!token) { + throw new AppError("Not authenticated", 401); + } + + const decoded = jwt.verify(token, config.JWT_SECRET!) as { userId: string }; + const user = await getUserById(decoded.userId) + if (user) { + // req.user = user; + (req as Request & {user: User}).user = user; + next(); + } else { + throw new AppError("User does not exist", 401); + + } +}) \ No newline at end of file diff --git a/src/middlewares/prismaErrors.ts b/src/middlewares/prismaErrors.ts new file mode 100644 index 0000000..d1164cd --- /dev/null +++ b/src/middlewares/prismaErrors.ts @@ -0,0 +1,35 @@ +import { Prisma } from "@prisma/client"; + +export const prismaErrorMap: Record = { + // User-friendly, generic messages for known Prisma errors + P2000: "One of the values is too long. Please shorten the input and try again.", + P2001: "We couldn't find the item you requested.", + P2002: "An item with that value already exists. Please use a different value.", + P2003: "A related record required for this action is missing.", + P2004: "A database constraint prevented this action. Please check your input and try again.", + P2005: "There is an invalid value in the database. Please try again or contact support.", + P2006: "A provided value has the wrong type or format. Please check your input.", + P2007: "Some input failed validation. Please review and correct the data.", + P2008: "The database could not parse the request. Please try again.", + P2009: "The database rejected the request. Please verify the data and try again.", + P2010: "A low-level database operation failed. Please try again later.", + P2011: "A required field was set to null. Please provide a value for all required fields.", + P2012: "A required value is missing. Please include all required information.", + P2013: "A required argument is missing. Please include all necessary fields.", + P2014: "A related record is missing. Ensure related data exists before retrying.", + P2015: "A related item could not be found. Please verify related records exist.", + P2016: "There was a problem interpreting the request. Please try again.", + P2017: "Related records are not connected. Please ensure relationships are correct.", + P2018: "Required related records were not found. Please confirm related data exists.", + P2019: "Invalid input provided. Please check the data and try again.", + P2020: "A value is out of the allowed range. Please use a valid value.", + P2021: "The requested table or resource does not exist.", + P2022: "A required database column is missing.", + P2023: "Inconsistent data detected. Please try again or contact support if the issue continues.", + P2024: "The database request timed out. Please try again.", + P2025: "The record you're trying to update or delete was not found.", + P2026: "This operation isn't supported by the database engine.", + P2027: "Multiple database errors occurred. Please review the request and try again.", + P2028: "The operation was not completed. Please try again." + +} \ No newline at end of file diff --git a/src/prisma/migrations/20250813111215_init/migration.sql b/src/prisma/migrations/20250813111215_init/migration.sql new file mode 100644 index 0000000..e6f03db --- /dev/null +++ b/src/prisma/migrations/20250813111215_init/migration.sql @@ -0,0 +1,14 @@ +-- CreateTable +CREATE TABLE "public"."User" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "public"."User"("email"); diff --git a/src/prisma/migrations/20250815104938_add_other_models/migration.sql b/src/prisma/migrations/20250815104938_add_other_models/migration.sql new file mode 100644 index 0000000..9cfc391 --- /dev/null +++ b/src/prisma/migrations/20250815104938_add_other_models/migration.sql @@ -0,0 +1,48 @@ +-- AlterTable +ALTER TABLE "public"."User" ADD COLUMN "transactionPIN" TEXT; + +-- CreateTable +CREATE TABLE "public"."Wallet" ( + "balance" DOUBLE PRECISION NOT NULL DEFAULT 100000, + "userId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Wallet_pkey" PRIMARY KEY ("userId") +); + +-- CreateTable +CREATE TABLE "public"."Donation" ( + "id" TEXT NOT NULL, + "amount" DOUBLE PRECISION NOT NULL, + "donorId" TEXT NOT NULL, + "beneficiaryId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Donation_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Wallet_userId_key" ON "public"."Wallet"("userId"); + +-- CreateIndex +CREATE INDEX "Wallet_userId_idx" ON "public"."Wallet"("userId"); + +-- CreateIndex +CREATE INDEX "Donation_donorId_idx" ON "public"."Donation"("donorId"); + +-- CreateIndex +CREATE INDEX "Donation_beneficiaryId_idx" ON "public"."Donation"("beneficiaryId"); + +-- CreateIndex +CREATE INDEX "User_email_idx" ON "public"."User"("email"); + +-- AddForeignKey +ALTER TABLE "public"."Wallet" ADD CONSTRAINT "Wallet_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Donation" ADD CONSTRAINT "Donation_donorId_fkey" FOREIGN KEY ("donorId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Donation" ADD CONSTRAINT "Donation_beneficiaryId_fkey" FOREIGN KEY ("beneficiaryId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/src/prisma/migrations/20250824190139_add_transaction_model/migration.sql b/src/prisma/migrations/20250824190139_add_transaction_model/migration.sql new file mode 100644 index 0000000..a3a415f --- /dev/null +++ b/src/prisma/migrations/20250824190139_add_transaction_model/migration.sql @@ -0,0 +1,54 @@ +/* + Warnings: + + - Added the required column `transactionId` to the `Donation` table without a default value. This is not possible if the table is not empty. + +*/ +-- CreateEnum +CREATE TYPE "public"."TransactionType" AS ENUM ('DONATION', 'AIRTIME_PURCHASE', 'WITHDRAWAL', 'DEPOSIT', 'TRANSFER'); + +-- AlterTable +ALTER TABLE "public"."Donation" ADD COLUMN "transactionId" TEXT NOT NULL; + +-- CreateTable +CREATE TABLE "public"."Transaction" ( + "id" TEXT NOT NULL, + "type" "public"."TransactionType" NOT NULL, + "reference" TEXT, + "description" TEXT, + "donationId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Transaction_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."TransactionEntry" ( + "id" TEXT NOT NULL, + "transactionId" TEXT NOT NULL, + "walletId" TEXT NOT NULL, + "amount" DOUBLE PRECISION NOT NULL, + "balanceBefore" DOUBLE PRECISION NOT NULL, + "balanceAfter" DOUBLE PRECISION NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "TransactionEntry_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Transaction_donationId_key" ON "public"."Transaction"("donationId"); + +-- CreateIndex +CREATE INDEX "TransactionEntry_transactionId_idx" ON "public"."TransactionEntry"("transactionId"); + +-- CreateIndex +CREATE INDEX "TransactionEntry_walletId_idx" ON "public"."TransactionEntry"("walletId"); + +-- AddForeignKey +ALTER TABLE "public"."Transaction" ADD CONSTRAINT "Transaction_donationId_fkey" FOREIGN KEY ("donationId") REFERENCES "public"."Donation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."TransactionEntry" ADD CONSTRAINT "TransactionEntry_transactionId_fkey" FOREIGN KEY ("transactionId") REFERENCES "public"."Transaction"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."TransactionEntry" ADD CONSTRAINT "TransactionEntry_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "public"."Wallet"("userId") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/src/prisma/migrations/20250825050302_remove_transaction_id_from_donation_modal/migration.sql b/src/prisma/migrations/20250825050302_remove_transaction_id_from_donation_modal/migration.sql new file mode 100644 index 0000000..e2cd59c --- /dev/null +++ b/src/prisma/migrations/20250825050302_remove_transaction_id_from_donation_modal/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `transactionId` on the `Donation` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "public"."Donation" DROP COLUMN "transactionId"; diff --git a/src/prisma/migrations/20250825120344_set_default_wallet_balance_to_0/migration.sql b/src/prisma/migrations/20250825120344_set_default_wallet_balance_to_0/migration.sql new file mode 100644 index 0000000..745604c --- /dev/null +++ b/src/prisma/migrations/20250825120344_set_default_wallet_balance_to_0/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "public"."Wallet" ALTER COLUMN "balance" SET DEFAULT 0; diff --git a/src/prisma/migrations/20250827132435_add_relation_btwn_user/migration.sql b/src/prisma/migrations/20250827132435_add_relation_btwn_user/migration.sql new file mode 100644 index 0000000..885ac19 --- /dev/null +++ b/src/prisma/migrations/20250827132435_add_relation_btwn_user/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - Added the required column `userId` to the `TransactionEntry` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "public"."TransactionEntry" ADD COLUMN "userId" TEXT NOT NULL; + +-- CreateIndex +CREATE INDEX "TransactionEntry_userId_idx" ON "public"."TransactionEntry"("userId"); + +-- AddForeignKey +ALTER TABLE "public"."TransactionEntry" ADD CONSTRAINT "TransactionEntry_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/src/prisma/migrations/migration_lock.toml b/src/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/src/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma new file mode 100644 index 0000000..eb6f144 --- /dev/null +++ b/src/prisma/schema.prisma @@ -0,0 +1,105 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +// ? background tasks and queues using redis +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(uuid()) + email String @unique + password String + name String + transactionPIN String? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + wallet Wallet? + donations Donation[] + received Donation[] @relation("UserReceivedDonations") + transactionEntries TransactionEntry[] + + @@index([email]) +} + +model Wallet { + balance Float @default(0) + userId String @id @unique + user User @relation(fields: [userId], references: [id]) + + transactionEntries TransactionEntry[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([userId]) +} + +model Donation { + id String @id @default(uuid()) + amount Float + donorId String + beneficiaryId String + donor User @relation(fields: [donorId], references: [id]) + beneficiary User @relation("UserReceivedDonations", fields: [beneficiaryId], references: [id]) + transaction Transaction? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([donorId]) + @@index([beneficiaryId]) +} + +model Transaction { + id String @id @default(uuid()) + type TransactionType + reference String? // e.g. external reference, donationId, airtimeId + description String? + + entries TransactionEntry[] + + donation Donation? @relation(fields: [donationId], references: [id]) + donationId String? @unique + + createdAt DateTime @default(now()) +} + +model TransactionEntry { + id String @id @default(uuid()) + transactionId String + transaction Transaction @relation(fields: [transactionId], references: [id]) + + walletId String + userId String + wallet Wallet @relation(fields: [walletId], references: [userId]) + user User @relation(fields: [userId], references: [id]) + + amount Float // positive for credit, negative for debit + balanceBefore Float + balanceAfter Float + + createdAt DateTime @default(now()) + + @@index([transactionId]) + @@index([walletId]) + @@index([userId]) +} + +enum TransactionType { + DONATION + AIRTIME_PURCHASE + WITHDRAWAL + DEPOSIT + TRANSFER +} diff --git a/src/prisma/seed.ts b/src/prisma/seed.ts new file mode 100644 index 0000000..4acbfa1 --- /dev/null +++ b/src/prisma/seed.ts @@ -0,0 +1,34 @@ +import { PrismaClient } from "@prisma/client"; +import * as bcrypt from "bcryptjs"; + +const prisma = new PrismaClient(); + +async function main() { + const hashedPassword = await bcrypt.hash("dummy", 10); + + await prisma.user.upsert({ + where: { id: "system" }, + update: {}, + create: { + id: "system", + email: "system@paritiebackendtest.com", + password: hashedPassword, + name: "Paritie Account", + wallet: { + create: { + balance: 1_000_000_000, // your treasury balance + }, + }, + }, + }); + + console.log("โœ…Paritie System wallet initialized"); +} + +main() + .then(() => prisma.$disconnect()) + .catch((e) => { + console.error(e); + prisma.$disconnect(); + process.exit(1); + }); diff --git a/src/routes/auth.routes.ts b/src/routes/auth.routes.ts new file mode 100644 index 0000000..69263f1 --- /dev/null +++ b/src/routes/auth.routes.ts @@ -0,0 +1,111 @@ +import express from 'express'; +import { handleCreateAcc, handleLoginAcc, handleRefreshToken, handleLogout } from "@/controllers/auth.controller" +const router = express.Router(); + + +/** + * @swagger + * /api/auth/create: + * post: + * summary: Creates a new user + * tags: [Auth] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * name: + * type: string + * description: Must be at least 2 characters long, only letters and spaces allowed + * minLength: 2 + * pattern: '^[A-Za-z ]+$' + * example: Dunsin + * email: + * type: string + * description: Must be a valid email address + * format: email + * example: dunsin@example.com + * password: + * type: string + * description: Must be at least 8 characters long, contain one uppercase letter, one lowercase letter, and one number + * minLength: 8 + * pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$' + * example: StrongPass123 + * responses: + * 201: + * description: User created successfully + */ + +router.post('/create', handleCreateAcc) + +/** + * @swagger + * /api/auth/login: + * post: + * summary: Login user + * tags: [Auth] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * email: + * type: string + * description: Must be a valid email address + * format: email + * example: dunsin@example.com + * password: + * type: string + * description: Must be at least 8 characters long, contain one uppercase letter, one lowercase letter, and one number + * minLength: 8 + * pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).+$' + * example: StrongPass123 + * responses: + * 200: + * description: Login successfully + */ + +router.post('/login', handleLoginAcc) + + +/** + * @swagger + * /api/auth/refresh-token: + * post: + * summary: Refresh access token using refresh token cookie + * tags: [Auth] + * parameters: + * - in: cookie + * name: refreshToken + * schema: + * type: string + * required: true + * example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJkNzEwYzI0NC1kMzRmLTQ4OGEtYWIyYS03NDUzMDIxYzIyODgiLCJpYXQiOjE3NTYxMTUxMDQsImV4cCI6MTc1NjcxOTkwNH0.eedeTOuvLma-vAwKtVp4Wco1t0MFV8lCmQXExduXp8g + * description: HTTP-only refresh token cookie + * responses: + * 200: + * description: New access token returned + * 401: + * description: Refresh token missing or invalid + */ +router.post("/refresh-token", handleRefreshToken); + + + +/** + * @swagger + * /api/auth/logout: + * get: + * summary: Logout (clears refresh token cookie) + * tags: [Auth] + * responses: + * 200: + * description: User logged out and refresh token cookie cleared + */ +router.get("/logout", handleLogout); + +export default router; diff --git a/src/routes/donation.routes.ts b/src/routes/donation.routes.ts new file mode 100644 index 0000000..49d1a3f --- /dev/null +++ b/src/routes/donation.routes.ts @@ -0,0 +1,129 @@ +import express from 'express'; +import { handleCreateDonation, handleGetUserDonations, handleFilterDonations, handleDonationDetails } from "@/controllers/donation.controller" +const router = express.Router(); + + + + +/** + * @swagger + * /api/donation/create: + * post: + * summary: Create a donation transaction + * tags: [Donation] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - amount + * - beneficiaryEmail + * properties: + * amount: + * type: number + * description: Amount to donate + * example: 50.0 + * beneficiaryEmail: + * type: string + * description: Email of the beneficiary receiving the donation + * example: "dunsin@exmaple.com" + * transactionPin: + * type: string + * description: User's 4 0r 6 digit transaction PIN + * example: "123456" + * required: true + * responses: + * 201: + * description: Donation created successfully + * 400: + * description: Invalid request payload or parameters + * 401: + * description: Unaut horized โ€” invalid transaction PIN + * 500: + * description: Internal server error + */ + + +router.post('/create', handleCreateDonation) + + +/** + * @swagger + * /api/donation/my-donations: + * get: + * summary: Get donations made by the user within a date range + * tags: [Donation] + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: from + * schema: + * type: string + * minimum: 1 + * required: false + * description: start date + * example: "01/02/2024" + * - in: query + * name: to + * schema: + * type: string + * minimum: 1 + * required: false + * description: end date + * example: "12/2025" + * - in: query + * name: page + * schema: + * type: integer + * minimum: 1 + * required: false + * description: Page number for pagination + * example: 1 + * - in: query + * name: limit + * schema: + * type: integer + * minimum: 1 + * required: false + * description: Number of results per page + * example: 10 + * responses: + * 200: + * description: list of donations made by the user + */ + +router.get("/my-donations", handleFilterDonations); + + +/** + * @swagger + * /api/donation/{id}: + * get: + * summary: Get details of a specific donation by ID + * tags: [Donation] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: id + * required: true + * schema: + * type: string + * description: The ID of the donation to retrieve + * responses: + * 200: + * description: Donation details retrieved successfully + * 404: + * description: Donation not found + */ +router.get("/:id", handleDonationDetails) + + + + +export default router; diff --git a/src/routes/index.ts b/src/routes/index.ts new file mode 100644 index 0000000..8bc063a --- /dev/null +++ b/src/routes/index.ts @@ -0,0 +1,17 @@ +import express from 'express'; +import authRoutes from './auth.routes'; +import transactionRoutes from './transaction.routes'; +import { ensureAuthenticated } from "@/middlewares/index" +import userRoutes from "./user.routes" +import donationRoutes from "./donation.routes" + + +const router = express.Router(); + + +router.use("/auth", authRoutes); +router.use("/tx", ensureAuthenticated, transactionRoutes) +router.use("/donation", ensureAuthenticated, donationRoutes) +router.use("/user", ensureAuthenticated, userRoutes) + +export default router \ No newline at end of file diff --git a/src/routes/transaction.routes.ts b/src/routes/transaction.routes.ts new file mode 100644 index 0000000..cc6ab8e --- /dev/null +++ b/src/routes/transaction.routes.ts @@ -0,0 +1,106 @@ +import express from 'express'; +import { handleCreateTxPIN, handleGetATransaction, handleGetUserTransactions } from "@/controllers/transaction.controller" +const router = express.Router(); + + +/** + * @swagger + * /api/tx/pin: + * post: + * summary: Creates a transaction PIN + * tags: [Donation] + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * pin: + * type: string + * description: Must be exactly 4 or 6digits long + * minLength: 4 + * example: 123456 + * responses: + * 200: + * description: Tx Pin created successfully + */ + +router.post('/pin', handleCreateTxPIN) + +/** + * @swagger + * /api/tx/: + * get: + * summary: Get all transactions made by the user + * tags: [Transaction] + * security: + * - bearerAuth: [] + * parameters: + * - in: query + * name: from + * schema: + * type: string + * minimum: 1 + * required: false + * description: start date + * example: "01/02/2024" + * - in: query + * name: to + * schema: + * type: string + * minimum: 1 + * required: false + * description: end date + * example: "12/2025" + * - in: query + * name: page + * schema: + * type: integer + * minimum: 1 + * required: false + * description: Page number for pagination + * example: 1 + * - in: query + * name: limit + * schema: + * type: integer + * minimum: 1 + * required: false + * description: Number of results per page + * example: 10 + * responses: + * 200: + * description: List of transactions made by the user + */ +router.get("/", handleGetUserTransactions) + + +/** + * @swagger + * /api/tx/{txId}: + * get: + * summary: Get a transaction by Id + * tags: [Transaction] + * security: + * - bearerAuth: [] + * parameters: + * - in: path + * name: txId + * required: true + * schema: + * type: string + * description: The ID of the transaction to retrieve + * example: 5dad3afe-a870-491d-b640-958daf3459f0 + * responses: + * 200: + * description: A transaction detail with it's entries + */ + +router.get("/:txId", handleGetATransaction) + + + +export default router; diff --git a/src/routes/user.routes.ts b/src/routes/user.routes.ts new file mode 100644 index 0000000..318d13a --- /dev/null +++ b/src/routes/user.routes.ts @@ -0,0 +1,45 @@ +import express from 'express'; +import { handleGetUser } from "@/controllers/user.controller" +import { handleGetUserBalance } from '@/controllers/transaction.controller'; +const router = express.Router(); + + + +/** + * @swagger + * /api/user/balance: + * get: + * summary: Return calcualted balance from transactions + * tags: [User] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: user balance from transactions + */ + + +/** + * @swagger + * /api/user: + * get: + * summary: Return logged in User + * tags: [User] + * security: + * - bearerAuth: [] + * responses: + * 200: + * description: Logged in User + */ + +router.get('/', handleGetUser) + + +router.get("/balance", handleGetUserBalance) + + + + + + +export default router; diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..052fc46 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,48 @@ +import 'module-alias/register'; +import express from 'express'; +import cors from 'cors'; +import V1Routes from '@/routes/index'; +import { errorHandler } from "@/middlewares/errorHandler"; +import morgan from "morgan" +import { config } from "@/constants/index" +import { AppError } from "@/utils/AppError"; +import cookieParser from "cookie-parser"; +import swaggerUi from "swagger-ui-express"; +import swaggerSpec from "@/utils/swagger"; + + +const app = express(); + + +const corsOptions = { + origin: "*", + credentials: true, // Allow credentials (cookies) + optionsSuccessStatus: 200 // Some legacy browsers choke on 204 +}; + +app.use(cors(corsOptions)); +app.use(express.json()); +app.use(morgan('dev')) +app.use(express.urlencoded({ extended: true })); +app.use(cookieParser()); + + + + +app.use('/api', V1Routes); +app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec)); + +// 404 Handler +app.all('*', (req, res, next) => { + next(new AppError(`Can't find ${req.originalUrl} on this server!`, 404)); +}); + +app.use(errorHandler) + + + +app.listen(config.PORT, () => { + console.log('Application started with config Loaded upโœ…'); + console.log(`Server running on port ${config.PORT}`); + console.log(`API documentation available at ๐Ÿ“๐Ÿ“š http://localhost:${config.PORT}/api-docs`); +}); \ No newline at end of file diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts new file mode 100644 index 0000000..e866f4c --- /dev/null +++ b/src/services/auth.service.ts @@ -0,0 +1,133 @@ +import { PrismaClient, User } from '@prisma/client' +import utils from '@/utils/index'; +import { AppError } from '@/utils/AppError'; + +const prisma = new PrismaClient() + +type CreateUserT = { + email: string; + password: string; + name: string; +} + +export const createUser = async (data: CreateUserT) => { + const { password } = data; + const hashedPassword = await utils.hashPassword(password); + + const SYSTEM_WALLET_ID = "system"; + const amount = 100_000; + + + return await prisma.$transaction(async (tx) => { + // 1. Create user + wallet + const user = await tx.user.create({ + data: { + ...data, + password: hashedPassword, + wallet: { create: { balance: 0 } }, + }, + select: { id: true, name: true, email: true, wallet: true }, + }); + + // Fetch system wallet transaction + const systemWallet = await prisma.wallet.findUniqueOrThrow({ + where: { userId: SYSTEM_WALLET_ID }, + }); + + if (systemWallet.balance < amount) { + throw new Error("โŒ System wallet has insufficient balance"); + } + // 2. Create transaction + const transaction = await tx.transaction.create({ + data: { + type: "DEPOSIT", + description: "Initial funding", + }, + }); + + // 3. Update balances in parallel + const [updatedSystemWallet, updatedUserWallet] = await Promise.all([ + tx.wallet.update({ + where: { userId: systemWallet.userId }, + data: { balance: { decrement: amount } }, + }), + tx.wallet.update({ + where: { userId: user.id }, + data: { balance: { increment: amount } }, + }), + ]); + + // 4. Transaction entries (batch insert) + await tx.transactionEntry.createMany({ + data: [ + { + transactionId: transaction.id, + walletId: systemWallet.userId, + userId: systemWallet.userId, + amount: -amount, + balanceBefore: systemWallet.balance, + balanceAfter: updatedSystemWallet.balance, + }, + { + transactionId: transaction.id, + walletId: user.id, + userId: user.id, + amount: amount, + balanceBefore: user.wallet!.balance, + balanceAfter: updatedUserWallet.balance, + }, + ], + }); + + const { wallet, ...userWithoutWallet } = user; + return userWithoutWallet; + }, { + timeout: 15000, // 15s instead of 5s + }); +}; + + + +export const getUserByEmail = async (email: string) => { + const user = await prisma.user.findUnique({ + where: { email }, + select: { + id: true, + email: true, + name: true, + wallet: true, + donations: true, + received: true, + transactionEntries: true + }, + }); + + return user; +}; + + + + +export const getUserPrivateFn = async (email: string) => { + const user = await prisma.user.findUnique({ + where: { email }, + }); + + + return user; +}; + +//only used internally in middlerware +export const getUserById = async (id: string) => { + const user = await prisma.user.findUnique({ + where: { + id + }, + include: { + wallet: true, + donations: true, + received: true + } + }); + return user +} diff --git a/src/services/donation.service.ts b/src/services/donation.service.ts new file mode 100644 index 0000000..344fae4 --- /dev/null +++ b/src/services/donation.service.ts @@ -0,0 +1,164 @@ + +import { PrismaClient, User } from '@prisma/client' +import utils from '@/utils/index'; +import { AppError } from '@/utils/AppError'; +import { paginate } from '@/utils/pagintion'; + +const prisma = new PrismaClient() + +export const createDonation = async ( + donor: User, + beneficiaryId: string, + amount: number, + txPIN: string +) => { + if (amount <= 0) { + throw new AppError("Donation amount must be greater than 0 :(", 400); + } + + if (!await utils.decryptPassword(donor.transactionPIN!, txPIN)) { + throw new AppError("Invalid transaction PIN", 401); + } + + + const data = await prisma.$transaction(async (tx) => { + // ? 1 - GET DONAOR AND BENEFICIARY WALLETS + + const donorWallet = await tx.wallet.findUnique({ + where: { userId: donor.id }, + select: { balance: true } + }); + + if (!donorWallet) { + throw new AppError("Donor wallet not found", 404); + } + + if (donorWallet.balance < amount) { + throw new AppError("Insufficient balance to make donation", 400); + } + + const beneficiaryWallet = await tx.wallet.findUnique({ + where: { userId: beneficiaryId }, + select: { balance: true } + }); + + if (!beneficiaryWallet) { + throw new AppError("Beneficiary wallet not found", 404); + } + + // ? 2 -CREATE DONATION RECORD + const donation = await tx.donation.create({ + data: { + amount, + donorId: donor.id, + beneficiaryId + } + }); + + + //? 3 - CREATE TRANSACTION RECORD + + const transaction = await tx.transaction.create({ + data: { + type: "DONATION", + description: `Donation of N${amount} from ${donor.id} to ${beneficiaryId}`, + donationId: donation.id + } + }) + + + //? 4 - UPDATE WALLET BALANCE + const updatedDonorWallet = await tx.wallet.update({ + where: { userId: donor.id }, + data: { balance: { decrement: amount } } + }); + + + const updatedBeneficiaryWallet = await tx.wallet.update({ + where: { userId: beneficiaryId }, + data: { balance: { increment: amount } } + }); + + //? 5 - CREATE TRANSCATION ENTRY ROWS + + const debitEntry = await tx.transactionEntry.create({ + data: { + transactionId: transaction.id, + walletId: donor.id, + userId: donor.id, + amount: -amount, + balanceBefore: donorWallet.balance, + balanceAfter: updatedDonorWallet.balance + } + }) + + const creditEntry = await tx.transactionEntry.create({ + data: { + transactionId: transaction.id, + walletId: beneficiaryId, + userId: beneficiaryId, + amount: +amount, + balanceBefore: beneficiaryWallet.balance, + balanceAfter: updatedBeneficiaryWallet.balance + } + }) + + + + + return { + donation, + transaction, + entries: [debitEntry, creditEntry] + } + }); + + return data +}; + + + +export const countUserDonations = async (userId: string) => { + return prisma.donation.count({ + where: { donorId: userId } + }); +}; + + + +export const donationsInPeriod = async ( + userId: string, + date?: { start?: Date; end?: Date }, + page?: string, + limit?: string +) => { + const where: any = { donorId: userId }; + + if ((date && date.start) && date.start) { + where.createdAt = { + gte: date.start, + lte: date.end, + }; + } + + return paginate({ + model: "donation", + where, + orderBy: { createdAt: "desc" }, + page: page ? +page : undefined, + limit: limit ? +limit : undefined, + }); +}; + + + +export const getDonationById = async (donationId: string) => { + const data = prisma.donation.findUnique({ + where: { id: donationId } + // include: { beneficiary: true, donor: true } + }); + + return data; +}; + + diff --git a/src/services/transaction.service.ts b/src/services/transaction.service.ts new file mode 100644 index 0000000..3105e26 --- /dev/null +++ b/src/services/transaction.service.ts @@ -0,0 +1,80 @@ +import utils from "@/utils"; +import { paginate } from "@/utils/pagintion"; +import { PrismaClient } from "@prisma/client"; + + + +const prisma = new PrismaClient() + + +export const setTransactionPIN = async (userId: string, pin: number) => { + const hashedPin = await utils.hashPassword(pin.toString()); + return await prisma.user.update({ + where: { id: userId }, + data: { transactionPIN: hashedPin } + }); +}; + + + +export const getUserTransactions = async (userId: string, date?: { start?: Date; end?: Date }, + page?: string, + limit?: string) => { + + const where: any = { + entries: { + some: { + wallet: { + userId + } + } + } + }; + + if ((date && date.start) && date.start) { + where.createdAt = { + gte: date.start, + lte: date.end, + }; + } + + + return paginate({ + model: "transaction", + where, + orderBy: { createdAt: "desc" }, + page: page ? +page : undefined, + limit: limit ? +limit : undefined, + include: { entries: true, donation: true } + }); + + +} + + +export const getBalanceFromTxs = async (userId: string) => { + const result = await prisma.transactionEntry.aggregate({ + _sum: { + amount: true, + }, + where: { + walletId: userId, + }, + }); + + return result._sum.amount ?? 0; // default to 0 if null +}; + + +export const getATransaction = async (txId: string) => { + const tx = await prisma.transaction.findUnique({ + where: { + id: txId + }, + include: { + entries: true + } + }) + + return tx; +} \ No newline at end of file diff --git a/src/types/express/index.d.ts b/src/types/express/index.d.ts new file mode 100644 index 0000000..8986dfa --- /dev/null +++ b/src/types/express/index.d.ts @@ -0,0 +1,13 @@ +import { User, Wallet } from "@prisma/client"; + +type UserWithWallet = User & { wallet?: Wallet | null }; + +declare global { + namespace Express { + interface Request { + user?: UserWithWallet; + } + } +} + +export { }; diff --git a/src/utils/ApiResponse.ts b/src/utils/ApiResponse.ts new file mode 100644 index 0000000..c1af413 --- /dev/null +++ b/src/utils/ApiResponse.ts @@ -0,0 +1,9 @@ +export class ApiResponse { + status: "success" | "fail"; + data: T | string; + + constructor(status: "success" | "fail", data: T | string) { + this.status = status; + this.data = data; + } +} diff --git a/src/utils/AppError.ts b/src/utils/AppError.ts new file mode 100644 index 0000000..d242814 --- /dev/null +++ b/src/utils/AppError.ts @@ -0,0 +1,11 @@ +export class AppError extends Error { + public statusCode: number; + public status: "fail" | "error"; + + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + this.status = statusCode < 500 ? "fail" : "error"; + Error.captureStackTrace(this, this.constructor); + } +} diff --git a/src/utils/globalErrorHandler.ts b/src/utils/globalErrorHandler.ts new file mode 100644 index 0000000..e81d957 --- /dev/null +++ b/src/utils/globalErrorHandler.ts @@ -0,0 +1,31 @@ +import { NextFunction, Request, Response } from 'express'; + +const globalErrorHandler = ( + err: any, + req: Request, + res: Response, + next: NextFunction +) => { + // Default error values + err.statusCode = err.statusCode || 500; + err.status = err.status || 'error'; + + // Handle operational errors (e.g., invalid input, missing resource) + if (err.isOperational) { + res.status(err.statusCode).json({ + status: err.status, + message: err.message, + }); + } else { + // Log the error for debugging + console.error('ERROR ๐Ÿ’ฅ', err); + + // Send a generic response for non-operational errors + res.status(500).json({ + status: 'error', + message: 'Something went wrong!', + }); + } +}; + +export default globalErrorHandler; \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..35f92a6 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,98 @@ +import { hash, compare } from 'bcryptjs'; +import { AppError } from './AppError'; + + +const utils = { + hashPassword: async (password: string): Promise => { + const saltRounds = 10; + return await hash(password, saltRounds); + }, + decryptPassword: async (hashedPassword: string, password: string): Promise => { + return await compare(password, hashedPassword); + }, + validEmail: (email: string): boolean => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email.toLocaleLowerCase()); + }, + validPassword: (password: string): boolean => { + // Example validation: at least 8 characters, one uppercase, one lowercase, one number + const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d]{8,}$/; + return passwordRegex.test(password); + }, + validName: (name: string): boolean => { + // Example validation: at least 2 characters, only letters and spaces + const nameRegex = /^[A-Za-z\s]{2,}$/; + return nameRegex.test(name); + }, + validateCreateUserInput: (data: { email: string; password: string; name: string }): boolean => { + return utils.validEmail(data.email) && utils.validPassword(data.password) && utils.validName(data.name); + }, + + validPIN: (pin: string): boolean => { + const pinRegex = /^(\d{4}|\d{6})$/; + return pinRegex.test(pin); + }, + formatEmail: (email: string) => { + let formatted = email.trim(); + formatted = formatted.toLowerCase(); + // Collapse multiple spaces inside (shouldnโ€™t normally exist but just in case) + formatted = formatted.replace(/\s+/g, ""); + return formatted; + }, + dataParser: (dmy: string) => { + if (!dmy || typeof dmy !== "string") { + throw new AppError("Date input required", 400); + } + + // Normalize: trim spaces, replace "-" with "/", collapse multiple slashes + const normalized = dmy.trim().replace(/-/g, "/").replace(/\s+/g, "").replace(/\/+/g, "/"); + const dmyParts = normalized.split("/").filter(Boolean); + + let start: Date; + let end: Date; + + if (dmyParts.length === 3) { + // dd/mm/yyyy + const [day, month, year] = dmyParts.map(Number); + if (isNaN(day) || isNaN(month) || isNaN(year)) { + throw new AppError("Invalid date numbers", 400); + } + start = new Date(year, month - 1, day, 0, 0, 0, 0); + end = new Date(year, month - 1, day, 23, 59, 59, 999); + } else if (dmyParts.length === 2) { + // mm/yyyy + const [month, year] = dmyParts.map(Number); + if (isNaN(month) || isNaN(year)) { + throw new AppError("Invalid month/year numbers", 400); + } + start = new Date(year, month - 1, 1, 0, 0, 0, 0); + end = new Date(year, month, 0, 23, 59, 59, 999); + } else if (dmyParts.length === 1) { + // yyyy + const year = Number(dmyParts[0]); + if (isNaN(year)) { + throw new AppError("Invalid year number", 400); + } + start = new Date(year, 0, 1, 0, 0, 0, 0); + end = new Date(year, 11, 31, 23, 59, 59, 999); + } else { + throw new AppError("Invalid date format. Use dd/mm/yyyy, mm/yyyy, or yyyy", 400); + } + + return {start, end} ; + } + + + +}; + + + + + + + + + + +export default utils \ No newline at end of file diff --git a/src/utils/pagintion.ts b/src/utils/pagintion.ts new file mode 100644 index 0000000..80d6d38 --- /dev/null +++ b/src/utils/pagintion.ts @@ -0,0 +1,45 @@ +import { PrismaClient } from "@prisma/client"; +const prisma = new PrismaClient(); + +type PaginationParams = { + model: keyof PrismaClient; + page?: number; + limit?: number; + where?: any; + orderBy?: any; + include?: any; +}; + +export const paginate = async ({ + model, + page = 1, + limit = 10, + where, + orderBy, + include, +}: PaginationParams) => { + const skip = (page - 1) * limit; + + const prismaModel = prisma[model] as any; + + const [data, total] = await Promise.all([ + prismaModel.findMany({ + skip, + take: limit, + where, + orderBy, + include, + }), + prismaModel.count({ where }), + ]); + + return { + data, + pagination: { + total, + page, + limit, + totalPages: Math.ceil(total / limit), + }, + }; +}; diff --git a/src/utils/swagger.ts b/src/utils/swagger.ts new file mode 100644 index 0000000..0daa75a --- /dev/null +++ b/src/utils/swagger.ts @@ -0,0 +1,38 @@ +import swaggerJSDoc from "swagger-jsdoc"; +import path from 'path'; +import { config } from "@/constants/index" + + + + +const swaggerDefinition = { + openapi: "3.0.0", + info: { + title: "Paritie Backend Test", + version: "1.0.0", + description: "API documentation", + }, + components: { + securitySchemes: { + bearerAuth: { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + }, + }, + }, + servers: [ + { + url: `http://localhost:${config.PORT}`, + }, + ], +}; + +const options = { + swaggerDefinition, + apis: [path.join(__dirname, "../routes/**/*.ts")], +}; + +const swaggerSpec = swaggerJSDoc(options); + +export default swaggerSpec; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e2fd144 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "nodenext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": "./", // Base directory for imports + "paths": { + "@/*": [ + "src/*" + ] + } + }, + "include": [ + "src/**/*", + "module-alias.js" + ], + "typeRoots": [ + "./node_modules/@types", + "./src/types" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..1bf1487 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1868 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@apidevtools/json-schema-ref-parser@^9.0.6": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz#8ff5386b365d4c9faa7c8b566ff16a46a577d9b8" + integrity sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.6" + call-me-maybe "^1.0.1" + js-yaml "^4.1.0" + +"@apidevtools/openapi-schemas@^2.0.4": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17" + integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ== + +"@apidevtools/swagger-methods@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267" + integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg== + +"@apidevtools/swagger-parser@10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz#32057ae99487872c4dd96b314a1ab4b95d89eaf5" + integrity sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.6" + "@apidevtools/openapi-schemas" "^2.0.4" + "@apidevtools/swagger-methods" "^3.0.2" + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + z-schema "^5.0.1" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@isaacs/balanced-match@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" + integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== + +"@isaacs/brace-expansion@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz#4b3dabab7d8e75a429414a96bd67bf4c1d13e0f3" + integrity sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA== + dependencies: + "@isaacs/balanced-match" "^4.0.1" + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@prisma/client@^6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-6.14.0.tgz#52b6aa169bb596a1aa9cab9a158a03765ffea68b" + integrity sha512-8E/Nk3eL5g7RQIg/LUj1ICyDmhD053STjxrPxUtCRybs2s/2sOEcx9NpITuAOPn07HEpWBfhAVe1T/HYWXUPOw== + +"@prisma/config@6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@prisma/config/-/config-6.14.0.tgz#33f820a182c5eaa85dd6dd705c84d878c978a666" + integrity sha512-IwC7o5KNNGhmblLs23swnfBjADkacBb7wvyDXUWLwuvUQciKJZqyecU0jw0d7JRkswrj+XTL8fdr0y2/VerKQQ== + dependencies: + c12 "3.1.0" + deepmerge-ts "7.1.5" + effect "3.16.12" + empathic "2.0.0" + +"@prisma/debug@6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-6.14.0.tgz#9dbf781cf2b2c942b9fc7eec6eba22f9015f1a8c" + integrity sha512-j4Lf+y+5QIJgQD4sJWSbkOD7geKx9CakaLp/TyTy/UDu9Wo0awvWCBH/BAxTHUaCpIl9USA5VS/KJhDqKJSwug== + +"@prisma/engines-version@6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49": + version "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49.tgz#ecca349e575cc473b3c9b95d9493b07fdccea001" + integrity sha512-EgN9ODJpiX45yvwcngoStp3uQPJ3l+AEVoQ6dMMO2QvmwIlnxfApzKmJQExzdo7/hqQANrz5txHJdGYHzOnGHA== + +"@prisma/engines@6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-6.14.0.tgz#b43c850e942db79e9d3923c020b891671b30da57" + integrity sha512-LhJjqsALFEcoAtF07nSaOkVguaxw/ZsgfROIYZ8bAZDobe7y8Wy+PkYQaPOK1iLSsFgV2MhCO/eNrI1gdSOj6w== + dependencies: + "@prisma/debug" "6.14.0" + "@prisma/engines-version" "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49" + "@prisma/fetch-engine" "6.14.0" + "@prisma/get-platform" "6.14.0" + +"@prisma/extension-accelerate@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@prisma/extension-accelerate/-/extension-accelerate-2.0.2.tgz#2a8afac940413fb814b50ce9015dac049a783503" + integrity sha512-yZK6/k7uOEFpEsKoZezQS1CKDboPtBCQ0NyI70e1Un8tDiRgg80iWGyjsJmRpps2ZIut3MroHP+dyR3wVKh8lA== + +"@prisma/fetch-engine@6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-6.14.0.tgz#f0cf3df2b1bfe65bb0904fb7da06b25dddea6f4b" + integrity sha512-MPzYPOKMENYOaY3AcAbaKrfvXVlvTc6iHmTXsp9RiwCX+bPyfDMqMFVUSVXPYrXnrvEzhGHfyiFy0PRLHPysNg== + dependencies: + "@prisma/debug" "6.14.0" + "@prisma/engines-version" "6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49" + "@prisma/get-platform" "6.14.0" + +"@prisma/get-platform@6.14.0": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-6.14.0.tgz#fc86665a299b8aa9c6b1546dff847a2289854dfb" + integrity sha512-7VjuxKNwjnBhKfqPpMeWiHEa2sVjYzmHdl1slW6STuUCe9QnOY0OY1ljGSvz6wpG4U8DfbDqkG1yofd/1GINww== + dependencies: + "@prisma/debug" "6.14.0" + +"@scarf/scarf@=1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.4.0.tgz#3bbb984085dbd6d982494538b523be1ce6562972" + integrity sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ== + +"@standard-schema/spec@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.0.0.tgz#f193b73dc316c4170f2e82a881da0f550d551b9c" + integrity sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA== + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/body-parser@*": + version "1.19.6" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" + integrity sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/cookie-parser@^1.4.8": + version "1.4.9" + resolved "https://registry.yarnpkg.com/@types/cookie-parser/-/cookie-parser-1.4.9.tgz#f0e79c766a58ee7369a52e7509b3840222f68ed2" + integrity sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g== + +"@types/cors@^2.8.17": + version "2.8.19" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342" + integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@^5.0.0": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz#2fa94879c9d46b11a5df4c74ac75befd6b283de6" + integrity sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express-session@^1.18.1": + version "1.18.2" + resolved "https://registry.yarnpkg.com/@types/express-session/-/express-session-1.18.2.tgz#778dc3296da9aa97d5bf8e42358a54c52a230317" + integrity sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg== + dependencies: + "@types/express" "*" + +"@types/express@*", "@types/express@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.3.tgz#6c4bc6acddc2e2a587142e1d8be0bce20757e956" + integrity sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.5.tgz#5b749ab2b16ba113423feb1a64a95dcd30398472" + integrity sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg== + +"@types/json-schema@^7.0.6": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/jsonwebtoken@^9.0.9": + version "9.0.10" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz#a7932a47177dcd4283b6146f3bd5c26d82647f09" + integrity sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA== + dependencies: + "@types/ms" "*" + "@types/node" "*" + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/module-alias@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/module-alias/-/module-alias-2.0.4.tgz#c6a784be6bc2ff5424889e23084ac001454d5f00" + integrity sha512-5+G/QXO/DvHZw60FjvbDzO4JmlD/nG5m2/vVGt25VN1eeP3w2bCoks1Wa7VuptMPM1TxJdx6RjO70N9Fw0nZPA== + +"@types/morgan@^1.9.9": + version "1.9.10" + resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.10.tgz#725c15d95a5e6150237524cd713bc2d68f9edf1a" + integrity sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA== + dependencies: + "@types/node" "*" + +"@types/ms@*": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" + integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== + +"@types/node@*": + version "24.2.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.2.1.tgz#83e41543f0a518e006594bb394e2cd961de56727" + integrity sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ== + dependencies: + undici-types "~7.10.0" + +"@types/node@^22.13.10": + version "22.17.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.17.1.tgz#484a755050497ebc3b37ff5adb7470f2e3ea5f5b" + integrity sha512-y3tBaz+rjspDTylNjAX37jEC3TETEFGNJL6uQDxwF9/8GLLIjW1rvVHlynyuUKMnMr1Roq8jOv3vkopBjC4/VA== + dependencies: + undici-types "~6.21.0" + +"@types/qs@*": + version "6.14.0" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.14.0.tgz#d8b60cecf62f2db0fb68e5e006077b9178b85de5" + integrity sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/send@*": + version "0.17.5" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.5.tgz#d991d4f2b16f2b1ef497131f00a9114290791e74" + integrity sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.8" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.8.tgz#8180c3fbe4a70e8f00b9f70b9ba7f08f35987877" + integrity sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/swagger-jsdoc@^6.0.4": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/swagger-jsdoc/-/swagger-jsdoc-6.0.4.tgz#bb4f60f3a5f103818e022f2e29ff8935113fb83d" + integrity sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ== + +"@types/swagger-ui-express@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@types/swagger-ui-express/-/swagger-ui-express-4.1.8.tgz#3c0e0bf2543c7efb500eaa081bfde6d92f88096c" + integrity sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g== + dependencies: + "@types/express" "*" + "@types/serve-static" "*" + +"@types/yamljs@^0.2.34": + version "0.2.34" + resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.34.tgz#c10b1f31b173f2cc93342f27b0796c2eb5b3ae84" + integrity sha512-gJvfRlv9ErxdOv7ux7UsJVePtX54NAvQyd8ncoiFqK8G5aeHIfQfGH2fbruvjAQ9657HwAaO54waS+Dsk2QTUQ== + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-walk@^8.1.1: + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +bcryptjs@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-3.0.2.tgz#caadcca1afefe372ed6e20f86db8e8546361c1ca" + integrity sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== + 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" + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +c12@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/c12/-/c12-3.1.0.tgz#9e237970e1d3b74ebae51d25945cb59664c12c89" + integrity sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw== + dependencies: + chokidar "^4.0.3" + confbox "^0.2.2" + defu "^6.1.4" + dotenv "^16.6.1" + exsolve "^1.0.7" + giget "^2.0.0" + jiti "^2.4.2" + ohash "^2.0.11" + pathe "^2.0.3" + perfect-debounce "^1.0.0" + pkg-types "^2.2.0" + rc9 "^2.1.2" + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +call-me-maybe@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" + integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== + +chokidar@^3.5.2: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + 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" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +citty@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.6.tgz#0f7904da1ed4625e1a9ea7e0fa780981aab7c5e4" + integrity sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== + dependencies: + consola "^3.2.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75" + integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q== + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confbox@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.2.2.tgz#8652f53961c74d9e081784beed78555974a9c110" + integrity sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ== + +consola@^3.2.3, consola@^3.4.0, consola@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.4.2.tgz#5af110145397bb67afdab77013fdc34cae590ea7" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-parser@^1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.7.tgz#e2125635dfd766888ffe90d60c286404fa0e7b26" + integrity sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw== + dependencies: + cookie "0.7.2" + cookie-signature "1.0.6" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie-signature@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454" + integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== + +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== + +cookie@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cors@^2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" + integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4: + version "4.4.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" + integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== + dependencies: + ms "^2.1.3" + +deepmerge-ts@7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz#ff818564007f5c150808d2b7b732cac83aa415ab" + integrity sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw== + +defu@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + +depd@2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destr@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.5.tgz#7d112ff1b925fb8d2079fac5bdb4a90973b51fdb" + integrity sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +doctrine@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dotenv@^16.4.7, dotenv@^16.6.1: + version "16.6.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.6.1.tgz#773f0e69527a8315c7285d5ee73c4459d20a8020" + integrity sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow== + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +effect@3.16.12: + version "3.16.12" + resolved "https://registry.yarnpkg.com/effect/-/effect-3.16.12.tgz#3762f745846cfa4905512e397e17f683438addbe" + integrity sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg== + dependencies: + "@standard-schema/spec" "^1.0.0" + fast-check "^3.23.1" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +empathic@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/empathic/-/empathic-2.0.0.tgz#71d3c2b94fad49532ef98a6c34be0386659f6131" + integrity sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +express-session@^1.18.1: + version "1.18.2" + resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.18.2.tgz#34db6252611b57055e877036eea09b4453dec5d8" + integrity sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A== + dependencies: + cookie "0.7.2" + cookie-signature "1.0.7" + debug "2.6.9" + depd "~2.0.0" + on-headers "~1.1.0" + parseurl "~1.3.3" + safe-buffer "5.2.1" + uid-safe "~2.1.5" + +express@^4.21.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== + 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" + +exsolve@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/exsolve/-/exsolve-1.0.7.tgz#3b74e4c7ca5c5f9a19c3626ca857309fa99f9e9e" + integrity sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw== + +fast-check@^3.23.1: + version "3.23.2" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.23.2.tgz#0129f1eb7e4f500f58e8290edc83c670e4a574a2" + integrity sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A== + dependencies: + pure-rand "^6.1.0" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== + 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" + +foreground-child@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +giget@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/giget/-/giget-2.0.0.tgz#395fc934a43f9a7a29a29d55b99f23e30c14f195" + integrity sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA== + dependencies: + citty "^0.1.6" + consola "^3.4.0" + defu "^6.1.4" + node-fetch-native "^1.6.6" + nypm "^0.6.0" + pathe "^2.0.3" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^11.0.0: + version "11.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.3.tgz#9d8087e6d72ddb3c4707b1d2778f80ea3eaefcd6" + integrity sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA== + dependencies: + foreground-child "^3.3.1" + jackspeak "^4.1.1" + minimatch "^10.0.3" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jackspeak@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.1.tgz#96876030f450502047fc7e8c7fcf8ce8124e43ae" + integrity sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + +jiti@^2.4.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.5.1.tgz#bd099c1c2be1c59bbea4e5adcd127363446759d0" + integrity sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonwebtoken@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + 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" + +jwa@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" + integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== + dependencies: + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lru-cache@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.1.0.tgz#afafb060607108132dbc1cf8ae661afb69486117" + integrity sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.3.tgz#cf7a0314a16c4d9ab73a7730a0e8e3c3502d47aa" + integrity sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw== + dependencies: + "@isaacs/brace-expansion" "^5.0.0" + +minimatch@^3.0.4, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +module-alias@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.3.tgz#ec2e85c68973bda6ab71ce7c93b763ec96053221" + integrity sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q== + +morgan@^1.10.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.1.tgz#4e02e6a4465a48e26af540191593955d17f61570" + integrity sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.1.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +node-fetch-native@^1.6.6: + version "1.6.7" + resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz#9d09ca63066cc48423211ed4caf5d70075d76a71" + integrity sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== + +nodemon@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.10.tgz#5015c5eb4fffcb24d98cf9454df14f4fecec9bc1" + integrity sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw== + dependencies: + chokidar "^3.5.2" + debug "^4" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^7.5.3" + simple-update-notifier "^2.0.0" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +nypm@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/nypm/-/nypm-0.6.1.tgz#4905b419641073de25ef0f19fb47c5658ada0c35" + integrity sha512-hlacBiRiv1k9hZFiphPUkfSQ/ZfQzZDzC+8z0wL3lvDAOUu/2NnChkKuMoMjNur/9OpKuz2QsIeiPVN0xM5Q0w== + dependencies: + citty "^0.1.6" + consola "^3.4.2" + pathe "^2.0.3" + pkg-types "^2.2.0" + tinyexec "^1.0.1" + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +ohash@^2.0.11: + version "2.0.11" + resolved "https://registry.yarnpkg.com/ohash/-/ohash-2.0.11.tgz#60b11e8cff62ca9dee88d13747a5baa145f5900b" + integrity sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.1.0.tgz#59da4f91c45f5f989c6e4bcedc5a3b0aed70ff65" + integrity sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + +path-to-regexp@0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== + +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-types@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-2.2.0.tgz#049bf404f82a66c465200149457acf0c5fb0fb2d" + integrity sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ== + dependencies: + confbox "^0.2.2" + exsolve "^1.0.7" + pathe "^2.0.3" + +prisma@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-6.14.0.tgz#818a7c721c269c7e8360ad5ab7bb94ab4d749007" + integrity sha512-QEuCwxu+Uq9BffFw7in8In+WfbSUN0ewnaSUKloLkbJd42w6EyFckux4M0f7VwwHlM3A8ssaz4OyniCXlsn0WA== + dependencies: + "@prisma/config" "6.14.0" + "@prisma/engines" "6.14.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +pure-rand@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== + +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + integrity sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc9@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/rc9/-/rc9-2.1.2.tgz#6282ff638a50caa0a91a31d76af4a0b9cbd1080d" + integrity sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg== + dependencies: + defu "^6.1.4" + destr "^2.0.3" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rimraf@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" + integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== + dependencies: + glob "^11.0.0" + package-json-from-dist "^1.0.0" + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.5.3, semver@^7.5.4: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== + 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" + +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== + dependencies: + encodeurl "~2.0.0" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.19.0" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + 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" + +side-channel@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + 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" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +swagger-jsdoc@^6.2.8: + version "6.2.8" + resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz#6d33d9fb07ff4a7c1564379c52c08989ec7d0256" + integrity sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ== + dependencies: + commander "6.2.0" + doctrine "3.0.0" + glob "7.1.6" + lodash.mergewith "^4.6.2" + swagger-parser "^10.0.3" + yaml "2.0.0-1" + +swagger-parser@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/swagger-parser/-/swagger-parser-10.0.3.tgz#04cb01c18c3ac192b41161c77f81e79309135d03" + integrity sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg== + dependencies: + "@apidevtools/swagger-parser" "10.0.3" + +swagger-ui-dist@>=5.0.0: + version "5.27.1" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.27.1.tgz#556e77c659752e99621ac61ad5ef6cb0832279e7" + integrity sha512-oGtpYO3lnoaqyGtlJalvryl7TwzgRuxpOVWqEHx8af0YXI+Kt+4jMpLdgMtMcmWmuQ0QTCHLKExwrBFMSxvAUA== + dependencies: + "@scarf/scarf" "=1.4.0" + +swagger-ui-express@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz#fb8c1b781d2793a6bd2f8a205a3f4bd6fa020dd8" + integrity sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA== + dependencies: + swagger-ui-dist ">=5.0.0" + +tinyexec@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-1.0.1.tgz#70c31ab7abbb4aea0a24f55d120e5990bfa1e0b1" + integrity sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +touch@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== + +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + 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" + +tsconfig-paths@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@^5.8.2: + version "5.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.2.tgz#d93450cddec5154a2d5cabe3b8102b83316fb2a6" + integrity sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A== + +uid-safe@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== + dependencies: + random-bytes "~1.0.0" + +ulid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ulid/-/ulid-3.0.1.tgz#6fae1779938843f476702946931122d127cf3243" + integrity sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q== + +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +undici-types@~7.10.0: + version "7.10.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.10.0.tgz#4ac2e058ce56b462b056e629cc6a02393d3ff350" + integrity sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +validator@^13.7.0: + version "13.15.15" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.15.tgz#246594be5671dc09daa35caec5689fcd18c6e7e4" + integrity sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yaml@2.0.0-1: + version "2.0.0-1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-1.tgz#8c3029b3ee2028306d5bcf396980623115ff8d18" + integrity sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +z-schema@^5.0.1: + version "5.0.6" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-5.0.6.tgz#46d6a687b15e4a4369e18d6cb1c7b8618fc256c5" + integrity sha512-+XR1GhnWklYdfr8YaZv/iu+vY+ux7V5DS5zH1DQf6bO5ufrt/5cgNhVO5qyhsjFXvsqQb/f08DWE9b6uPscyAg== + dependencies: + lodash.get "^4.4.2" + lodash.isequal "^4.5.0" + validator "^13.7.0" + optionalDependencies: + commander "^10.0.0"