From a495bc152e16c333f6ea912f67bb660d46badad8 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:17:40 +0800 Subject: [PATCH 01/84] refactor: standardize token auth responses using HTTPResponse - Replaced res.status() with HTTPResponse.error for consistent API error format - Added specific error handling for expired and invalid JWTs --- src/middleware/authenticate-token.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/middleware/authenticate-token.ts b/src/middleware/authenticate-token.ts index 70260bc..605985a 100644 --- a/src/middleware/authenticate-token.ts +++ b/src/middleware/authenticate-token.ts @@ -1,6 +1,7 @@ import jwt from "jsonwebtoken"; import { Request, Response, NextFunction } from "express"; import { AuthRequest } from "../types/auth-request"; +import { HTTPResponse } from "../util/http-response"; export const authenticateToken = async ( req: AuthRequest, @@ -9,7 +10,7 @@ export const authenticateToken = async ( ) => { const token = req.cookies.token; if (!token) { - res.status(401).json({ success: false, message: "No token provided" }); + HTTPResponse.error(res, 401, "No token provided in cookies."); return; } @@ -19,9 +20,20 @@ export const authenticateToken = async ( }; req.userId = decoded.userId; next(); - } catch (err) { - res - .status(403) - .json({ success: false, message: "Invalid or expired token" }); + } catch (err: any) { + const isExpired = err.name === "TokenExpiredError"; + const isInvalid = err.name === "JsonWebTokenError"; + + if (isExpired) { + HTTPResponse.error(res, 403, "Token has expired."); + return; + } + + if (isInvalid) { + HTTPResponse.error(res, 403, "Invalid token"); + return; + } + + HTTPResponse.error(res, 403, "Unable to verify token"); } }; From ddc135f139fea42a18dfe7799d8f4e84da556ea3 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:18:12 +0800 Subject: [PATCH 02/84] chore: remove flushDB call from server initialization --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index b6ddda7..c041729 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,7 +33,6 @@ app.use("/message", authenticateToken, messageRoutes); // Connect DB and start server const initServer = async () => { await connectDB(); - await flushDB(); app.listen(PORT, () => { console.log(`👍 Server running on port ${PORT}`); From 31a33ca2728bb80f04168b9d3330609bae318cea Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:18:56 +0800 Subject: [PATCH 03/84] chore: rename package to brainbytes-api for clarity --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9040ae9..8478e66 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "brainbytes", + "name": "brainbytes-api", "version": "1.0.0", "description": "", "main": "src/index.ts", From cef1790d5383ce6dede3adc8793a801fa3bd718a Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:19:46 +0800 Subject: [PATCH 04/84] chore(tsconfig): simplify and update compiler options - Set target to ES2020 for improved language feature support - Added rootDir and outDir for cleaner build output structure - Removed commented defaults for a cleaner config file --- tsconfig.json | 117 ++++---------------------------------------------- 1 file changed, 8 insertions(+), 109 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 904d43f..95d3604 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,113 +1,12 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "libReplacement": true, /* Enable lib replacement. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "noUncheckedSideEffectImports": true, /* Check side effect imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ - // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "target": "es2020", + "module": "commonjs", + "rootDir": "src", + "outDir": "dist", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true } } From 154cb7557d0053a2af5d0561f302f24f51cd3c43 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:20:23 +0800 Subject: [PATCH 05/84] chore(docker): rename backend service to expressjs and clean up config - Renamed service from 'backend' to 'expressjs' for clarity - Removed redundant container_name entries - Reordered services for consistency --- docker-compose.yaml | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 974a88f..13f8ab1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,14 +3,19 @@ version: "3.8" # Define all services (containers) services: + mongo: + # Use official mongoDB image + image: mongo:latest + restart: unless-stopped + ports: + - "27017:27017" + volumes: + - mongo-data:/data/db # Backend service - backend: + expressjs: # Build the docker image from the dockerfile in the current dir build: . - # Set custom container name - container_name: brainbytes-server - # Map port 8000 inside the container to port 8000 on the host machine ports: - "8000:8000" @@ -30,17 +35,6 @@ services: command: pnpm start # MongoDB service - mongo: - # Use official mongoDB image - image: mongo:latest - - container_name: brainbytes-db - restart: unless-stopped - ports: - - "27017:27017" - - volumes: - - mongo-data:/data/db volumes: mongo-data: From 15c433386cd89db0788635ab8d11c3a4079963bc Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 16 Jun 2025 14:20:51 +0800 Subject: [PATCH 06/84] chore: add .env.template for environment variable guidance --- .env.template | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .env.template diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..9852a65 --- /dev/null +++ b/.env.template @@ -0,0 +1,9 @@ +PORT= +NODE_ENV= +ENABLE_CORS= +CLIENT_IP= +JWT_SECRET= +IS_DOCKERIZED= +MONGO_ATLAS_URI= +MONGO_DOCKER_URI= +GEMINI_KEY= \ No newline at end of file From fb9a37de8fc80f7384bce7f8e500f900990a20c9 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 17 Jun 2025 00:06:01 +0800 Subject: [PATCH 07/84] refactor: add named status methods to HTTPResponse for clearer API responses Replaced generic success and error methods with specific HTTP methods. - Improves clarity, reusability, and reduces repetition when returning responses --- src/util/http-response.ts | 59 +++++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/util/http-response.ts b/src/util/http-response.ts index e75f373..de4fe86 100644 --- a/src/util/http-response.ts +++ b/src/util/http-response.ts @@ -1,21 +1,68 @@ import { Response } from "express"; export const HTTPResponse = { - success(res: Response, statusCode: number, message: string, data?: T) { - return res.status(statusCode).json({ + ok(res: Response, message: string, data?: T) { + return res.status(200).json({ success: true, message, data, }); }, - error( + created(res: Response, message: string, data?: T) { + return res.status(201).json({ + success: true, + message, + data, + }); + }, + + badRequest(res: Response, message = "Bad Request.", errorDetails?: T) { + return res.status(400).json({ + success: false, + message, + errorDetails, + }); + }, + + unauthorized(res: Response, message = "Unauthorized", errorDetails?: T) { + return res.status(401).json({ + success: false, + message, + errorDetails, + }); + }, + + forbidden(res: Response, message = "Forbidden", errorDetails?: T) { + return res.status(403).json({ + success: false, + message, + errorDetails, + }); + }, + + notFound(res: Response, message = "Not found", errorDetails?: T) { + return res.status(404).json({ + success: false, + message, + errorDetails, + }); + }, + + conflict(res: Response, message = "Conflict", errorDetails?: T) { + return res.status(409).json({ + success: false, + message, + errorDetails, + }); + }, + + internalServerError( res: Response, - statusCode: number, - message?: string, + message = "Internal Server Error", errorDetails?: T ) { - return res.status(statusCode).json({ + return res.status(500).json({ success: false, message, errorDetails, From daf09b04ff72e3356eb1eaedb8f6e7d1525b728c Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 17 Jun 2025 00:07:39 +0800 Subject: [PATCH 08/84] refactor: add named status methods to HTTPResponse for clearer API responses - Replaced generic success and error methods with specific HTTP methods. - Improves clarity, reusability, and reduces repetition when returning responses --- src/controller/auth.controller.ts | 30 ++++++++------ src/controller/chat.controller.ts | 59 ++++++++++++++++++---------- src/controller/message.controller.ts | 25 ++++++++---- src/middleware/authenticate-token.ts | 10 ++--- 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/src/controller/auth.controller.ts b/src/controller/auth.controller.ts index a53b29b..64a7586 100644 --- a/src/controller/auth.controller.ts +++ b/src/controller/auth.controller.ts @@ -12,11 +12,7 @@ export const registerUser = async (req: Request, res: Response) => { // Check if user exits const userExists = await User.findOne({ email }); if (userExists) { - HTTPResponse.error( - res, - 409, - "An account with this email already exists." - ); + HTTPResponse.conflict(res, "An account with this email already exists."); return; } @@ -41,10 +37,14 @@ export const registerUser = async (req: Request, res: Response) => { }); // return success response - HTTPResponse.success(res, 201, "User resgistration success", user); + HTTPResponse.created(res, "User resgistration success", user); } catch (error) { console.error(error); - HTTPResponse.error(res, 500, "An unexpected error has occurred", error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occurred", + error + ); } }; @@ -54,19 +54,19 @@ export const loginUser = async (req: Request, res: Response) => { const { email, password } = req.body; if (!email || !password) { - HTTPResponse.error(res, 400, "Email and password required"); + HTTPResponse.badRequest(res, "Email and password required"); return; } const user = await User.findOne({ email }); if (!user) { - HTTPResponse.error(res, 404, "User with this email does not exist."); + HTTPResponse.notFound(res, "User with this email does not exist."); return; } const isPasswordCorrect = await bcrypt.compare(password, user.password); if (!isPasswordCorrect) { - HTTPResponse.error(res, 400, "Incorrect password"); + HTTPResponse.badRequest(res, "Incorrect password"); return; } @@ -84,10 +84,14 @@ export const loginUser = async (req: Request, res: Response) => { }); // return success response - HTTPResponse.success(res, 200, "Login success"); + HTTPResponse.ok(res, "Login success"); } catch (error: any) { console.error(error); - HTTPResponse.error(res, 500, "An unexpected error has occurred", error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occurred", + error + ); } }; @@ -97,5 +101,5 @@ export const logoutUser = async (req: Request, res: Response) => { secure: process.env.NODE_ENV === "production", sameSite: "strict", }); - HTTPResponse.success(res, 200, "Logged out successfully"); + HTTPResponse.ok(res, "Logout success"); }; diff --git a/src/controller/chat.controller.ts b/src/controller/chat.controller.ts index 8df01f8..c9c84b4 100644 --- a/src/controller/chat.controller.ts +++ b/src/controller/chat.controller.ts @@ -12,7 +12,7 @@ export const createChat = async (req: AuthRequest, res: Response) => { const templateType: TemplateValue = template || Template.TUTOR; if (!prompt) { - HTTPResponse.error(res, 400, "Empty prompt"); + HTTPResponse.badRequest(res, "Empty prompt"); return; } @@ -30,7 +30,7 @@ export const createChat = async (req: AuthRequest, res: Response) => { if (!chatMetaData) { console.error("Failed to generate chat metadata"); - HTTPResponse.error(res, 500, "Failed to generate chat metadata"); + HTTPResponse.internalServerError(res, "Failed to generate chat metadata"); return; } @@ -41,7 +41,7 @@ export const createChat = async (req: AuthRequest, res: Response) => { if (!Chat) { console.error("Failed to create Chat"); - HTTPResponse.error(res, 500, "Failed to create chat"); + HTTPResponse.internalServerError(res, "Failed to create chat"); return; } @@ -58,13 +58,17 @@ export const createChat = async (req: AuthRequest, res: Response) => { response: parsed.response, }); - HTTPResponse.success(res, 201, "Chat successfully created", [ - chat, - message, - ]); + HTTPResponse.created(res, "Chat successfully created", { + chat: chat, + message: message, + }); } catch (error) { console.error(error); - HTTPResponse.error(res, 500, "An unexpected error has occured.", error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occured.", + error + ); } }; @@ -76,10 +80,14 @@ export const getUserChat = async (req: AuthRequest, res: Response) => { createdAt: -1, }); - HTTPResponse.success(res, 200, "Chat successfully retrieved", result); + HTTPResponse.ok(res, "Chat successfully retrieved", result); } catch (error) { console.error(error); - HTTPResponse.error(res, 500, "An unexpcted error has occured", error); + HTTPResponse.internalServerError( + res, + "An unexpcted error has occured", + error + ); } }; @@ -90,20 +98,27 @@ export const deleteChat = async (req: AuthRequest, res: Response) => { const toDelete = await Chat.findById(id); if (!toDelete) { - HTTPResponse.error(res, 404, `Chat not found for id: ${id}`); + HTTPResponse.notFound(res, `Chat not found for id: ${id}`); return; } if (String(toDelete.user) !== userId) { - HTTPResponse.error(res, 401, "You are unauthorized to delete this chat"); + HTTPResponse.unauthorized( + res, + "You are unauthorized to delete this chat" + ); return; } await toDelete.deleteOne(); - HTTPResponse.success(res, 200, "Chat successfully deleted"); + HTTPResponse.ok(res, "Chat successfully deleted"); } catch (error) { console.error(error); - HTTPResponse.error(res, 500, "An unexpected error has occurred", error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occurred", + error + ); } }; @@ -114,33 +129,37 @@ export const updateChat = async (req: AuthRequest, res: Response) => { const title = req.body?.title; if (!title) { - HTTPResponse.error(res, 400, "Please provided chat title"); + HTTPResponse.badRequest(res, "Please provided chat title"); return; } const toUpdate = await Chat.findById(id); if (!toUpdate) { - HTTPResponse.error(res, 404, `Chat not found for id ${id}`); + HTTPResponse.notFound(res, `Chat not found for id ${id}`); return; } if (String(toUpdate.user) !== userId) { - HTTPResponse.error(res, 401, `You are authorized to update this chat`); + HTTPResponse.unauthorized(res, "You are authorized to update this chat"); return; } if (toUpdate.title === title) { - HTTPResponse.success(res, 200, `Chat details are up to date.`); + HTTPResponse.ok(res, "Chat details are up to date"); return; } toUpdate.title = title; await toUpdate.save(); - HTTPResponse.success(res, 200, `Chat title successfully update`, toUpdate); + HTTPResponse.ok(res, "Chat title successfully update", toUpdate); } catch (error) { console.error(error); - HTTPResponse.error(res, 500, "An unexpected error has occurred", error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occurred", + error + ); } }; diff --git a/src/controller/message.controller.ts b/src/controller/message.controller.ts index 071a10c..a053d06 100644 --- a/src/controller/message.controller.ts +++ b/src/controller/message.controller.ts @@ -13,18 +13,18 @@ export const createMessage = async (req: AuthRequest, res: Response) => { const chatId = req.params.chatId as string; if (!Types.ObjectId.isValid(chatId)) { - HTTPResponse.error(res, 400, "Invalid chat ID format"); + HTTPResponse.badRequest(res, "Invalid chat ID format"); return; } if (!prompt) { - HTTPResponse.error(res, 400, "Empty prompt or invalid prompt"); + HTTPResponse.badRequest(res, "Empty prompt or invalid prompt"); return; } const chat = await Chat.findById(chatId); if (!chat) { - HTTPResponse.error(res, 404, `Chat not found for id ${chatId}`); + HTTPResponse.notFound(res, `Chat not found for id ${chatId}`); return; } @@ -48,7 +48,7 @@ export const createMessage = async (req: AuthRequest, res: Response) => { }); if (!ai) { - HTTPResponse.error(res, 500, "Failed to generate response"); + HTTPResponse.internalServerError(res, "Failed to generate response"); return; } @@ -60,14 +60,18 @@ export const createMessage = async (req: AuthRequest, res: Response) => { }); if (!message) { - HTTPResponse.error(res, 500, "Failed to create message"); + HTTPResponse.internalServerError(res, "Failed to create message"); return; } - HTTPResponse.success(res, 201, "Message successfully created", message); + HTTPResponse.created(res, "Message successfully created", message); } catch (error) { console.log(error); - HTTPResponse.error(res, 500, "An unexpected error has occurred", error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occurred", + error + ); } }; @@ -87,6 +91,11 @@ export const getMessagesByChatId = async (req: AuthRequest, res: Response) => { data: messages, }); } catch (error: any) { - HTTPResponse.error(res, 500, "An unexpected error has occurred", error); + console.log(error); + HTTPResponse.internalServerError( + res, + "An unexpected error has occurred", + error + ); } }; diff --git a/src/middleware/authenticate-token.ts b/src/middleware/authenticate-token.ts index 605985a..ae0e6e8 100644 --- a/src/middleware/authenticate-token.ts +++ b/src/middleware/authenticate-token.ts @@ -1,5 +1,5 @@ import jwt from "jsonwebtoken"; -import { Request, Response, NextFunction } from "express"; +import { Response, NextFunction } from "express"; import { AuthRequest } from "../types/auth-request"; import { HTTPResponse } from "../util/http-response"; @@ -10,7 +10,7 @@ export const authenticateToken = async ( ) => { const token = req.cookies.token; if (!token) { - HTTPResponse.error(res, 401, "No token provided in cookies."); + HTTPResponse.badRequest(res, "No token provided in cookies."); return; } @@ -25,15 +25,15 @@ export const authenticateToken = async ( const isInvalid = err.name === "JsonWebTokenError"; if (isExpired) { - HTTPResponse.error(res, 403, "Token has expired."); + HTTPResponse.forbidden(res, "Token has expired."); return; } if (isInvalid) { - HTTPResponse.error(res, 403, "Invalid token"); + HTTPResponse.forbidden(res, "Invalid token"); return; } - HTTPResponse.error(res, 403, "Unable to verify token"); + HTTPResponse.forbidden(res, "Unable to verify token"); } }; From b012763136b7e0881337a8358bf9bfeeed35bbe2 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 17 Jun 2025 00:08:38 +0800 Subject: [PATCH 09/84] refactor: use HTTPResponse.badRequest and format validation errors 0 Replaced raw res.status(400) with HTTPResponse.badRequest for consistency - Improved validation error messages for firstName and lastName - Combined all validation errors into a single readable string --- src/middleware/validator/auth-validator.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/middleware/validator/auth-validator.ts b/src/middleware/validator/auth-validator.ts index a80441c..3d36115 100644 --- a/src/middleware/validator/auth-validator.ts +++ b/src/middleware/validator/auth-validator.ts @@ -1,20 +1,23 @@ import { NextFunction, Request, Response } from "express"; import { body, validationResult } from "express-validator"; +import { HTTPResponse } from "../../util/http-response"; const authValidator = [ body("email").isEmail().withMessage("Invalid email address"), body("password") .isLength({ min: 8 }) .withMessage("Password must be 8 charactes long"), - body("firstName").notEmpty(), - body("lastName").notEmpty(), + body("firstName").notEmpty().withMessage("First name cannot be empty"), + body("lastName").notEmpty().withMessage("Last name cannot be empty"), (req: Request, res: Response, next: NextFunction) => { const errors = validationResult(req); if (!errors.isEmpty()) { - res - .status(400) - .json({ success: false, message: "validation error", data: errors }); + const errText = errors + .array() + .map((err) => err.msg) + .join(", "); + HTTPResponse.badRequest(res, "Validation failed", errText); return; } next(); From d50d63b504146b90305c0fab80a2396d8aeb5d35 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:12:42 +0800 Subject: [PATCH 10/84] refactor: make message parameter optional in ok and created responses --- src/util/http-response.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/http-response.ts b/src/util/http-response.ts index de4fe86..95dc12d 100644 --- a/src/util/http-response.ts +++ b/src/util/http-response.ts @@ -1,7 +1,7 @@ import { Response } from "express"; export const HTTPResponse = { - ok(res: Response, message: string, data?: T) { + ok(res: Response, message?: string, data?: T) { return res.status(200).json({ success: true, message, @@ -9,7 +9,7 @@ export const HTTPResponse = { }); }, - created(res: Response, message: string, data?: T) { + created(res: Response, message?: string, data?: T) { return res.status(201).json({ success: true, message, From fccd52ab8ea182bcd93b34c83f34cd2124d6db5e Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:13:03 +0800 Subject: [PATCH 11/84] chore: add ESLint configuration to project --- eslint.config.mjs | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 eslint.config.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..9db1bea --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,11 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import { defineConfig } from "eslint/config"; + + +export default defineConfig([ + { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"] }, + { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], languageOptions: { globals: globals.browser } }, + tseslint.configs.recommended, +]); From 8bceced0669fb377b180ea17f9f25366179188f4 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:13:35 +0800 Subject: [PATCH 12/84] chore: update target to ES2022 and exclude node_modules --- tsconfig.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 95d3604..5bc00d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,12 @@ { "compilerOptions": { - "target": "es2020", - "module": "commonjs", + "target": "ES2022", + "module": "CommonJS", "rootDir": "src", "outDir": "dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true - } + "strict": true + }, + "exclude": ["node_modules/"] } From 59e5387cac3e3d41cb927215ba6710bc21204a52 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:14:38 +0800 Subject: [PATCH 13/84] chore: rename project to brainbytes, add ESLint scripts and config, and update dependencies --- package.json | 31 +- pnpm-lock.yaml | 1143 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 1025 insertions(+), 149 deletions(-) diff --git a/package.json b/package.json index 8478e66..c05bf08 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,42 @@ { - "name": "brainbytes-api", + "name": "brainbytes", "version": "1.0.0", "description": "", "main": "src/index.ts", "scripts": { "start": "tsx src/index.ts", "dev": "tsx watch src/index.ts", - "compose": "docker-compose up --build" + "compose": "docker-compose up --build", + "lint": "npx eslint ./src/**", + "lint:fix": "npx eslint ./src/** --fix" }, "keywords": [], "author": "", "license": "ISC", "packageManager": "pnpm@10.10.0", "dependencies": { - "@google/genai": "^1.0.1", + "@google/genai": "^1.5.1", "bcryptjs": "^3.0.2", - "body-parser": "^2.2.0", "cookie-parser": "^1.4.7", "cors": "^2.8.5", "express": "^5.1.0", "express-validator": "^7.2.1", "jsonwebtoken": "^9.0.2", - "mongodb": "^6.16.0", - "mongoose": "^8.15.0" + "mongodb": "^6.17.0", + "mongoose": "^8.16.0" }, "devDependencies": { - "@types/body-parser": "^1.19.5", - "@types/cookie-parser": "^1.4.8", - "@types/cors": "^2.8.18", - "@types/express": "^5.0.2", - "@types/jsonwebtoken": "^9.0.9", - "@types/node": "^22.15.21", + "@eslint/js": "^9.29.0", + "@types/cookie-parser": "^1.4.9", + "@types/cors": "^2.8.19", + "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^24.0.3", "dotenv": "^16.5.0", - "tsx": "^4.20.3" + "eslint": "^9.29.0", + "globals": "^16.2.0", + "tsx": "^4.20.3", + "typescript": "^5.8.3", + "typescript-eslint": "^8.34.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5174724..8b9d12e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,11 @@ importers: .: dependencies: '@google/genai': - specifier: ^1.0.1 - version: 1.0.1(@modelcontextprotocol/sdk@1.12.0) + specifier: ^1.5.1 + version: 1.5.1 bcryptjs: specifier: ^3.0.2 version: 3.0.2 - body-parser: - specifier: ^2.2.0 - version: 2.2.0 cookie-parser: specifier: ^1.4.7 version: 1.4.7 @@ -33,36 +30,48 @@ importers: specifier: ^9.0.2 version: 9.0.2 mongodb: - specifier: ^6.16.0 - version: 6.16.0 + specifier: ^6.17.0 + version: 6.17.0 mongoose: - specifier: ^8.15.0 - version: 8.15.0 + specifier: ^8.16.0 + version: 8.16.0 devDependencies: - '@types/body-parser': - specifier: ^1.19.5 - version: 1.19.5 + '@eslint/js': + specifier: ^9.29.0 + version: 9.29.0 '@types/cookie-parser': - specifier: ^1.4.8 - version: 1.4.8(@types/express@5.0.2) + specifier: ^1.4.9 + version: 1.4.9(@types/express@5.0.3) '@types/cors': - specifier: ^2.8.18 - version: 2.8.18 + specifier: ^2.8.19 + version: 2.8.19 '@types/express': - specifier: ^5.0.2 - version: 5.0.2 + specifier: ^5.0.3 + version: 5.0.3 '@types/jsonwebtoken': - specifier: ^9.0.9 - version: 9.0.9 + specifier: ^9.0.10 + version: 9.0.10 '@types/node': - specifier: ^22.15.21 - version: 22.15.21 + specifier: ^24.0.3 + version: 24.0.3 dotenv: specifier: ^16.5.0 version: 16.5.0 + eslint: + specifier: ^9.29.0 + version: 9.29.0 + globals: + specifier: ^16.2.0 + version: 16.2.0 tsx: specifier: ^4.20.3 version: 4.20.3 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: ^8.34.1 + version: 8.34.1(eslint@9.29.0)(typescript@5.8.3) packages: @@ -216,44 +225,123 @@ packages: cpu: [x64] os: [win32] - '@google/genai@1.0.1': - resolution: {integrity: sha512-qf8sq9vpuKUeBKukAn43z2eC1I/Jw63b9wo6O+1x3EIroF3oDouJOtW1AzwvfO+9gzCPfLjuCUONhMKiBC8vkQ==} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.0': + resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.29.0': + resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.2': + resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@google/genai@1.5.1': + resolution: {integrity: sha512-9SKpNo5iqvB622lN3tSCbeuiLGTcStRd+3muOrI9pZMpzfLDc/xC7dWIJd5kK+4AZuY28nsvQmCZe0fPj3JUew==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.11.0 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true - '@modelcontextprotocol/sdk@1.12.0': - resolution: {integrity: sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==} - engines: {node: '>=18'} + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@mongodb-js/saslprep@1.3.0': + resolution: {integrity: sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} - '@mongodb-js/saslprep@1.2.2': - resolution: {integrity: sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==} + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/cookie-parser@1.4.8': - resolution: {integrity: sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==} + '@types/cookie-parser@1.4.9': + resolution: {integrity: sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==} peerDependencies: '@types/express': '*' - '@types/cors@2.8.18': - resolution: {integrity: sha512-nX3d0sxJW41CqQvfOzVG1NCTXfFDrDWIghCZncpHeWlVFd81zxB/DLhg7avFg6eHLCRX7ckBmoIIcqa++upvJA==} + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/express-serve-static-core@5.0.6': resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} - '@types/express@5.0.2': - resolution: {integrity: sha512-BtjL3ZwbCQriyb0DGw+Rt12qAXPiBTPs815lsUvtt1Grk0vLRMZNMUZ741d5rjk+UQOxfDiBZ3dxpX00vSkK3g==} + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/jsonwebtoken@9.0.9': - resolution: {integrity: sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==} + '@types/jsonwebtoken@9.0.10': + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -261,8 +349,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@22.15.21': - resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==} + '@types/node@24.0.3': + resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -270,11 +358,11 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + '@types/send@0.17.5': + resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/serve-static@1.15.8': + resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -282,10 +370,79 @@ packages: '@types/whatwg-url@11.0.5': resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@typescript-eslint/eslint-plugin@8.34.1': + resolution: {integrity: sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.34.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.34.1': + resolution: {integrity: sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.34.1': + resolution: {integrity: sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.34.1': + resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.34.1': + resolution: {integrity: sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.34.1': + resolution: {integrity: sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.34.1': + resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.34.1': + resolution: {integrity: sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.34.1': + resolution: {integrity: sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.34.1': + resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.3: resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} @@ -293,6 +450,16 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -307,8 +474,18 @@ packages: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} engines: {node: '>=18'} - bson@6.10.3: - resolution: {integrity: sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + bson@6.10.4: + resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} engines: {node: '>=16.20.1'} buffer-equal-constant-time@1.0.1: @@ -326,6 +503,24 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@1.0.0: resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} engines: {node: '>= 0.6'} @@ -366,6 +561,9 @@ packages: supports-color: optional: true + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -408,23 +606,55 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} - eventsource-parser@3.0.2: - resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==} - engines: {node: '>=18.0.0'} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} - engines: {node: '>=18.0.0'} + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} - engines: {node: '>= 16'} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.29.0: + resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} express-validator@7.2.1: resolution: {integrity: sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==} @@ -440,13 +670,42 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + finalhandler@2.1.0: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -482,6 +741,22 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.2.0: + resolution: {integrity: sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==} + engines: {node: '>=18'} + google-auth-library@9.15.1: resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} engines: {node: '>=14'} @@ -494,10 +769,17 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + gtoken@7.1.0: resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} engines: {node: '>=14.0.0'} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -518,6 +800,22 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -525,6 +823,18 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} @@ -535,12 +845,22 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} @@ -561,6 +881,17 @@ packages: resolution: {integrity: sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==} engines: {node: '>=12.0.0'} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} @@ -579,6 +910,9 @@ packages: lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} @@ -600,6 +934,14 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} @@ -608,11 +950,18 @@ packages: resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} engines: {node: '>= 0.6'} + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + mongodb-connection-string-url@3.0.2: resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==} - mongodb@6.16.0: - resolution: {integrity: sha512-D1PNcdT0y4Grhou5Zi/qgipZOYeWrhLEpk33n3nm6LGtz61jvO88WlrWCK/bigMjpnOdAUKKQwsGIl0NtWMyYw==} + mongodb@6.17.0: + resolution: {integrity: sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==} engines: {node: '>=16.20.1'} peerDependencies: '@aws-sdk/credential-providers': ^3.188.0 @@ -638,8 +987,8 @@ packages: socks: optional: true - mongoose@8.15.0: - resolution: {integrity: sha512-WFKsY1q12ScGabnZWUB9c/QzZmz/ESorrV27OembB7Gz6rrh9m3GA4Srsv1uvW1s9AHO5DeZ6DdUTyF9zyNERQ==} + mongoose@8.16.0: + resolution: {integrity: sha512-gLuAZsbwY0PHjrvfuXvUkUq9tXjyAjN3ioXph5Y6Seu7/Uo8xJaM+rrMbL/x34K4T3UTgtXRyfoq1YU16qKyIw==} engines: {node: '>=16.20.1'} mpath@0.9.0: @@ -653,6 +1002,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -681,10 +1033,30 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -693,9 +1065,13 @@ packages: resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} engines: {node: '>=16'} - pkce-challenge@5.0.0: - resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} - engines: {node: '>=16.20.0'} + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} @@ -709,6 +1085,9 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -717,13 +1096,24 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -780,6 +1170,22 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -791,17 +1197,39 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + tsx@4.20.3: resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} hasBin: true + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + typescript-eslint@8.34.1: + resolution: {integrity: sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} @@ -841,6 +1269,10 @@ packages: engines: {node: '>= 8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -856,13 +1288,17 @@ packages: utf-8-validate: optional: true + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + zod-to-json-schema@3.24.5: resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} peerDependencies: zod: ^3.24.1 - zod@3.25.24: - resolution: {integrity: sha512-E77RpEqxeBGBVbcK/5QKQsLM+3u6aN7pVgiGJENbwYfdsExPS/xyyUMfmeM3eY32LBCIjuzv6XU505sHn2t+Kw==} + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} snapshots: @@ -941,98 +1377,158 @@ snapshots: '@esbuild/win32-x64@0.25.5': optional: true - '@google/genai@1.0.1(@modelcontextprotocol/sdk@1.12.0)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0)': + dependencies: + eslint: 9.29.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.29.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.2': + dependencies: + '@eslint/core': 0.15.0 + levn: 0.4.1 + + '@google/genai@1.5.1': dependencies: - '@modelcontextprotocol/sdk': 1.12.0 google-auth-library: 9.15.1 ws: 8.18.2 - zod: 3.25.24 - zod-to-json-schema: 3.24.5(zod@3.25.24) + zod: 3.25.67 + zod-to-json-schema: 3.24.5(zod@3.25.67) transitivePeerDependencies: - bufferutil - encoding - supports-color - utf-8-validate - '@modelcontextprotocol/sdk@1.12.0': + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': dependencies: - ajv: 6.12.6 - content-type: 1.0.5 - cors: 2.8.5 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - express: 5.1.0 - express-rate-limit: 7.5.0(express@5.1.0) - pkce-challenge: 5.0.0 - raw-body: 3.0.0 - zod: 3.25.24 - zod-to-json-schema: 3.24.5(zod@3.25.24) - transitivePeerDependencies: - - supports-color + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 - '@mongodb-js/saslprep@1.2.2': + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@mongodb-js/saslprep@1.3.0': dependencies: sparse-bitfield: 3.0.3 - '@types/body-parser@1.19.5': + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.15.21 + '@types/node': 24.0.3 '@types/connect@3.4.38': dependencies: - '@types/node': 22.15.21 + '@types/node': 24.0.3 - '@types/cookie-parser@1.4.8(@types/express@5.0.2)': + '@types/cookie-parser@1.4.9(@types/express@5.0.3)': dependencies: - '@types/express': 5.0.2 + '@types/express': 5.0.3 - '@types/cors@2.8.18': + '@types/cors@2.8.19': dependencies: - '@types/node': 22.15.21 + '@types/node': 24.0.3 + + '@types/estree@1.0.8': {} '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 22.15.21 + '@types/node': 24.0.3 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 + '@types/send': 0.17.5 - '@types/express@5.0.2': + '@types/express@5.0.3': dependencies: - '@types/body-parser': 1.19.5 + '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 5.0.6 - '@types/serve-static': 1.15.7 + '@types/serve-static': 1.15.8 - '@types/http-errors@2.0.4': {} + '@types/http-errors@2.0.5': {} - '@types/jsonwebtoken@9.0.9': + '@types/json-schema@7.0.15': {} + + '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 22.15.21 + '@types/node': 24.0.3 '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} - '@types/node@22.15.21': + '@types/node@24.0.3': dependencies: - undici-types: 6.21.0 + undici-types: 7.8.0 '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} - '@types/send@0.17.4': + '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.15.21 + '@types/node': 24.0.3 - '@types/serve-static@1.15.7': + '@types/serve-static@1.15.8': dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 22.15.21 - '@types/send': 0.17.4 + '@types/http-errors': 2.0.5 + '@types/node': 24.0.3 + '@types/send': 0.17.5 '@types/webidl-conversions@7.0.3': {} @@ -1040,11 +1536,109 @@ snapshots: dependencies: '@types/webidl-conversions': 7.0.3 + '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.34.1 + '@typescript-eslint/type-utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.34.1 + eslint: 9.29.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.34.1 + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.34.1 + debug: 4.4.1 + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.34.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.8.3) + '@typescript-eslint/types': 8.34.1 + debug: 4.4.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.34.1': + dependencies: + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/visitor-keys': 8.34.1 + + '@typescript-eslint/tsconfig-utils@8.34.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.34.1(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + debug: 4.4.1 + eslint: 9.29.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.34.1': {} + + '@typescript-eslint/typescript-estree@8.34.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.34.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.8.3) + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/visitor-keys': 8.34.1 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.34.1(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@typescript-eslint/scope-manager': 8.34.1 + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.34.1': + dependencies: + '@typescript-eslint/types': 8.34.1 + eslint-visitor-keys: 4.2.1 + accepts@2.0.0: dependencies: mime-types: 3.0.1 negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + agent-base@7.1.3: {} ajv@6.12.6: @@ -1054,6 +1648,14 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + base64-js@1.5.1: {} bcryptjs@3.0.2: {} @@ -1074,7 +1676,20 @@ snapshots: transitivePeerDependencies: - supports-color - bson@6.10.3: {} + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + bson@6.10.4: {} buffer-equal-constant-time@1.0.1: {} @@ -1090,6 +1705,21 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + content-disposition@1.0.0: dependencies: safe-buffer: 5.2.1 @@ -1122,6 +1752,8 @@ snapshots: dependencies: ms: 2.1.3 + deep-is@0.1.4: {} + depd@2.0.0: {} dotenv@16.5.0: {} @@ -1178,17 +1810,76 @@ snapshots: escape-html@1.0.3: {} - etag@1.8.1: {} + escape-string-regexp@4.0.0: {} + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} - eventsource-parser@3.0.2: {} + eslint-visitor-keys@4.2.1: {} - eventsource@3.0.7: + eslint@9.29.0: dependencies: - eventsource-parser: 3.0.2 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.29.0 + '@eslint/plugin-kit': 0.3.2 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color - express-rate-limit@7.5.0(express@5.1.0): + espree@10.4.0: dependencies: - express: 5.1.0 + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + etag@1.8.1: {} express-validator@7.2.1: dependencies: @@ -1221,7 +1912,7 @@ snapshots: router: 2.2.0 send: 1.2.0 serve-static: 2.2.0 - statuses: 2.0.1 + statuses: 2.0.2 type-is: 2.0.1 vary: 1.1.2 transitivePeerDependencies: @@ -1231,8 +1922,30 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + finalhandler@2.1.0: dependencies: debug: 4.4.1 @@ -1240,10 +1953,22 @@ snapshots: escape-html: 1.0.3 on-finished: 2.4.1 parseurl: 1.3.3 - statuses: 2.0.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + forwarded@0.2.0: {} fresh@2.0.0: {} @@ -1295,6 +2020,18 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.2.0: {} + google-auth-library@9.15.1: dependencies: base64-js: 1.5.1 @@ -1311,6 +2048,8 @@ snapshots: gopd@1.2.0: {} + graphemer@1.4.0: {} + gtoken@7.1.0: dependencies: gaxios: 6.7.1 @@ -1319,6 +2058,8 @@ snapshots: - encoding - supports-color + has-flag@4.0.0: {} + has-symbols@1.1.0: {} hasown@2.0.2: @@ -1344,22 +2085,49 @@ snapshots: dependencies: safer-buffer: 2.1.2 + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + inherits@2.0.4: {} ipaddr.js@1.9.1: {} + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + is-promise@4.0.0: {} is-stream@2.0.1: {} isexe@2.0.0: {} + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + json-bigint@1.0.0: dependencies: bignumber.js: 9.3.0 + json-buffer@3.0.1: {} + json-schema-traverse@0.4.1: {} + json-stable-stringify-without-jsonify@1.0.1: {} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 @@ -1397,6 +2165,19 @@ snapshots: kareem@2.6.3: {} + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + lodash.includes@4.3.0: {} lodash.isboolean@3.0.3: {} @@ -1409,6 +2190,8 @@ snapshots: lodash.isstring@4.0.1: {} + lodash.merge@4.6.2: {} + lodash.once@4.1.1: {} lodash@4.17.21: {} @@ -1421,28 +2204,43 @@ snapshots: merge-descriptors@2.0.0: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + mime-db@1.54.0: {} mime-types@3.0.1: dependencies: mime-db: 1.54.0 + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + mongodb-connection-string-url@3.0.2: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 14.2.0 - mongodb@6.16.0: + mongodb@6.17.0: dependencies: - '@mongodb-js/saslprep': 1.2.2 - bson: 6.10.3 + '@mongodb-js/saslprep': 1.3.0 + bson: 6.10.4 mongodb-connection-string-url: 3.0.2 - mongoose@8.15.0: + mongoose@8.16.0: dependencies: - bson: 6.10.3 + bson: 6.10.4 kareem: 2.6.3 - mongodb: 6.16.0 + mongodb: 6.17.0 mpath: 0.9.0 mquery: 5.0.0 ms: 2.1.3 @@ -1467,6 +2265,8 @@ snapshots: ms@2.1.3: {} + natural-compare@1.4.0: {} + negotiator@1.0.0: {} node-fetch@2.7.0: @@ -1485,13 +2285,38 @@ snapshots: dependencies: wrappy: 1.0.2 + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} + path-key@3.1.1: {} path-to-regexp@8.2.0: {} - pkce-challenge@5.0.0: {} + picomatch@2.3.1: {} + + prelude-ls@1.2.1: {} proxy-addr@2.0.7: dependencies: @@ -1504,6 +2329,8 @@ snapshots: dependencies: side-channel: 1.1.0 + queue-microtask@1.2.3: {} + range-parser@1.2.1: {} raw-body@3.0.0: @@ -1513,8 +2340,12 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} + reusify@1.1.0: {} + router@2.2.0: dependencies: debug: 4.4.1 @@ -1525,6 +2356,10 @@ snapshots: transitivePeerDependencies: - supports-color + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safe-buffer@5.2.1: {} safer-buffer@2.1.2: {} @@ -1543,7 +2378,7 @@ snapshots: ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 - statuses: 2.0.1 + statuses: 2.0.2 transitivePeerDependencies: - supports-color @@ -1600,6 +2435,18 @@ snapshots: statuses@2.0.1: {} + statuses@2.0.2: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + toidentifier@1.0.1: {} tr46@0.0.3: {} @@ -1608,6 +2455,10 @@ snapshots: dependencies: punycode: 2.3.1 + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + tsx@4.20.3: dependencies: esbuild: 0.25.5 @@ -1615,13 +2466,29 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + type-is@2.0.1: dependencies: content-type: 1.0.5 media-typer: 1.1.0 mime-types: 3.0.1 - undici-types@6.21.0: {} + typescript-eslint@8.34.1(eslint@9.29.0)(typescript@5.8.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/parser': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.34.1(eslint@9.29.0)(typescript@5.8.3) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + typescript@5.8.3: {} + + undici-types@7.8.0: {} unpipe@1.0.0: {} @@ -1653,12 +2520,16 @@ snapshots: dependencies: isexe: 2.0.0 + word-wrap@1.2.5: {} + wrappy@1.0.2: {} ws@8.18.2: {} - zod-to-json-schema@3.24.5(zod@3.25.24): + yocto-queue@0.1.0: {} + + zod-to-json-schema@3.24.5(zod@3.25.67): dependencies: - zod: 3.25.24 + zod: 3.25.67 - zod@3.25.24: {} + zod@3.25.67: {} From 3cffc6b620b7864a3daf0a4af90f292408e76dc2 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:15:12 +0800 Subject: [PATCH 14/84] refactor: move Express app setup to app.ts and simplify index.ts startup logic --- src/app.ts | 37 +++++++++++++++++++++++++++++++++++++ src/index.ts | 31 ++----------------------------- 2 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 src/app.ts diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..82f0df3 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,37 @@ +import express from "express"; +import dotenv from "dotenv"; +import cors from "cors"; +import cookieParser from "cookie-parser"; + +import authRoutes from "./routes/auth.routes"; +import chatRoutes from "./routes/chat.routes"; +import messageRoutes from "./routes/message.routes"; +import { HTTPResponse } from "./util/http-response"; +import { authenticateToken } from "./middleware/authenticate-token"; +import { Request, Response } from "express"; + +dotenv.config(); + +const app = express(); + +// Middleware +app.use(express.json()); +if (process.env.ENABLE_CORS === "true") { + app.use( + cors({ + origin: process.env.CLIENT_IP, + credentials: true, + }) + ); +} +app.use(cookieParser()); + +// Routes +app.get("/health", (_req: Request, res: Response) => { + HTTPResponse.ok(res); +}); +app.use("/auth", authRoutes); +app.use("/chat", authenticateToken, chatRoutes); +app.use("/message", authenticateToken, messageRoutes); + +export default app; diff --git a/src/index.ts b/src/index.ts index c041729..1c742ec 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,35 +1,8 @@ -import dotenv from "dotenv"; -import express from "express"; -import authRoutes from "./routes/auth.routes"; -import cors from "cors"; -import cookieParser from "cookie-parser"; -import convoRoutes from "./routes/chat.routes"; -import messageRoutes from "./routes/message.routes"; -import { authenticateToken } from "./middleware/authenticate-token"; -import { connectDB, flushDB } from "./util/database"; +import app from "./app"; +import { connectDB } from "./util/database"; -dotenv.config(); - -// Initialize express -const app = express(); const PORT = process.env.PORT || 8000; -// middlewares -app.use(express.json()); -if (process.env.ENABLE_CORS === "true") { - app.use( - cors({ - origin: process.env.CLIENT_IP, - credentials: true, - }) - ); -} -app.use(cookieParser()); - -app.use("/auth", authRoutes); -app.use("/chat", authenticateToken, convoRoutes); -app.use("/message", authenticateToken, messageRoutes); - // Connect DB and start server const initServer = async () => { await connectDB(); From 10f797c3280ea40c5692b2c02fccbea1042b1fda Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:15:50 +0800 Subject: [PATCH 15/84] fix: use post hook for cascading delete of messages in chat model --- src/model/chat.model.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/model/chat.model.ts b/src/model/chat.model.ts index ecddafa..5fbac91 100644 --- a/src/model/chat.model.ts +++ b/src/model/chat.model.ts @@ -76,12 +76,10 @@ const chatSchema = new mongoose.Schema( } ); -chatSchema.pre("findOneAndDelete", async function (next) { - const chat = await this.model.findOne(this.getFilter()); - if (chat) { - await Message.deleteMany({ chat: chat._id }); +chatSchema.post("findOneAndDelete", async function (doc) { + if (doc) { + await Message.deleteMany({ chat: doc._id }); } - next(); }); const Chat = mongoose.model("chat", chatSchema); From 9b842646d3a4a59b857c9768aac5959c29ad0283 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:16:59 +0800 Subject: [PATCH 16/84] refactor: use unknown instead of any for error handling in auth controller --- src/controller/auth.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controller/auth.controller.ts b/src/controller/auth.controller.ts index 64a7586..37a40e0 100644 --- a/src/controller/auth.controller.ts +++ b/src/controller/auth.controller.ts @@ -85,7 +85,7 @@ export const loginUser = async (req: Request, res: Response) => { // return success response HTTPResponse.ok(res, "Login success"); - } catch (error: any) { + } catch (error: unknown) { console.error(error); HTTPResponse.internalServerError( res, From 3369eb92d1c764a82a94f856bb9a2d827cfb767b Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:17:28 +0800 Subject: [PATCH 17/84] refactor: use findOneAndDelete for chat deletion in deleteChat controller --- src/controller/chat.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controller/chat.controller.ts b/src/controller/chat.controller.ts index c9c84b4..060ce87 100644 --- a/src/controller/chat.controller.ts +++ b/src/controller/chat.controller.ts @@ -110,7 +110,7 @@ export const deleteChat = async (req: AuthRequest, res: Response) => { return; } - await toDelete.deleteOne(); + await Chat.findOneAndDelete({ _id: id }); HTTPResponse.ok(res, "Chat successfully deleted"); } catch (error) { console.error(error); From 11fd3a4d6ca0b428c88e0399821657b3d9413839 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:17:54 +0800 Subject: [PATCH 18/84] feat: return 404 if no messages found for given chatId --- src/controller/message.controller.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controller/message.controller.ts b/src/controller/message.controller.ts index a053d06..69404e6 100644 --- a/src/controller/message.controller.ts +++ b/src/controller/message.controller.ts @@ -85,12 +85,12 @@ export const getMessagesByChatId = async (req: AuthRequest, res: Response) => { }) .lean(); - res.status(200).json({ - success: true, - message: `messages found for chatId ${chatId}`, - data: messages, - }); - } catch (error: any) { + if (!messages || messages.length === 0) { + HTTPResponse.notFound(res, `No message found for chatId ${chatId}`); + } + + HTTPResponse.ok(res, `Messages found for chatId ${chatId}`, messages); + } catch (error: unknown) { console.log(error); HTTPResponse.internalServerError( res, From 367feec972ce5677853a19ed31736f0a5d710725 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 19 Jun 2025 21:18:19 +0800 Subject: [PATCH 19/84] refactor: improve type safety in token auth middleware by using unknown error type --- src/middleware/authenticate-token.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/middleware/authenticate-token.ts b/src/middleware/authenticate-token.ts index ae0e6e8..2647718 100644 --- a/src/middleware/authenticate-token.ts +++ b/src/middleware/authenticate-token.ts @@ -20,18 +20,17 @@ export const authenticateToken = async ( }; req.userId = decoded.userId; next(); - } catch (err: any) { - const isExpired = err.name === "TokenExpiredError"; - const isInvalid = err.name === "JsonWebTokenError"; + } catch (err: unknown) { + if (err instanceof Error) { + if (err.name === "TokenExpiredError") { + HTTPResponse.forbidden(res, "Token has expired."); + return; + } - if (isExpired) { - HTTPResponse.forbidden(res, "Token has expired."); - return; - } - - if (isInvalid) { - HTTPResponse.forbidden(res, "Invalid token"); - return; + if (err.name === "JsonWebTokenError") { + HTTPResponse.forbidden(res, "Invalid token"); + return; + } } HTTPResponse.forbidden(res, "Unable to verify token"); From a0a09e677c2a7955b2a046c9d1388e7bec9093db Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:44:42 +0800 Subject: [PATCH 20/84] test: add integration tests for registration and login routes using Jest and Supertest --- src/test/integration/auth.test.ts | 119 ++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/test/integration/auth.test.ts diff --git a/src/test/integration/auth.test.ts b/src/test/integration/auth.test.ts new file mode 100644 index 0000000..f59d374 --- /dev/null +++ b/src/test/integration/auth.test.ts @@ -0,0 +1,119 @@ +import mongoose from "mongoose"; +import User from "../../model/user.model"; +import request from "supertest"; +import app from "../../app"; + +const testUser = { + firstName: "Test", + lastName: "User", + email: "testuser@email.com", + password: "testpassword", +}; + +const createUser = async () => { + return await User.create({ ...testUser }); +}; + +const postRegister = async (overrides = {}) => { + return request(app) + .post("/auth/register") + .send({ ...testUser, ...overrides }); +}; + +const postLogin = async (overrides = {}) => { + return request(app) + .post("/auth/login") + .send({ ...testUser, ...overrides }); +}; + +describe("/auth", () => { + beforeAll(async () => { + await mongoose.connect(process.env.MONGO_URI_TEST!); + }); + + beforeEach(async () => { + await User.deleteMany({}); + }); + + afterAll(async () => { + await mongoose.connection.close(); + }); + + // Missing fields + describe("POST /auth/register", () => { + test("should return 400 if required fields are missing", async () => { + const res = await postRegister({ + firstName: "", + lastName: "", + email: "", + password: "", + }); + + expect(res.status).toEqual(400); + expect(res.body.success).toEqual(false); + expect(res.body.message).toBe("Validation failed"); + }); + + // Already exists + test("should return 409 if user already exists", async () => { + await createUser(); + + const res = await postRegister(); + + expect(res.status).toEqual(409); + expect(res.body.success).toEqual(false); + expect(res.body.message).toBe("User already exists"); + }); + + // Successful sign up + test("should return 201 created and create user if data is valid", async () => { + const res = await postRegister(); + + expect(res.status).toEqual(201); + expect(res.body.success).toEqual(true); + expect(res.body.data).toBeDefined(); + }); + }); + + describe("POST /auth/login", () => { + // Successful login + test("should return 200 and login successfully with valid credentials", async () => { + await createUser(); + + const res = await postLogin(); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + expect(res.body.message).toBe("Login success"); + }); + + // Non existing user + test("should return 404 if user does not exist", async () => { + const res = await postLogin({ email: "nouser@email.com" }); + + expect(res.status).toEqual(404); + expect(res.body.success).toEqual(false); + expect(res.body.message).toBe("User not found"); + }); + + // Incorrect password + test("should return 400 if password is incorrect", async () => { + await createUser(); + + const res = await postLogin({ password: "wrongpassword" }); + + expect(res.status).toEqual(400); + expect(res.body.success).toEqual(false); + expect(res.body.message).toBe("Incorrect password"); + }); + + // Missing fields + test("should return 400 if email or password is missing", async () => { + const res = await postLogin({ email: "", password: "" }); + + expect(res.status).toEqual(400); + expect(res.body.success).toEqual(false); + expect(res.body.message).toBe("Email and password required"); + }); + }); +}); From 0c2a118feb3db9528f1b6b4adba25d61f6101ef0 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:45:41 +0800 Subject: [PATCH 21/84] chore: add basic Jest config for ts-jest with Node environment --- jest.config.js | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..b28cdff --- /dev/null +++ b/jest.config.js @@ -0,0 +1,2 @@ +export const preset = "ts-jest"; +export const testEnvironment = "node"; From b550a9e9d1e2e4e28777b6b60a076883a658934c Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:46:46 +0800 Subject: [PATCH 22/84] chore: rename and add MongoDB URI variables for clarity and testing --- .env.template | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 9852a65..8f777bb 100644 --- a/.env.template +++ b/.env.template @@ -4,6 +4,7 @@ ENABLE_CORS= CLIENT_IP= JWT_SECRET= IS_DOCKERIZED= -MONGO_ATLAS_URI= -MONGO_DOCKER_URI= +MONGO_URI_ATLAS= +MONGO_URI_TEST= +MONGO_URI_DOCKER= GEMINI_KEY= \ No newline at end of file From a13e2c466a9748e687b3e9ed5f88a2ab479f5739 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:47:09 +0800 Subject: [PATCH 23/84] chore: add coverage and dist dir to .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1dcef2d..99f3a73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -.env \ No newline at end of file +.env +coverage +dist \ No newline at end of file From a3abc2a2a1c8e721c0bacc4de573a50dc83859f3 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:47:42 +0800 Subject: [PATCH 24/84] chore: rename project, update scripts, and add testing dependencies - Renamed package to brainbytes-api - Added Jest, Supertest, and ts-jest for testing - Added clean, build, and test scripts - Updated lint commands for better glob handling --- package.json | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c05bf08..3ac6bbc 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "brainbytes", + "name": "brainbytes-api", "version": "1.0.0", "description": "", "main": "src/index.ts", @@ -7,8 +7,12 @@ "start": "tsx src/index.ts", "dev": "tsx watch src/index.ts", "compose": "docker-compose up --build", - "lint": "npx eslint ./src/**", - "lint:fix": "npx eslint ./src/** --fix" + "lint": "eslint 'src/**/*.ts'", + "lint:fix": "eslint 'src/**/*.ts' --fix", + "test": "jest", + "test:coverage": "jest --coverage", + "build": "tsc", + "clean": "rmdir /s /q dist" }, "keywords": [], "author": "", @@ -30,11 +34,16 @@ "@types/cookie-parser": "^1.4.9", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", + "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.0.3", + "@types/supertest": "^6.0.3", "dotenv": "^16.5.0", "eslint": "^9.29.0", "globals": "^16.2.0", + "jest": "^30.0.2", + "supertest": "^7.1.1", + "ts-jest": "^29.4.0", "tsx": "^4.20.3", "typescript": "^5.8.3", "typescript-eslint": "^8.34.1" From 9ff02f0f1306c672c356986d33b5a29f0d54f593 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:47:52 +0800 Subject: [PATCH 25/84] chore: rename project, update scripts, and add testing dependencies - Renamed package to brainbytes-api - Added Jest, Supertest, and ts-jest for testing - Added clean, build, and test scripts - Updated lint commands for better glob handling --- pnpm-lock.yaml | 2985 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 2887 insertions(+), 98 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b9d12e..641d04f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -48,12 +48,18 @@ importers: '@types/express': specifier: ^5.0.3 version: 5.0.3 + '@types/jest': + specifier: ^30.0.0 + version: 30.0.0 '@types/jsonwebtoken': specifier: ^9.0.10 version: 9.0.10 '@types/node': specifier: ^24.0.3 version: 24.0.3 + '@types/supertest': + specifier: ^6.0.3 + version: 6.0.3 dotenv: specifier: ^16.5.0 version: 16.5.0 @@ -63,6 +69,15 @@ importers: globals: specifier: ^16.2.0 version: 16.2.0 + jest: + specifier: ^30.0.2 + version: 30.0.2(@types/node@24.0.3) + supertest: + specifier: ^7.1.1 + version: 7.1.1 + ts-jest: + specifier: ^29.4.0 + version: 29.4.0(@babel/core@7.27.4)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@30.0.2(@babel/core@7.27.4))(jest-util@30.0.2)(jest@30.0.2(@types/node@24.0.3))(typescript@5.8.3) tsx: specifier: ^4.20.3 version: 4.20.3 @@ -75,6 +90,180 @@ importers: packages: + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.27.5': + resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.27.5': + resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.27.4': + resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + '@esbuild/aix-ppc64@0.25.5': resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} @@ -296,9 +485,128 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@30.0.2': + resolution: {integrity: sha512-krGElPU0FipAqpVZ/BRZOy0MZh/ARdJ0Nj+PiH1ykFY1+VpBlYNLjdjVA5CFKxnKR6PFqFutO4Z7cdK9BlGiDA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/core@30.0.2': + resolution: {integrity: sha512-mUMFdDtYWu7la63NxlyNIhgnzynszxunXWrtryR7bV24jV9hmi7XCZTzZHaLJjcBU66MeUAPZ81HjwASVpYhYQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment@30.0.2': + resolution: {integrity: sha512-hRLhZRJNxBiOhxIKSq2UkrlhMt3/zVFQOAi5lvS8T9I03+kxsbflwHJEF+eXEYXCrRGRhHwECT7CDk6DyngsRA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.0.2': + resolution: {integrity: sha512-FHF2YdtFBUQOo0/qdgt+6UdBFcNPF/TkVzcc+4vvf8uaBzUlONytGBeeudufIHHW1khRfM1sBbRT1VCK7n/0dQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect@30.0.2': + resolution: {integrity: sha512-blWRFPjv2cVfh42nLG6L3xIEbw+bnuiZYZDl/BZlsNG/i3wKV6FpPZ2EPHguk7t5QpLaouIu+7JmYO4uBR6AOg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@30.0.2': + resolution: {integrity: sha512-jfx0Xg7l0gmphTY9UKm5RtH12BlLYj/2Plj6wXjVW5Era4FZKfXeIvwC67WX+4q8UCFxYS20IgnMcFBcEU0DtA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.0.1': + resolution: {integrity: sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@30.0.2': + resolution: {integrity: sha512-DwTtus9jjbG7b6jUdkcVdptf0wtD1v153A+PVwWB/zFwXhqu6hhtSd+uq88jofMhmYPtkmPmVGUBRNCZEKXn+w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/reporters@30.0.2': + resolution: {integrity: sha512-l4QzS/oKf57F8WtPZK+vvF4Io6ukplc6XgNFu4Hd/QxaLEO9f+8dSFzUua62Oe0HKlCUjKHpltKErAgDiMJKsA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/schemas@30.0.1': + resolution: {integrity: sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/snapshot-utils@30.0.1': + resolution: {integrity: sha512-6Dpv7vdtoRiISEFwYF8/c7LIvqXD7xDXtLPNzC2xqAfBznKip0MQM+rkseKwUPUpv2PJ7KW/YsnwWXrIL2xF+A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@30.0.1': + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-result@30.0.2': + resolution: {integrity: sha512-KKMuBKkkZYP/GfHMhI+cH2/P3+taMZS3qnqqiPC1UXZTJskkCS+YU/ILCtw5anw1+YsTulDHFpDo70mmCedW8w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-sequencer@30.0.2': + resolution: {integrity: sha512-fbyU5HPka0rkalZ3MXVvq0hwZY8dx3Y6SCqR64zRmh+xXlDeFl0IdL4l9e7vp4gxEXTYHbwLFA1D+WW5CucaSw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/transform@30.0.2': + resolution: {integrity: sha512-kJIuhLMTxRF7sc0gPzPtCDib/V9KwW3I2U25b+lYCYMVqHHSrcZopS8J8H+znx9yixuFv+Iozl8raLt/4MoxrA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@30.0.1': + resolution: {integrity: sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@mongodb-js/saslprep@1.3.0': resolution: {integrity: sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==} + '@napi-rs/wasm-runtime@0.2.11': + resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==} + + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -311,6 +619,41 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.7': + resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@sinclair/typebox@0.34.35': + resolution: {integrity: sha512-C6ypdODf2VZkgRT6sFM8E1F8vR+HcffniX0Kp8MsU8PIfrlXbNCBz0jzj17GjdmjTx1OtZzdH8+iALL21UjF5A==} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@13.0.5': + resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} + + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.7': + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -322,6 +665,9 @@ packages: peerDependencies: '@types/express': '*' + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/cors@2.8.19': resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} @@ -337,12 +683,27 @@ packages: '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jest@30.0.0': + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/jsonwebtoken@9.0.10': resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -364,12 +725,27 @@ packages: '@types/serve-static@1.15.8': resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==} + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} '@types/whatwg-url@11.0.5': resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@typescript-eslint/eslint-plugin@8.34.1': resolution: {integrity: sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -429,6 +805,104 @@ packages: resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unrs/resolver-binding-android-arm-eabi@1.9.0': + resolution: {integrity: sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.9.0': + resolution: {integrity: sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.9.0': + resolution: {integrity: sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.9.0': + resolution: {integrity: sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.9.0': + resolution: {integrity: sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.9.0': + resolution: {integrity: sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.9.0': + resolution: {integrity: sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.9.0': + resolution: {integrity: sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.9.0': + resolution: {integrity: sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.9.0': + resolution: {integrity: sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.9.0': + resolution: {integrity: sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.9.0': + resolution: {integrity: sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.9.0': + resolution: {integrity: sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.9.0': + resolution: {integrity: sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.9.0': + resolution: {integrity: sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.9.0': + resolution: {integrity: sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.9.0': + resolution: {integrity: sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.9.0': + resolution: {integrity: sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.9.0': + resolution: {integrity: sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==} + cpu: [x64] + os: [win32] + accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -450,13 +924,74 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + babel-jest@30.0.2: + resolution: {integrity: sha512-A5kqR1/EUTidM2YC2YMEUDP2+19ppgOwK0IAd9Swc3q2KqFb5f9PtRUXVeZcngu0z5mDMyZ9zH2huJZSOMLiTQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 + + babel-plugin-istanbul@7.0.0: + resolution: {integrity: sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==} + engines: {node: '>=12'} + + babel-plugin-jest-hoist@30.0.1: + resolution: {integrity: sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + babel-preset-current-node-syntax@1.1.0: + resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} + peerDependencies: + '@babel/core': ^7.0.0 + + babel-preset-jest@30.0.1: + resolution: {integrity: sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -484,6 +1019,18 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browserslist@4.25.0: + resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + bson@6.10.4: resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} engines: {node: '>=16.20.1'} @@ -491,6 +1038,9 @@ packages: buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -507,10 +1057,43 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001723: + resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + ci-info@4.2.0: + resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + engines: {node: '>=8'} + + cjs-module-lexer@2.1.0: + resolution: {integrity: sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -518,6 +1101,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -529,6 +1119,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-parser@1.4.7: resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==} engines: {node: '>= 0.8.0'} @@ -544,6 +1137,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -561,13 +1157,36 @@ packages: supports-color: optional: true + dedent@1.6.0: + resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + dotenv@16.5.0: resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} engines: {node: '>=12'} @@ -576,16 +1195,40 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.170: + resolution: {integrity: sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -598,14 +1241,26 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.25.5: resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -636,6 +1291,11 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -656,8 +1316,20 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} - express-validator@7.2.1: - resolution: {integrity: sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==} + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + exit-x@0.2.2: + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} + + expect@30.0.2: + resolution: {integrity: sha512-YN9Mgv2mtTWXVmifQq3QT+ixCL/uLuLJw+fdp8MOjKqu8K3XQh3o5aulMM1tn+O2DdrWNxLZTeJsCY/VofUA0A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + express-validator@7.2.1: + resolution: {integrity: sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==} engines: {node: '>= 8.0.0'} express@5.1.0: @@ -680,13 +1352,22 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -695,6 +1376,10 @@ packages: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -706,6 +1391,18 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + engines: {node: '>= 6'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -714,6 +1411,9 @@ packages: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -730,14 +1430,30 @@ packages: resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} engines: {node: '>=14'} + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -749,6 +1465,18 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -769,6 +1497,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -784,10 +1515,17 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -796,6 +1534,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -812,10 +1554,19 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -823,10 +1574,21 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -845,22 +1607,198 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jake@10.9.2: + resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} + engines: {node: '>=10'} + hasBin: true + + jest-changed-files@30.0.2: + resolution: {integrity: sha512-Ius/iRST9FKfJI+I+kpiDh8JuUlAISnRszF9ixZDIqJF17FckH5sOzKC8a0wd0+D+8em5ADRHA5V5MnfeDk2WA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-circus@30.0.2: + resolution: {integrity: sha512-NRozwx4DaFHcCUtwdEd/0jBLL1imyMrCbla3vF//wdsB2g6jIicMbjx9VhqE/BYU4dwsOQld+06ODX0oZ9xOLg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-cli@30.0.2: + resolution: {integrity: sha512-yQ6Qz747oUbMYLNAqOlEby+hwXx7WEJtCl0iolBRpJhr2uvkBgiVMrvuKirBc8utwQBnkETFlDUkYifbRpmBrQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@30.0.2: + resolution: {integrity: sha512-vo0fVq+uzDcXETFVnCUyr5HaUCM8ES6DEuS9AFpma34BVXMRRNlsqDyiW5RDHaEFoeFlJHoI4Xjh/WSYIAL58g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@types/node': '*' + esbuild-register: '>=3.4.0' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-diff@30.0.2: + resolution: {integrity: sha512-2UjrNvDJDn/oHFpPrUTVmvYYDNeNtw2DlY3er8bI6vJJb9Fb35ycp/jFLd5RdV59tJ8ekVXX3o/nwPcscgXZJQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@30.0.1: + resolution: {integrity: sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-each@30.0.2: + resolution: {integrity: sha512-ZFRsTpe5FUWFQ9cWTMguCaiA6kkW5whccPy9JjD1ezxh+mJeqmz8naL8Fl/oSbNJv3rgB0x87WBIkA5CObIUZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-environment-node@30.0.2: + resolution: {integrity: sha512-XsGtZ0H+a70RsxAQkKuIh0D3ZlASXdZdhpOSBq9WRPq6lhe0IoQHGW0w9ZUaPiZQ/CpkIdprvlfV1QcXcvIQLQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-haste-map@30.0.2: + resolution: {integrity: sha512-telJBKpNLeCb4MaX+I5k496556Y2FiKR/QLZc0+MGBYl4k3OO0472drlV2LUe7c1Glng5HuAu+5GLYp//GpdOQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-leak-detector@30.0.2: + resolution: {integrity: sha512-U66sRrAYdALq+2qtKffBLDWsQ/XoNNs2Lcr83sc9lvE/hEpNafJlq2lXCPUBMNqamMECNxSIekLfe69qg4KMIQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.0.2: + resolution: {integrity: sha512-1FKwgJYECR8IT93KMKmjKHSLyru0DqguThov/aWpFccC0wbiXGOxYEu7SScderBD7ruDOpl7lc5NG6w3oxKfaA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.0.2: + resolution: {integrity: sha512-vXywcxmr0SsKXF/bAD7t7nMamRvPuJkras00gqYeB1V0WllxZrbZ0paRr3XqpFU2sYYjD0qAaG2fRyn/CGZ0aw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.0.2: + resolution: {integrity: sha512-PnZOHmqup/9cT/y+pXIVbbi8ID6U1XHRmbvR7MvUy4SLqhCbwpkmXhLbsWbGewHrV5x/1bF7YDjs+x24/QSvFA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve-dependencies@30.0.2: + resolution: {integrity: sha512-Lp1iIXpsF5fGM4vyP8xHiIy2H5L5yO67/nXoYJzH4kz+fQmO+ZMKxzYLyWxYy4EeCLeNQ6a9OozL+uHZV2iuEA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve@30.0.2: + resolution: {integrity: sha512-q/XT0XQvRemykZsvRopbG6FQUT6/ra+XV6rPijyjT6D0msOyCvR2A5PlWZLd+fH0U8XWKZfDiAgrUNDNX2BkCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runner@30.0.2: + resolution: {integrity: sha512-6H+CIFiDLVt1Ix6jLzASXz3IoIiDukpEIxL9FHtDQ2BD/k5eFtDF5e5N9uItzRE3V1kp7VoSRyrGBytXKra4xA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runtime@30.0.2: + resolution: {integrity: sha512-H1a51/soNOeAjoggu6PZKTH7DFt8JEGN4mesTSwyqD2jU9PXD04Bp6DKbt2YVtQvh2JcvH2vjbkEerCZ3lRn7A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-snapshot@30.0.2: + resolution: {integrity: sha512-KeoHikoKGln3OlN7NS7raJ244nIVr2K46fBTNdfuxqYv2/g4TVyWDSO4fmk08YBJQMjs3HNfG1rlLfL/KA+nUw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@30.0.2: + resolution: {integrity: sha512-8IyqfKS4MqprBuUpZNlFB5l+WFehc8bfCe1HSZFHzft2mOuND8Cvi9r1musli+u6F3TqanCZ/Ik4H4pXUolZIg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@30.0.2: + resolution: {integrity: sha512-noOvul+SFER4RIvNAwGn6nmV2fXqBq67j+hKGHKGFCmK4ks/Iy1FSrqQNBLGKlu4ZZIRL6Kg1U72N1nxuRCrGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watcher@30.0.2: + resolution: {integrity: sha512-vYO5+E7jJuF+XmONr6CrbXdlYrgvZqtkn6pdkgjt/dU64UAdc0v1cAVaAeWtAfUUMScxNmnUjKPUMdCpNVASwg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@30.0.2: + resolution: {integrity: sha512-RN1eQmx7qSLFA+o9pfJKlqViwL5wt+OL3Vff/A+/cPsmuw7NPwfgl33AP+/agRmHzPOFgXviRycR9kYwlcRQXg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest@30.0.2: + resolution: {integrity: sha512-HlSEiHRcmTuGwNyeawLTEzpQUMFn+f741FfoNg7RXG2h0WLJKozVCpcQLT0GW17H6kNCqRwGf+Ii/I1YVNvEGQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonwebtoken@9.0.2: resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} engines: {node: '>=12', npm: '>=6'} @@ -884,10 +1822,21 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -910,6 +1859,9 @@ packages: lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -919,6 +1871,22 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -934,29 +1902,61 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + mime-types@3.0.1: resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} engines: {node: '>= 0.6'} + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + mongodb-connection-string-url@3.0.2: resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==} @@ -1002,6 +2002,11 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + napi-postinstall@0.2.4: + resolution: {integrity: sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -1018,6 +2023,20 @@ packages: encoding: optional: true + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1033,22 +2052,45 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -1057,22 +2099,49 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@8.2.0: resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} engines: {node: '>=16'} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + pretty-format@30.0.2: + resolution: {integrity: sha512-yC5/EBSOrTtqhCKfLHqoUIAXVRZnukHPwWBJWR7h84Q3Be1DRQZLncwcfLoPA5RPQ65qfiCMqgYwdUuQ//eVpg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -1081,6 +2150,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@7.0.1: + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -1096,10 +2168,25 @@ packages: resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} engines: {node: '>= 0.8'} + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -1120,6 +2207,10 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} @@ -1163,9 +2254,34 @@ packages: sift@17.1.3: resolution: {integrity: sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==} + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + sparse-bitfield@3.0.3: resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -1174,14 +2290,65 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + superagent@10.2.1: + resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==} + engines: {node: '>=14.18.0'} + + supertest@7.1.1: + resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==} + engines: {node: '>=14.18.0'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + synckit@0.11.8: + resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} + engines: {node: ^14.18.0 || >=16.0.0} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1203,6 +2370,36 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-jest@29.4.0: + resolution: {integrity: sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.20.3: resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} @@ -1212,6 +2409,18 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -1235,6 +2444,15 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unrs-resolver@1.9.0: + resolution: {integrity: sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==} + + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -1242,6 +2460,10 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + validator@13.12.0: resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} engines: {node: '>= 0.10'} @@ -1250,6 +2472,9 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -1273,9 +2498,21 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ws@8.18.2: resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} engines: {node: '>=10.0.0'} @@ -1288,6 +2525,21 @@ packages: utf-8-validate: optional: true + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -1302,17 +2554,225 @@ packages: snapshots: - '@esbuild/aix-ppc64@0.25.5': - optional: true + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 - '@esbuild/android-arm64@0.25.5': - optional: true + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 - '@esbuild/android-arm@0.25.5': - optional: true + '@babel/compat-data@7.27.5': {} - '@esbuild/android-x64@0.25.5': - optional: true + '@babel/core@7.27.4': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.27.5': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.27.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.25.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.27.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.27.6': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + + '@babel/parser@7.27.5': + dependencies: + '@babel/types': 7.27.6 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.4)': + dependencies: + '@babel/core': 7.27.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + + '@babel/traverse@7.27.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.5 + '@babel/parser': 7.27.5 + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + debug: 4.4.1 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.27.6': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bcoe/v8-coverage@0.2.3': {} + + '@emnapi/core@1.4.3': + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true '@esbuild/darwin-arm64@0.25.5': optional: true @@ -1450,10 +2910,234 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@30.0.2': + dependencies: + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + chalk: 4.1.2 + jest-message-util: 30.0.2 + jest-util: 30.0.2 + slash: 3.0.0 + + '@jest/core@30.0.2': + dependencies: + '@jest/console': 30.0.2 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.0.2 + '@jest/test-result': 30.0.2 + '@jest/transform': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 4.2.0 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-changed-files: 30.0.2 + jest-config: 30.0.2(@types/node@24.0.3) + jest-haste-map: 30.0.2 + jest-message-util: 30.0.2 + jest-regex-util: 30.0.1 + jest-resolve: 30.0.2 + jest-resolve-dependencies: 30.0.2 + jest-runner: 30.0.2 + jest-runtime: 30.0.2 + jest-snapshot: 30.0.2 + jest-util: 30.0.2 + jest-validate: 30.0.2 + jest-watcher: 30.0.2 + micromatch: 4.0.8 + pretty-format: 30.0.2 + slash: 3.0.0 + transitivePeerDependencies: + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + '@jest/diff-sequences@30.0.1': {} + + '@jest/environment@30.0.2': + dependencies: + '@jest/fake-timers': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + jest-mock: 30.0.2 + + '@jest/expect-utils@30.0.2': + dependencies: + '@jest/get-type': 30.0.1 + + '@jest/expect@30.0.2': + dependencies: + expect: 30.0.2 + jest-snapshot: 30.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@30.0.2': + dependencies: + '@jest/types': 30.0.1 + '@sinonjs/fake-timers': 13.0.5 + '@types/node': 24.0.3 + jest-message-util: 30.0.2 + jest-mock: 30.0.2 + jest-util: 30.0.2 + + '@jest/get-type@30.0.1': {} + + '@jest/globals@30.0.2': + dependencies: + '@jest/environment': 30.0.2 + '@jest/expect': 30.0.2 + '@jest/types': 30.0.1 + jest-mock: 30.0.2 + transitivePeerDependencies: + - supports-color + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 24.0.3 + jest-regex-util: 30.0.1 + + '@jest/reporters@30.0.2': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 30.0.2 + '@jest/test-result': 30.0.2 + '@jest/transform': 30.0.2 + '@jest/types': 30.0.1 + '@jridgewell/trace-mapping': 0.3.25 + '@types/node': 24.0.3 + chalk: 4.1.2 + collect-v8-coverage: 1.0.2 + exit-x: 0.2.2 + glob: 10.4.5 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + jest-message-util: 30.0.2 + jest-util: 30.0.2 + jest-worker: 30.0.2 + slash: 3.0.0 + string-length: 4.0.2 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + + '@jest/schemas@30.0.1': + dependencies: + '@sinclair/typebox': 0.34.35 + + '@jest/snapshot-utils@30.0.1': + dependencies: + '@jest/types': 30.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + '@jest/source-map@30.0.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@30.0.2': + dependencies: + '@jest/console': 30.0.2 + '@jest/types': 30.0.1 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.2 + + '@jest/test-sequencer@30.0.2': + dependencies: + '@jest/test-result': 30.0.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.0.2 + slash: 3.0.0 + + '@jest/transform@30.0.2': + dependencies: + '@babel/core': 7.27.4 + '@jest/types': 30.0.1 + '@jridgewell/trace-mapping': 0.3.25 + babel-plugin-istanbul: 7.0.0 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 30.0.2 + jest-regex-util: 30.0.1 + jest-util: 30.0.2 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@jest/types@30.0.1': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.1 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 24.0.3 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + '@mongodb-js/saslprep@1.3.0': dependencies: sparse-bitfield: 3.0.3 + '@napi-rs/wasm-runtime@0.2.11': + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 + optional: true + + '@noble/hashes@1.8.0': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1466,6 +3150,51 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.7': {} + + '@sinclair/typebox@0.34.35': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@13.0.5': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.7 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.27.6 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + + '@types/babel__traverse@7.20.7': + dependencies: + '@babel/types': 7.27.6 + '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -1479,6 +3208,8 @@ snapshots: dependencies: '@types/express': 5.0.3 + '@types/cookiejar@2.1.5': {} + '@types/cors@2.8.19': dependencies: '@types/node': 24.0.3 @@ -1500,6 +3231,21 @@ snapshots: '@types/http-errors@2.0.5': {} + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jest@30.0.0': + dependencies: + expect: 30.0.2 + pretty-format: 30.0.2 + '@types/json-schema@7.0.15': {} '@types/jsonwebtoken@9.0.10': @@ -1507,6 +3253,8 @@ snapshots: '@types/ms': 2.1.0 '@types/node': 24.0.3 + '@types/methods@1.1.4': {} + '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} @@ -1530,12 +3278,32 @@ snapshots: '@types/node': 24.0.3 '@types/send': 0.17.5 + '@types/stack-utils@2.0.3': {} + + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 24.0.3 + form-data: 4.0.3 + + '@types/supertest@6.0.3': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.9 + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': + dependencies: + '@types/yargs-parser': 21.0.3 + '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -1628,6 +3396,67 @@ snapshots: '@typescript-eslint/types': 8.34.1 eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.0': {} + + '@unrs/resolver-binding-android-arm-eabi@1.9.0': + optional: true + + '@unrs/resolver-binding-android-arm64@1.9.0': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.9.0': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.9.0': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.9.0': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.9.0': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.9.0': + dependencies: + '@napi-rs/wasm-runtime': 0.2.11 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.9.0': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.9.0': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.9.0': + optional: true + accepts@2.0.0: dependencies: mime-types: 3.0.1 @@ -1648,23 +3477,104 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-styles@4.3.0: + ansi-escapes@4.3.2: dependencies: - color-convert: 2.0.1 + type-fest: 0.21.3 - argparse@2.0.1: {} + ansi-regex@5.0.1: {} - balanced-match@1.0.2: {} + ansi-regex@6.1.0: {} - base64-js@1.5.1: {} + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 - bcryptjs@3.0.2: {} + ansi-styles@5.2.0: {} - bignumber.js@9.3.0: {} + ansi-styles@6.2.1: {} - body-parser@2.2.0: + anymatch@3.1.3: dependencies: - bytes: 3.1.2 + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + asap@2.0.6: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + babel-jest@30.0.2(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@jest/transform': 30.0.2 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 7.0.0 + babel-preset-jest: 30.0.1(@babel/core@7.27.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-istanbul@7.0.0: + dependencies: + '@babel/helper-plugin-utils': 7.27.1 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 6.0.3 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-jest-hoist@30.0.1: + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.27.6 + '@types/babel__core': 7.20.5 + + babel-preset-current-node-syntax@1.1.0(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.27.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.27.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.27.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.27.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.27.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.27.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.27.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.27.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.27.4) + + babel-preset-jest@30.0.1(@babel/core@7.27.4): + dependencies: + '@babel/core': 7.27.4 + babel-plugin-jest-hoist: 30.0.1 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.27.4) + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bcryptjs@3.0.2: {} + + bignumber.js@9.3.0: {} + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.1 http-errors: 2.0.0 @@ -1689,10 +3599,27 @@ snapshots: dependencies: fill-range: 7.1.1 + browserslist@4.25.0: + dependencies: + caniuse-lite: 1.0.30001723 + electron-to-chromium: 1.5.170 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.25.0) + + bs-logger@0.2.6: + dependencies: + fast-json-stable-stringify: 2.1.0 + + bser@2.1.1: + dependencies: + node-int64: 0.4.0 + bson@6.10.4: {} buffer-equal-constant-time@1.0.1: {} + buffer-from@1.1.2: {} + bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -1707,17 +3634,45 @@ snapshots: callsites@3.1.0: {} + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001723: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + char-regex@1.0.2: {} + + ci-info@4.2.0: {} + + cjs-module-lexer@2.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + component-emitter@1.3.1: {} + concat-map@0.0.1: {} content-disposition@1.0.0: @@ -1726,6 +3681,8 @@ snapshots: content-type@1.0.5: {} + convert-source-map@2.0.0: {} + cookie-parser@1.4.7: dependencies: cookie: 0.7.2 @@ -1737,6 +3694,8 @@ snapshots: cookie@0.7.2: {} + cookiejar@2.1.4: {} + cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -1752,10 +3711,23 @@ snapshots: dependencies: ms: 2.1.3 + dedent@1.6.0: {} + deep-is@0.1.4: {} + deepmerge@4.3.1: {} + + delayed-stream@1.0.0: {} + depd@2.0.0: {} + detect-newline@3.1.0: {} + + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + dotenv@16.5.0: {} dunder-proto@1.0.1: @@ -1764,14 +3736,32 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + eastasianwidth@0.2.0: {} + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 ee-first@1.1.1: {} + ejs@3.1.10: + dependencies: + jake: 10.9.2 + + electron-to-chromium@1.5.170: {} + + emittery@0.13.1: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + encodeurl@2.0.0: {} + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -1780,6 +3770,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -1808,8 +3805,12 @@ snapshots: '@esbuild/win32-ia32': 0.25.5 '@esbuild/win32-x64': 0.25.5 + escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@2.0.0: {} + escape-string-regexp@4.0.0: {} eslint-scope@8.4.0: @@ -1867,6 +3868,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -1881,6 +3884,29 @@ snapshots: etag@1.8.1: {} + execa@5.1.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + exit-x@0.2.2: {} + + expect@30.0.2: + dependencies: + '@jest/expect-utils': 30.0.2 + '@jest/get-type': 30.0.1 + jest-matcher-utils: 30.0.2 + jest-message-util: 30.0.2 + jest-mock: 30.0.2 + jest-util: 30.0.2 + express-validator@7.2.1: dependencies: lodash: 4.17.21 @@ -1934,14 +3960,24 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-safe-stringify@2.1.1: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 + fb-watchman@2.0.2: + dependencies: + bser: 2.1.1 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -1957,6 +3993,11 @@ snapshots: transitivePeerDependencies: - supports-color + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -1969,10 +4010,31 @@ snapshots: flatted@3.3.3: {} + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + forwarded@0.2.0: {} fresh@2.0.0: {} + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -1995,139 +4057,561 @@ snapshots: google-logging-utils: 0.0.2 json-bigint: 1.0.0 transitivePeerDependencies: - - encoding + - encoding + - supports-color + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-package-type@0.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@11.12.0: {} + + globals@14.0.0: {} + + globals@16.2.0: {} + + google-auth-library@9.15.1: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1 + gcp-metadata: 6.1.1 + gtoken: 7.1.0 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + google-logging-utils@0.0.2: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + gtoken@7.1.0: + dependencies: + gaxios: 6.7.1 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + html-escaper@2.0.2: {} + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + human-signals@2.1.0: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-local@3.2.0: + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-arrayish@0.2.1: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-fn@2.1.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-promise@4.0.0: {} + + is-stream@2.0.1: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.27.4 + '@babel/parser': 7.27.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + debug: 4.4.1 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jake@10.9.2: + dependencies: + async: 3.2.6 + chalk: 4.1.2 + filelist: 1.0.4 + minimatch: 3.1.2 + + jest-changed-files@30.0.2: + dependencies: + execa: 5.1.1 + jest-util: 30.0.2 + p-limit: 3.1.0 + + jest-circus@30.0.2: + dependencies: + '@jest/environment': 30.0.2 + '@jest/expect': 30.0.2 + '@jest/test-result': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.6.0 + is-generator-fn: 2.1.0 + jest-each: 30.0.2 + jest-matcher-utils: 30.0.2 + jest-message-util: 30.0.2 + jest-runtime: 30.0.2 + jest-snapshot: 30.0.2 + jest-util: 30.0.2 + p-limit: 3.1.0 + pretty-format: 30.0.2 + pure-rand: 7.0.1 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + + jest-cli@30.0.2(@types/node@24.0.3): + dependencies: + '@jest/core': 30.0.2 + '@jest/test-result': 30.0.2 + '@jest/types': 30.0.1 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.0.2(@types/node@24.0.3) + jest-util: 30.0.2 + jest-validate: 30.0.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + jest-config@30.0.2(@types/node@24.0.3): + dependencies: + '@babel/core': 7.27.4 + '@jest/get-type': 30.0.1 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.0.2 + '@jest/types': 30.0.1 + babel-jest: 30.0.2(@babel/core@7.27.4) + chalk: 4.1.2 + ci-info: 4.2.0 + deepmerge: 4.3.1 + glob: 10.4.5 + graceful-fs: 4.2.11 + jest-circus: 30.0.2 + jest-docblock: 30.0.1 + jest-environment-node: 30.0.2 + jest-regex-util: 30.0.1 + jest-resolve: 30.0.2 + jest-runner: 30.0.2 + jest-util: 30.0.2 + jest-validate: 30.0.2 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 30.0.2 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 24.0.3 + transitivePeerDependencies: + - babel-plugin-macros - supports-color - get-intrinsic@1.3.0: + jest-diff@30.0.2: 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 + '@jest/diff-sequences': 30.0.1 + '@jest/get-type': 30.0.1 + chalk: 4.1.2 + pretty-format: 30.0.2 - get-proto@1.0.1: + jest-docblock@30.0.1: dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + detect-newline: 3.1.0 - get-tsconfig@4.10.1: + jest-each@30.0.2: dependencies: - resolve-pkg-maps: 1.0.0 + '@jest/get-type': 30.0.1 + '@jest/types': 30.0.1 + chalk: 4.1.2 + jest-util: 30.0.2 + pretty-format: 30.0.2 - glob-parent@5.1.2: + jest-environment-node@30.0.2: dependencies: - is-glob: 4.0.3 + '@jest/environment': 30.0.2 + '@jest/fake-timers': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + jest-mock: 30.0.2 + jest-util: 30.0.2 + jest-validate: 30.0.2 - glob-parent@6.0.2: + jest-haste-map@30.0.2: dependencies: - is-glob: 4.0.3 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 30.0.1 + jest-util: 30.0.2 + jest-worker: 30.0.2 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 - globals@14.0.0: {} + jest-leak-detector@30.0.2: + dependencies: + '@jest/get-type': 30.0.1 + pretty-format: 30.0.2 - globals@16.2.0: {} + jest-matcher-utils@30.0.2: + dependencies: + '@jest/get-type': 30.0.1 + chalk: 4.1.2 + jest-diff: 30.0.2 + pretty-format: 30.0.2 - google-auth-library@9.15.1: + jest-message-util@30.0.2: dependencies: - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - gaxios: 6.7.1 - gcp-metadata: 6.1.1 - gtoken: 7.1.0 - jws: 4.0.0 - transitivePeerDependencies: - - encoding - - supports-color + '@babel/code-frame': 7.27.1 + '@jest/types': 30.0.1 + '@types/stack-utils': 2.0.3 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.8 + pretty-format: 30.0.2 + slash: 3.0.0 + stack-utils: 2.0.6 - google-logging-utils@0.0.2: {} + jest-mock@30.0.2: + dependencies: + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + jest-util: 30.0.2 - gopd@1.2.0: {} + jest-pnp-resolver@1.2.3(jest-resolve@30.0.2): + optionalDependencies: + jest-resolve: 30.0.2 - graphemer@1.4.0: {} + jest-regex-util@30.0.1: {} - gtoken@7.1.0: + jest-resolve-dependencies@30.0.2: dependencies: - gaxios: 6.7.1 - jws: 4.0.0 + jest-regex-util: 30.0.1 + jest-snapshot: 30.0.2 transitivePeerDependencies: - - encoding - supports-color - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - hasown@2.0.2: + jest-resolve@30.0.2: dependencies: - function-bind: 1.1.2 + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 30.0.2 + jest-pnp-resolver: 1.2.3(jest-resolve@30.0.2) + jest-util: 30.0.2 + jest-validate: 30.0.2 + slash: 3.0.0 + unrs-resolver: 1.9.0 + + jest-runner@30.0.2: + dependencies: + '@jest/console': 30.0.2 + '@jest/environment': 30.0.2 + '@jest/test-result': 30.0.2 + '@jest/transform': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + chalk: 4.1.2 + emittery: 0.13.1 + exit-x: 0.2.2 + graceful-fs: 4.2.11 + jest-docblock: 30.0.1 + jest-environment-node: 30.0.2 + jest-haste-map: 30.0.2 + jest-leak-detector: 30.0.2 + jest-message-util: 30.0.2 + jest-resolve: 30.0.2 + jest-runtime: 30.0.2 + jest-util: 30.0.2 + jest-watcher: 30.0.2 + jest-worker: 30.0.2 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color - http-errors@2.0.0: + jest-runtime@30.0.2: dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 + '@jest/environment': 30.0.2 + '@jest/fake-timers': 30.0.2 + '@jest/globals': 30.0.2 + '@jest/source-map': 30.0.1 + '@jest/test-result': 30.0.2 + '@jest/transform': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + chalk: 4.1.2 + cjs-module-lexer: 2.1.0 + collect-v8-coverage: 1.0.2 + glob: 10.4.5 + graceful-fs: 4.2.11 + jest-haste-map: 30.0.2 + jest-message-util: 30.0.2 + jest-mock: 30.0.2 + jest-regex-util: 30.0.1 + jest-resolve: 30.0.2 + jest-snapshot: 30.0.2 + jest-util: 30.0.2 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.3 - debug: 4.4.1 + jest-snapshot@30.0.2: + dependencies: + '@babel/core': 7.27.4 + '@babel/generator': 7.27.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4) + '@babel/types': 7.27.6 + '@jest/expect-utils': 30.0.2 + '@jest/get-type': 30.0.1 + '@jest/snapshot-utils': 30.0.1 + '@jest/transform': 30.0.2 + '@jest/types': 30.0.1 + babel-preset-current-node-syntax: 1.1.0(@babel/core@7.27.4) + chalk: 4.1.2 + expect: 30.0.2 + graceful-fs: 4.2.11 + jest-diff: 30.0.2 + jest-matcher-utils: 30.0.2 + jest-message-util: 30.0.2 + jest-util: 30.0.2 + pretty-format: 30.0.2 + semver: 7.7.2 + synckit: 0.11.8 transitivePeerDependencies: - supports-color - iconv-lite@0.6.3: + jest-util@30.0.2: dependencies: - safer-buffer: 2.1.2 - - ignore@5.3.2: {} - - ignore@7.0.5: {} + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + chalk: 4.1.2 + ci-info: 4.2.0 + graceful-fs: 4.2.11 + picomatch: 4.0.2 - import-fresh@3.3.1: + jest-validate@30.0.2: dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - inherits@2.0.4: {} - - ipaddr.js@1.9.1: {} - - is-extglob@2.1.1: {} + '@jest/get-type': 30.0.1 + '@jest/types': 30.0.1 + camelcase: 6.3.0 + chalk: 4.1.2 + leven: 3.1.0 + pretty-format: 30.0.2 - is-glob@4.0.3: + jest-watcher@30.0.2: dependencies: - is-extglob: 2.1.1 + '@jest/test-result': 30.0.2 + '@jest/types': 30.0.1 + '@types/node': 24.0.3 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 30.0.2 + string-length: 4.0.2 - is-number@7.0.0: {} + jest-worker@30.0.2: + dependencies: + '@types/node': 24.0.3 + '@ungap/structured-clone': 1.3.0 + jest-util: 30.0.2 + merge-stream: 2.0.0 + supports-color: 8.1.1 - is-promise@4.0.0: {} + jest@30.0.2(@types/node@24.0.3): + dependencies: + '@jest/core': 30.0.2 + '@jest/types': 30.0.1 + import-local: 3.2.0 + jest-cli: 30.0.2(@types/node@24.0.3) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node - is-stream@2.0.1: {} + js-tokens@4.0.0: {} - isexe@2.0.0: {} + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 js-yaml@4.1.0: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-bigint@1.0.0: dependencies: bignumber.js: 9.3.0 json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + jsonwebtoken@9.0.2: dependencies: jws: 3.2.2 @@ -2169,11 +4653,19 @@ snapshots: dependencies: json-buffer: 3.0.1 + leven@3.1.0: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 + lines-and-columns@1.2.4: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -2190,12 +4682,30 @@ snapshots: lodash.isstring@4.0.1: {} + lodash.memoize@4.1.2: {} + lodash.merge@4.6.2: {} lodash.once@4.1.1: {} lodash@4.17.21: {} + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + make-error@1.3.6: {} + + makeerror@1.0.12: + dependencies: + tmpl: 1.0.5 + math-intrinsics@1.1.0: {} media-typer@1.1.0: {} @@ -2204,27 +4714,47 @@ snapshots: merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} + merge2@1.4.1: {} + methods@1.1.2: {} + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + mime-types@3.0.1: dependencies: mime-db: 1.54.0 + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 + minipass@7.1.2: {} + mongodb-connection-string-url@3.0.2: dependencies: '@types/whatwg-url': 11.0.5 @@ -2265,6 +4795,8 @@ snapshots: ms@2.1.3: {} + napi-postinstall@0.2.4: {} + natural-compare@1.4.0: {} negotiator@1.0.0: {} @@ -2273,6 +4805,16 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-int64@0.4.0: {} + + node-releases@2.0.19: {} + + normalize-path@3.0.0: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + object-assign@4.1.1: {} object-inspect@1.13.4: {} @@ -2285,6 +4827,10 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -2294,30 +4840,72 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parseurl@1.3.3: {} path-exists@4.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + path-to-regexp@8.2.0: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} + picomatch@4.0.2: {} + + pirates@4.0.7: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + prelude-ls@1.2.1: {} + pretty-format@30.0.2: + dependencies: + '@jest/schemas': 30.0.1 + ansi-styles: 5.2.0 + react-is: 18.3.1 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -2325,6 +4913,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@7.0.1: {} + qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -2340,8 +4930,18 @@ snapshots: iconv-lite: 0.6.3 unpipe: 1.0.0 + react-is@18.3.1: {} + + require-directory@2.1.1: {} + + resolve-cwd@3.0.0: + dependencies: + resolve-from: 5.0.0 + resolve-from@4.0.0: {} + resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} reusify@1.1.0: {} @@ -2364,6 +4964,8 @@ snapshots: safer-buffer@2.1.2: {} + semver@6.3.1: {} + semver@7.7.2: {} send@1.2.0: @@ -2429,20 +5031,105 @@ snapshots: sift@17.1.3: {} + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + source-map-support@0.5.13: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 + sprintf-js@1.0.3: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + statuses@2.0.1: {} statuses@2.0.2: {} + string-length@4.0.2: + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + strip-json-comments@3.1.1: {} + superagent@10.2.1: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.1 + fast-safe-stringify: 2.1.1 + form-data: 4.0.3 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.0 + transitivePeerDependencies: + - supports-color + + supertest@7.1.1: + dependencies: + methods: 1.1.2 + superagent: 10.2.1 + transitivePeerDependencies: + - supports-color + supports-color@7.2.0: dependencies: has-flag: 4.0.0 + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + synckit@0.11.8: + dependencies: + '@pkgr/core': 0.2.7 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + tmpl@1.0.5: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -2459,6 +5146,29 @@ snapshots: dependencies: typescript: 5.8.3 + ts-jest@29.4.0(@babel/core@7.27.4)(@jest/transform@30.0.2)(@jest/types@30.0.1)(babel-jest@30.0.2(@babel/core@7.27.4))(jest-util@30.0.2)(jest@30.0.2(@types/node@24.0.3))(typescript@5.8.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 30.0.2(@types/node@24.0.3) + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.7.2 + type-fest: 4.41.0 + typescript: 5.8.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.27.4 + '@jest/transform': 30.0.2 + '@jest/types': 30.0.1 + babel-jest: 30.0.2(@babel/core@7.27.4) + jest-util: 30.0.2 + + tslib@2.8.1: + optional: true + tsx@4.20.3: dependencies: esbuild: 0.25.5 @@ -2470,6 +5180,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-detect@4.0.8: {} + + type-fest@0.21.3: {} + + type-fest@4.41.0: {} + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -2492,16 +5208,56 @@ snapshots: unpipe@1.0.0: {} + unrs-resolver@1.9.0: + dependencies: + napi-postinstall: 0.2.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.9.0 + '@unrs/resolver-binding-android-arm64': 1.9.0 + '@unrs/resolver-binding-darwin-arm64': 1.9.0 + '@unrs/resolver-binding-darwin-x64': 1.9.0 + '@unrs/resolver-binding-freebsd-x64': 1.9.0 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.9.0 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.9.0 + '@unrs/resolver-binding-linux-arm64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-arm64-musl': 1.9.0 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-riscv64-musl': 1.9.0 + '@unrs/resolver-binding-linux-s390x-gnu': 1.9.0 + '@unrs/resolver-binding-linux-x64-gnu': 1.9.0 + '@unrs/resolver-binding-linux-x64-musl': 1.9.0 + '@unrs/resolver-binding-wasm32-wasi': 1.9.0 + '@unrs/resolver-binding-win32-arm64-msvc': 1.9.0 + '@unrs/resolver-binding-win32-ia32-msvc': 1.9.0 + '@unrs/resolver-binding-win32-x64-msvc': 1.9.0 + + update-browserslist-db@1.1.3(browserslist@4.25.0): + dependencies: + browserslist: 4.25.0 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 uuid@9.0.1: {} + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + validator@13.12.0: {} vary@1.1.2: {} + walker@1.0.8: + dependencies: + makeerror: 1.0.12 + webidl-conversions@3.0.1: {} webidl-conversions@7.0.0: {} @@ -2522,10 +5278,43 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + wrappy@1.0.2: {} + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + ws@8.18.2: {} + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + yocto-queue@0.1.0: {} zod-to-json-schema@3.24.5(zod@3.25.67): From ac4b60af457ded196d1888a130d3bdb496cfdc0e Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:48:20 +0800 Subject: [PATCH 26/84] chore: exclude test files from TypeScript build output --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 5bc00d4..c059485 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,5 +8,5 @@ "forceConsistentCasingInFileNames": true, "strict": true }, - "exclude": ["node_modules/"] + "exclude": ["node_modules", "**/*.test.ts"] } From f15c6107df644608f83df83f322d9954fcb5afaa Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:49:35 +0800 Subject: [PATCH 27/84] refactor(auth): move password hashing to user model pre-save hook - Added pre-save hook in user model to handle hashing - Removed manual hashing from registerUser controller - Simplified controller logic and improved maintainability --- src/controller/auth.controller.ts | 9 +++------ src/model/user.model.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/controller/auth.controller.ts b/src/controller/auth.controller.ts index 37a40e0..b6f0107 100644 --- a/src/controller/auth.controller.ts +++ b/src/controller/auth.controller.ts @@ -12,15 +12,12 @@ export const registerUser = async (req: Request, res: Response) => { // Check if user exits const userExists = await User.findOne({ email }); if (userExists) { - HTTPResponse.conflict(res, "An account with this email already exists."); + HTTPResponse.conflict(res, "User already exists"); return; } - // Hash password - const hashed = await bcrypt.hash(password, 10); - // create and save user - const user = new User({ firstName, lastName, email, password: hashed }); + const user = new User({ firstName, lastName, email, password }); await user.save(); // create jwt @@ -60,7 +57,7 @@ export const loginUser = async (req: Request, res: Response) => { const user = await User.findOne({ email }); if (!user) { - HTTPResponse.notFound(res, "User with this email does not exist."); + HTTPResponse.notFound(res, "User not found"); return; } diff --git a/src/model/user.model.ts b/src/model/user.model.ts index 412b50a..168d5af 100644 --- a/src/model/user.model.ts +++ b/src/model/user.model.ts @@ -1,4 +1,5 @@ import mongoose from "mongoose"; +import bcrypt from "bcryptjs"; export interface IUser { _id: mongoose.Schema.Types.ObjectId; @@ -44,6 +45,13 @@ const userSchema = new mongoose.Schema( } ); +userSchema.pre("save", async function (next) { + if (!this.isModified("password")) return next(); + + this.password = await bcrypt.hash(this.password, 10); + next(); +}); + const User = mongoose.model("User", userSchema); export default User; From b356b9d32b33e590b58d6f3c4d4b4f90d7d7f1c1 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:50:01 +0800 Subject: [PATCH 28/84] test: add health check endpoint test --- src/test/integration/app.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/test/integration/app.test.ts diff --git a/src/test/integration/app.test.ts b/src/test/integration/app.test.ts new file mode 100644 index 0000000..ea19c16 --- /dev/null +++ b/src/test/integration/app.test.ts @@ -0,0 +1,9 @@ +import request from "supertest"; +import app from "../../app"; + +describe("GET /health", () => { + test("should return 200 ok", async () => { + const res = await request(app).get("/health"); + expect(res.status).toEqual(200); + }); +}); From be8e133150062feccf0ef9e2b6679aef27a4ca55 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 20 Jun 2025 17:50:28 +0800 Subject: [PATCH 29/84] chore: improve MongoDB connection logging and env variable naming --- src/util/database.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/util/database.ts b/src/util/database.ts index 467e608..eb773a1 100644 --- a/src/util/database.ts +++ b/src/util/database.ts @@ -1,22 +1,22 @@ import mongoose from "mongoose"; export const connectDB = async () => { - const isDockerized = process.env.IS_DOCKERIZED === "true"; + const IS_DOCKERIZED = process.env.IS_DOCKERIZED === "true"; - const mongoUri = isDockerized - ? process.env.MONGO_DOCKER_URI - : process.env.MONGO_ATLAS_URI; + const MONGO_URI = IS_DOCKERIZED + ? process.env.MONGO_URI_DOCKER + : process.env.MONGO_URI_ATLAS; try { console.debug( `${ - isDockerized - ? "ℹ️ Initializing local mongodb" - : "ℹ️ Initializing mongodb atlas" + IS_DOCKERIZED + ? "ℹ️ Connecting to containerized mongodb" + : "ℹ️ Connecting to mongodb atlas" }` ); - await mongoose.connect(mongoUri!); - console.log("✅ Database connection established"); + await mongoose.connect(MONGO_URI!); + console.log(`✅ Successfully connected to ${MONGO_URI}`); } catch (err) { console.error("👎 MongoDB connection error:", err); process.exit(1); From db45a053e2eb3d35ca945877d163afc51cdb627a Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:43:08 +0800 Subject: [PATCH 30/84] chore: add mongodb-memory-server to dev dependencies. --- package.json | 1 + pnpm-lock.yaml | 166 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 165 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3ac6bbc..5c7578f 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "eslint": "^9.29.0", "globals": "^16.2.0", "jest": "^30.0.2", + "mongodb-memory-server": "^10.1.4", "supertest": "^7.1.1", "ts-jest": "^29.4.0", "tsx": "^4.20.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 641d04f..7cdb573 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -72,6 +72,9 @@ importers: jest: specifier: ^30.0.2 version: 30.0.2(@types/node@24.0.3) + mongodb-memory-server: + specifier: ^10.1.4 + version: 10.1.4 supertest: specifier: ^7.1.1 version: 7.1.1 @@ -961,12 +964,18 @@ packages: asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + b4a@1.6.7: + resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} + babel-jest@30.0.2: resolution: {integrity: sha512-A5kqR1/EUTidM2YC2YMEUDP2+19ppgOwK0IAd9Swc3q2KqFb5f9PtRUXVeZcngu0z5mDMyZ9zH2huJZSOMLiTQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -995,6 +1004,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + bare-events@2.5.4: + resolution: {integrity: sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -1035,6 +1047,9 @@ packages: resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==} engines: {node: '>=16.20.1'} + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} @@ -1105,6 +1120,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} @@ -1342,6 +1360,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1376,6 +1397,10 @@ packages: resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} engines: {node: '>= 0.8'} + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1391,6 +1416,15 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -1877,6 +1911,10 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -1960,6 +1998,14 @@ packages: mongodb-connection-string-url@3.0.2: resolution: {integrity: sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==} + mongodb-memory-server-core@10.1.4: + resolution: {integrity: sha512-o8fgY7ZalEd8pGps43fFPr/hkQu1L8i6HFEGbsTfA2zDOW0TopgpswaBCqDr0qD7ptibyPfB5DmC+UlIxbThzA==} + engines: {node: '>=16.20.1'} + + mongodb-memory-server@10.1.4: + resolution: {integrity: sha512-+oKQ/kc3CX+816oPFRtaF0CN4vNcGKNjpOQe4bHo/21A3pMD+lC7Xz1EX5HP7siCX4iCpVchDMmCOFXVQSGkUg==} + engines: {node: '>=16.20.1'} + mongodb@6.17.0: resolution: {integrity: sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==} engines: {node: '>=16.20.1'} @@ -2014,6 +2060,10 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + new-find-package-json@2.0.0: + resolution: {integrity: sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==} + engines: {node: '>=12.22.0'} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -2115,6 +2165,9 @@ packages: resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} engines: {node: '>=16'} + pend@1.2.0: + resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2290,6 +2343,9 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} + streamx@2.22.1: + resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} + string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} @@ -2342,10 +2398,16 @@ packages: resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} engines: {node: ^14.18.0 || >=16.0.0} + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -2540,6 +2602,10 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yauzl@3.2.0: + resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==} + engines: {node: '>=12'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3506,10 +3572,16 @@ snapshots: asap@2.0.6: {} + async-mutex@0.5.0: + dependencies: + tslib: 2.8.1 + async@3.2.6: {} asynckit@0.4.0: {} + b4a@1.6.7: {} + babel-jest@30.0.2(@babel/core@7.27.4): dependencies: '@babel/core': 7.27.4 @@ -3566,6 +3638,9 @@ snapshots: balanced-match@1.0.2: {} + bare-events@2.5.4: + optional: true + base64-js@1.5.1: {} bcryptjs@3.0.2: {} @@ -3616,6 +3691,8 @@ snapshots: bson@6.10.4: {} + buffer-crc32@0.2.13: {} + buffer-equal-constant-time@1.0.1: {} buffer-from@1.1.2: {} @@ -3671,6 +3748,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commondir@1.0.1: {} + component-emitter@1.3.1: {} concat-map@0.0.1: {} @@ -3948,6 +4027,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-fifo@1.3.2: {} + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3993,6 +4074,12 @@ snapshots: transitivePeerDependencies: - supports-color + find-cache-dir@3.3.2: + dependencies: + commondir: 1.0.1 + make-dir: 3.1.0 + pkg-dir: 4.2.0 + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -4010,6 +4097,10 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.9(debug@4.4.1): + optionalDependencies: + debug: 4.4.1 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -4696,6 +4787,10 @@ snapshots: dependencies: yallist: 3.1.1 + make-dir@3.1.0: + dependencies: + semver: 6.3.1 + make-dir@4.0.0: dependencies: semver: 7.7.2 @@ -4760,6 +4855,44 @@ snapshots: '@types/whatwg-url': 11.0.5 whatwg-url: 14.2.0 + mongodb-memory-server-core@10.1.4: + dependencies: + async-mutex: 0.5.0 + camelcase: 6.3.0 + debug: 4.4.1 + find-cache-dir: 3.3.2 + follow-redirects: 1.15.9(debug@4.4.1) + https-proxy-agent: 7.0.6 + mongodb: 6.17.0 + new-find-package-json: 2.0.0 + semver: 7.7.2 + tar-stream: 3.1.7 + tslib: 2.8.1 + yauzl: 3.2.0 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + + mongodb-memory-server@10.1.4: + dependencies: + mongodb-memory-server-core: 10.1.4 + tslib: 2.8.1 + transitivePeerDependencies: + - '@aws-sdk/credential-providers' + - '@mongodb-js/zstd' + - gcp-metadata + - kerberos + - mongodb-client-encryption + - snappy + - socks + - supports-color + mongodb@6.17.0: dependencies: '@mongodb-js/saslprep': 1.3.0 @@ -4801,6 +4934,12 @@ snapshots: negotiator@1.0.0: {} + new-find-package-json@2.0.0: + dependencies: + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -4886,6 +5025,8 @@ snapshots: path-to-regexp@8.2.0: {} + pend@1.2.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -5058,6 +5199,13 @@ snapshots: statuses@2.0.2: {} + streamx@2.22.1: + dependencies: + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + optionalDependencies: + bare-events: 2.5.4 + string-length@4.0.2: dependencies: char-regex: 1.0.2 @@ -5122,12 +5270,22 @@ snapshots: dependencies: '@pkgr/core': 0.2.7 + tar-stream@3.1.7: + dependencies: + b4a: 1.6.7 + fast-fifo: 1.3.2 + streamx: 2.22.1 + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 + text-decoder@1.2.3: + dependencies: + b4a: 1.6.7 + tmpl@1.0.5: {} to-regex-range@5.0.1: @@ -5166,8 +5324,7 @@ snapshots: babel-jest: 30.0.2(@babel/core@7.27.4) jest-util: 30.0.2 - tslib@2.8.1: - optional: true + tslib@2.8.1: {} tsx@4.20.3: dependencies: @@ -5315,6 +5472,11 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yauzl@3.2.0: + dependencies: + buffer-crc32: 0.2.13 + pend: 1.2.0 + yocto-queue@0.1.0: {} zod-to-json-schema@3.24.5(zod@3.25.67): From 9b36d6f864da748f36dca07b3d041c1c32e12820 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:43:30 +0800 Subject: [PATCH 31/84] chore: remove MONGO_URI_TEST env variable --- .env.template | 1 - 1 file changed, 1 deletion(-) diff --git a/.env.template b/.env.template index 8f777bb..0b4eb15 100644 --- a/.env.template +++ b/.env.template @@ -5,6 +5,5 @@ CLIENT_IP= JWT_SECRET= IS_DOCKERIZED= MONGO_URI_ATLAS= -MONGO_URI_TEST= MONGO_URI_DOCKER= GEMINI_KEY= \ No newline at end of file From fb6107d56c699a40b001df17046c105e8dc82d61 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:44:30 +0800 Subject: [PATCH 32/84] chore: use ESM-style default export in config --- jest.config.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/jest.config.js b/jest.config.js index b28cdff..0f701df 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,2 +1,6 @@ -export const preset = "ts-jest"; -export const testEnvironment = "node"; +export default { + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["**/test/**/*test.ts"], + testPathIgnorePatterns: ["/node_modules"], +}; From 70f19019b5931c2d54c2100d63b408067ab9076a Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:45:47 +0800 Subject: [PATCH 33/84] feat: add in-memory MongoDB test-db utility --- src/test/util/test-db.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/test/util/test-db.ts diff --git a/src/test/util/test-db.ts b/src/test/util/test-db.ts new file mode 100644 index 0000000..9bb6348 --- /dev/null +++ b/src/test/util/test-db.ts @@ -0,0 +1,22 @@ +import mongoose from "mongoose"; +import { MongoMemoryServer } from "mongodb-memory-server"; + +let mongoServer: MongoMemoryServer; + +export const connectDatabase = async () => { + mongoServer = await MongoMemoryServer.create(); + const uri = mongoServer.getUri(); + await mongoose.connect(uri, { dbName: "test-db" }); +}; + +export const closeDatabase = async () => { + if (mongoServer) await mongoServer.stop(); + await mongoose.connection.close(); +}; + +export const clearDatabase = async () => { + const collections = await mongoose.connection.db?.collections(); + for (const collection of collections!) { + await collection.deleteMany({}); + } +}; From 9ae48f97e68527b86abd18c596bd1913ebb74b1f Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 22 Jun 2025 23:47:08 +0800 Subject: [PATCH 34/84] refactor: migrate to in-memory MongoDB for test setup --- src/test/integration/auth.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/integration/auth.test.ts b/src/test/integration/auth.test.ts index f59d374..1fbdf91 100644 --- a/src/test/integration/auth.test.ts +++ b/src/test/integration/auth.test.ts @@ -1,4 +1,4 @@ -import mongoose from "mongoose"; +import { connectDatabase, closeDatabase, clearDatabase } from "../util/test-db"; import User from "../../model/user.model"; import request from "supertest"; import app from "../../app"; @@ -28,15 +28,15 @@ const postLogin = async (overrides = {}) => { describe("/auth", () => { beforeAll(async () => { - await mongoose.connect(process.env.MONGO_URI_TEST!); + await connectDatabase(); }); beforeEach(async () => { - await User.deleteMany({}); + await clearDatabase(); }); afterAll(async () => { - await mongoose.connection.close(); + await closeDatabase(); }); // Missing fields From 182620ecec382b6c574bff5e774aff3bec6dc28f Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:17:33 +0800 Subject: [PATCH 35/84] refactor: make attachmentUrls optional in ConvoGenParam --- src/service/llm-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/llm-service.ts b/src/service/llm-service.ts index 901ccea..6ce7061 100644 --- a/src/service/llm-service.ts +++ b/src/service/llm-service.ts @@ -4,8 +4,8 @@ import { TemplateValue } from "./template-config"; export interface ConvoGenParam { prompt: string; - attachmentUrls: string[]; template: TemplateValue; + attachmentUrls?: string[]; history?: ConversationHistory[]; } From d564272cc0597394cbfca7917fd8c0fb1086735a Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:18:52 +0800 Subject: [PATCH 36/84] refactor: set default empty array for optional attachmentUrls in invoke function --- src/service/llms/gemini-service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/service/llms/gemini-service.ts b/src/service/llms/gemini-service.ts index 00498db..02dfdab 100644 --- a/src/service/llms/gemini-service.ts +++ b/src/service/llms/gemini-service.ts @@ -5,7 +5,12 @@ import { GeminiConfig } from "../templates/gemini-configs"; const BASE_MODEL = "gemini-2.0-flash"; export class GeminiLLM implements GenAI { - async invoke({ prompt, attachmentUrls, template, history }: ConvoGenParam) { + async invoke({ + prompt, + attachmentUrls = [], + template, + history, + }: ConvoGenParam) { const client = new GoogleGenAI({ apiKey: process.env.GEMINI_KEY }); const attachments = await Promise.all( From 0df4031e608c098793c0229f17b5d3c0e3919d3f Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:20:38 +0800 Subject: [PATCH 37/84] refactor: convert from class-based to functional module - Replaced PromptService and PromptResponseGenerator classes with a single generateResponse function. - This simplifies the API, reduces boilerplate, and aligns with functional programming practices. --- src/service/prompt-service.ts | 105 ++++++++++++---------------------- 1 file changed, 37 insertions(+), 68 deletions(-) diff --git a/src/service/prompt-service.ts b/src/service/prompt-service.ts index 5988a18..e3cdcd0 100644 --- a/src/service/prompt-service.ts +++ b/src/service/prompt-service.ts @@ -1,4 +1,4 @@ -import { GenAI, LLM } from "./llm-service"; +import { ConvoGenParam, GenAI, LLM } from "./llm-service"; import { Template, TemplateResponseMap, @@ -10,75 +10,44 @@ export interface ConversationHistory { response: string; } -class PromptService { - constructor() { - console.info("ℹ️ PromptService initialized"); - } - - build(llm?: GenAI) { - console.debug( - `ℹ️ Building PromptResponseGenerator with LLM: ${ - llm?.constructor?.name || "Default (Gemini)" - }` +export async function generateResponse( + { prompt, attachmentUrls, template, history }: ConvoGenParam, + llm: GenAI = LLM.GEMINI +) { + if (!prompt) { + console.warn( + "PromptResponseGenerator: generateResponse called without user input" ); - return new PromptResponseGenerator(llm || LLM.GEMINI); + throw new Error("User input is required"); } -} - -interface GenerateConversationParams { - userInput: string; - attachmentsUrls?: string[]; - history?: ConversationHistory[]; - template?: TemplateValue; -} - -class PromptResponseGenerator { - constructor(private llm: GenAI) {} - async generateResponse({ - userInput, - attachmentsUrls, - template, - history, - }: GenerateConversationParams) { - if (!userInput) { - console.warn( - "PromptResponseGenerator: generateResponse called without user input" - ); - throw new Error("User input is required"); - } - - console.info( - `[prompt-service] Generating response for user input: ${userInput}` - ); - console.debug( - `Attachments: ${JSON.stringify( - attachmentsUrls - )}, Template: ${template}, History length: ${history?.length || 0}` - ); - - try { - const response = await this.llm.invoke({ - prompt: userInput, - attachmentUrls: attachmentsUrls || [], - template: template || Template.DEFAULT, - history: history || [], - }); - - console.info("[prompt-service] Returning text response: ", response.text); - return { - text: response?.text, - image: "", - }; - } catch (error) { - return { - response: JSON.parse( - JSON.stringify({ error: "Failed to generate response" }) - ) as TemplateResponseMap[T], - image: "", - }; - } + console.info(`[prompt-service] Generating response for prompt: ${prompt}`); + console.debug( + `Attachments: ${JSON.stringify( + attachmentUrls + )}, Template: ${template}, History length: ${history?.length || 0}` + ); + + try { + const response = await llm.invoke({ + prompt: prompt, + attachmentUrls: attachmentUrls ?? [], + template: template || Template.DEFAULT, + history: history || [], + }); + + console.info("[prompt-service] Returning text response: ", response.text); + return { + text: response?.text, + image: "", + }; + } catch (error) { + console.error(error); + return { + response: JSON.parse( + JSON.stringify({ error: "Failed to generate response" }) + ) as TemplateResponseMap[T], + image: "", + }; } } - -export const promptService = new PromptService(); From 4b46a548d2cdaf785fe62d57348c4df8574e013d Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:21:42 +0800 Subject: [PATCH 38/84] refactor: Update import and use updated generateResponse funciton - Replaced usage of class-based promptService with the new functional generateResponse method in the chat controller. --- src/controller/chat.controller.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/controller/chat.controller.ts b/src/controller/chat.controller.ts index 060ce87..94a4948 100644 --- a/src/controller/chat.controller.ts +++ b/src/controller/chat.controller.ts @@ -1,6 +1,6 @@ import { Response } from "express"; import { AuthRequest } from "../types/auth-request"; -import { promptService } from "../service/prompt-service"; +import { generateResponse } from "../service/prompt-service"; import { Template, TemplateValue } from "../service/template-config"; import Message from "../model/message.model"; import Chat from "../model/chat.model"; @@ -16,8 +16,8 @@ export const createChat = async (req: AuthRequest, res: Response) => { return; } - const chatMetaDataGenerator = await promptService.build().generateResponse({ - userInput: String(prompt), + const chatMetaDataGenerator = await generateResponse({ + prompt: String(prompt), template: Template.GENERATE_TITLE, }); @@ -45,9 +45,9 @@ export const createChat = async (req: AuthRequest, res: Response) => { return; } - const ai = await promptService.build().generateResponse({ - userInput: prompt, - attachmentsUrls: attachmentUrls, + const ai = await generateResponse({ + prompt: prompt, + attachmentUrls: attachmentUrls, template: templateType, }); From 4582cd2f24c749650800fe02e245cabd02ad6ceb Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:21:58 +0800 Subject: [PATCH 39/84] refactor: Update import and use updated generateResponse funciton - Replaced usage of class-based promptService with the new functional generateResponse method in the message controller. --- src/controller/message.controller.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/controller/message.controller.ts b/src/controller/message.controller.ts index 69404e6..8f3decb 100644 --- a/src/controller/message.controller.ts +++ b/src/controller/message.controller.ts @@ -4,12 +4,15 @@ import { AuthRequest } from "../types/auth-request"; import { HTTPResponse } from "../util/http-response"; import Chat from "../model/chat.model"; import { Template, TemplateValue } from "../service/template-config"; -import { ConversationHistory, promptService } from "../service/prompt-service"; +import { + ConversationHistory, + generateResponse, +} from "../service/prompt-service"; import { Types } from "mongoose"; export const createMessage = async (req: AuthRequest, res: Response) => { try { - const { prompt, attachmentsUrls, template } = req.body; + const { prompt, attachmentUrls, template } = req.body; const chatId = req.params.chatId as string; if (!Types.ObjectId.isValid(chatId)) { @@ -40,9 +43,9 @@ export const createMessage = async (req: AuthRequest, res: Response) => { }; }); - const ai = await promptService.build().generateResponse({ - userInput: prompt, - attachmentsUrls: attachmentsUrls, + const ai = await generateResponse({ + prompt: prompt, + attachmentUrls: attachmentUrls, template: templateType, history: history, }); From 5bce6c05504203ea60d5320b44daff65749f8c22 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 24 Jun 2025 23:23:03 +0800 Subject: [PATCH 40/84] feat: Implement integration test for chat endpoints. - Utilized jest and supertest to simulate HTTP request - Mocked generateResponse function to isolate service logic --- src/test/integration/chat.test.ts | 115 ++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 src/test/integration/chat.test.ts diff --git a/src/test/integration/chat.test.ts b/src/test/integration/chat.test.ts new file mode 100644 index 0000000..fad6372 --- /dev/null +++ b/src/test/integration/chat.test.ts @@ -0,0 +1,115 @@ +import { clearDatabase, closeDatabase, connectDatabase } from "../util/test-db"; +import app from "../../app"; +import request from "supertest"; +import User, { IUser } from "../../model/user.model"; +import * as promptService from "../../service/prompt-service"; +import Chat, { Subject } from "../../model/chat.model"; + +const testUser = { + firstName: "Test", + lastName: "User", + email: "testuser@email.com", + password: "testpassword", +}; + +const mockChatMetaData = { + text: JSON.stringify({ + title: "Mock Title", + subject: Subject.GENERAL_KNOWLEDGE, + }), + image: "", +}; + +const mockResponse = { + text: JSON.stringify({ + response: + "Photosynthesis is the process by which plants use sunlight, water, and carbon dioxide to create oxygen and energy in the form of sugar.", + }), + image: "", +}; + +describe("/chat", () => { + const testAgent = request.agent(app); + let loggedInUser: IUser; + + const createChat = async () => { + return await Chat.create({ + user: loggedInUser._id, + title: "Test", + subject: Subject.GENERAL_KNOWLEDGE, + }); + }; + + beforeAll(async () => { + await connectDatabase(); + + // Save test user + loggedInUser = await User.create(testUser); + + // Login to obtain token + const res = await testAgent.post("/auth/login").send({ + email: testUser.email, + password: testUser.password, + }); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + }); + + afterAll(async () => { + // Clear + await clearDatabase(); + // Close + await closeDatabase(); + }); + + test("should return 201 and metdata if created", async () => { + jest + .spyOn(promptService, "generateResponse") + .mockResolvedValueOnce(mockChatMetaData) + .mockResolvedValueOnce(mockResponse); + + const res = await testAgent + .post("/chat/") + .send({ prompt: "What is photosynthesis?" }); + + expect(res.status).toEqual(201); + expect(res.body.success).toEqual(true); + expect(res.body.data).toBeDefined(); + expect(res.body.data.chat.title).toEqual("Mock Title"); + expect(res.body.data.chat.subject).toEqual("general_knowledge"); + expect(res.body.data.message.response).toEqual( + JSON.parse(mockResponse.text).response + ); + }); + + test("should return 200 and data", async () => { + const res = await testAgent.get("/chat/"); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + expect(res.body.data).toBeDefined(); + expect(res.body.data.length).toEqual(1); + }); + + test("should return 200 if chat deleted", async () => { + const chat = await createChat(); + + const res = await testAgent.delete(`/chat/${chat.id}`); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + }); + + test("should return 200 if chat updated", async () => { + const chat = await createChat(); + + const res = await testAgent + .patch(`/chat/${chat.id}`) + .send({ title: "Updated" }); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + expect(res.body.data.title).toEqual("Updated"); + }); +}); From 76074103746cb8f88188982c7eda5b1c70329660 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 28 Jun 2025 01:24:16 +0800 Subject: [PATCH 41/84] test: add integration tests for /messsage endpoints using jest and supertest. --- src/test/integration/message.test.ts | 76 ++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/test/integration/message.test.ts diff --git a/src/test/integration/message.test.ts b/src/test/integration/message.test.ts new file mode 100644 index 0000000..49d2dcb --- /dev/null +++ b/src/test/integration/message.test.ts @@ -0,0 +1,76 @@ +import request from "supertest"; +import app from "../../app"; +import { clearDatabase, closeDatabase, connectDatabase } from "../util/test-db"; +import User, { IUser } from "../../model/user.model"; +import Chat, { IChat, Subject } from "../../model/chat.model"; +import * as promptService from "../../service/prompt-service"; + +const testUser = { + firstName: "Test", + lastName: "User", + email: "testuser@email.com", + password: "testpassword", +}; + +const mockResponse = { + text: JSON.stringify({ response: "2" }), + image: "", +}; + +describe("/message", () => { + const testAgent = request.agent(app); + let loggedInUser: IUser; + let testChat: IChat; + + beforeAll(async () => { + await connectDatabase(); + + loggedInUser = await User.create(testUser); + + const res = await testAgent + .post("/auth/login") + .send({ email: testUser.email, password: testUser.password }); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + + testChat = await Chat.create({ + user: loggedInUser._id, + title: "Test title", + subject: Subject.MATH, + }); + }); + + afterAll(async () => { + await clearDatabase(); + + await closeDatabase(); + }); + + test("should return 201 and metadata", async () => { + jest + .spyOn(promptService, "generateResponse") + .mockResolvedValue(mockResponse); + + const res = await testAgent.post(`/message/${testChat.id}`).send({ + prompt: "What is 1 + 1", + }); + + expect(res.status).toEqual(201); + expect(res.body.success).toEqual(true); + expect(res.body.data).toBeDefined(); + expect(res.body.data.chat).toEqual(testChat.id); + expect(res.body.data.response).toEqual( + JSON.parse(mockResponse.text).response + ); + }); + + test("should return 200 and messages", async () => { + const res = await testAgent.get(`/message/${testChat.id}`); + + expect(res.status).toEqual(200); + expect(res.body.success).toEqual(true); + expect(res.body.data).toBeDefined(); + expect(res.body.data.length).toEqual(1); + }); +}); From a1f5f9e5c81f3c69d72dad92f39aa03d1c3c79c9 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 28 Jun 2025 01:25:01 +0800 Subject: [PATCH 42/84] chore: rename Dockerfile to Dockerfile.dev --- Dockerfile => Dockerfile.dev | 0 docker-compose.yaml | 10 ++++------ 2 files changed, 4 insertions(+), 6 deletions(-) rename Dockerfile => Dockerfile.dev (100%) diff --git a/Dockerfile b/Dockerfile.dev similarity index 100% rename from Dockerfile rename to Dockerfile.dev diff --git a/docker-compose.yaml b/docker-compose.yaml index 13f8ab1..96e8e0d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,11 +1,10 @@ -# Use docker compose file format version 3.8 -version: "3.8" - # Define all services (containers) services: + # MongoDB service mongo: # Use official mongoDB image image: mongo:latest + # Always restart unless forcibly stop the process. restart: unless-stopped ports: - "27017:27017" @@ -14,7 +13,8 @@ services: # Backend service expressjs: # Build the docker image from the dockerfile in the current dir - build: . + build: + dockerfile: Dockerfile.dev # Map port 8000 inside the container to port 8000 on the host machine ports: @@ -34,7 +34,5 @@ services: # Override the default command and run npm start command: pnpm start - # MongoDB service - volumes: mongo-data: From 4b990eebeda884ba7476537ce2790dc48714bc2d Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:43:58 +0800 Subject: [PATCH 43/84] feact: Implement logger util using winston - Supports all level of logging e.g., info, warn, error, http, etc. --- src/util/logger.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/util/logger.ts diff --git a/src/util/logger.ts b/src/util/logger.ts new file mode 100644 index 0000000..8198edd --- /dev/null +++ b/src/util/logger.ts @@ -0,0 +1,15 @@ +import winston from "winston"; + +const logger = winston.createLogger({ + level: "http", + format: winston.format.combine( + winston.format.colorize(), + winston.format.timestamp(), + winston.format.printf(({ level, message, timestamp }) => { + return `[${timestamp}] ${level}: ${message}`; + }) + ), + transports: [new winston.transports.Console()], +}); + +export default logger; From 5b7cfa841cd8576fc39c40ead2aa7109ea47b76d Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:45:09 +0800 Subject: [PATCH 44/84] refactor: Replace standard logging with custom logger. --- src/controller/auth.controller.ts | 5 +++-- src/controller/chat.controller.ts | 18 +++++++----------- src/controller/message.controller.ts | 19 ++++++------------- src/index.ts | 3 ++- src/service/llms/gemini-service.ts | 8 ++++---- src/service/prompt-service.ts | 16 +++++++--------- src/util/database.ts | 11 ++++++----- 7 files changed, 35 insertions(+), 45 deletions(-) diff --git a/src/controller/auth.controller.ts b/src/controller/auth.controller.ts index b6f0107..c1f8936 100644 --- a/src/controller/auth.controller.ts +++ b/src/controller/auth.controller.ts @@ -3,6 +3,7 @@ import User from "../model/user.model"; import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import { HTTPResponse } from "../util/http-response"; +import logger from "../util/logger"; export const registerUser = async (req: Request, res: Response) => { try { @@ -36,7 +37,7 @@ export const registerUser = async (req: Request, res: Response) => { // return success response HTTPResponse.created(res, "User resgistration success", user); } catch (error) { - console.error(error); + logger.error(error); HTTPResponse.internalServerError( res, "An unexpected error has occurred", @@ -83,7 +84,7 @@ export const loginUser = async (req: Request, res: Response) => { // return success response HTTPResponse.ok(res, "Login success"); } catch (error: unknown) { - console.error(error); + logger.error(error); HTTPResponse.internalServerError( res, "An unexpected error has occurred", diff --git a/src/controller/chat.controller.ts b/src/controller/chat.controller.ts index 94a4948..3fdc724 100644 --- a/src/controller/chat.controller.ts +++ b/src/controller/chat.controller.ts @@ -5,6 +5,7 @@ import { Template, TemplateValue } from "../service/template-config"; import Message from "../model/message.model"; import Chat from "../model/chat.model"; import { HTTPResponse } from "../util/http-response"; +import logger from "../util/logger"; export const createChat = async (req: AuthRequest, res: Response) => { try { @@ -21,15 +22,10 @@ export const createChat = async (req: AuthRequest, res: Response) => { template: Template.GENERATE_TITLE, }); - console.info( - `AI response: ${JSON.stringify(chatMetaDataGenerator.response)}` - ); - const chatMetaData = JSON.parse(chatMetaDataGenerator.text!); - console.log("Chat MetaData: ", chatMetaData); if (!chatMetaData) { - console.error("Failed to generate chat metadata"); + logger.error("Failed to generate chat metadata"); HTTPResponse.internalServerError(res, "Failed to generate chat metadata"); return; } @@ -40,7 +36,7 @@ export const createChat = async (req: AuthRequest, res: Response) => { }); if (!Chat) { - console.error("Failed to create Chat"); + logger.error("Failed to create Chat"); HTTPResponse.internalServerError(res, "Failed to create chat"); return; } @@ -63,7 +59,7 @@ export const createChat = async (req: AuthRequest, res: Response) => { message: message, }); } catch (error) { - console.error(error); + logger.error(error); HTTPResponse.internalServerError( res, "An unexpected error has occured.", @@ -82,7 +78,7 @@ export const getUserChat = async (req: AuthRequest, res: Response) => { HTTPResponse.ok(res, "Chat successfully retrieved", result); } catch (error) { - console.error(error); + logger.error(error); HTTPResponse.internalServerError( res, "An unexpcted error has occured", @@ -113,7 +109,7 @@ export const deleteChat = async (req: AuthRequest, res: Response) => { await Chat.findOneAndDelete({ _id: id }); HTTPResponse.ok(res, "Chat successfully deleted"); } catch (error) { - console.error(error); + logger.error(error); HTTPResponse.internalServerError( res, "An unexpected error has occurred", @@ -155,7 +151,7 @@ export const updateChat = async (req: AuthRequest, res: Response) => { HTTPResponse.ok(res, "Chat title successfully update", toUpdate); } catch (error) { - console.error(error); + logger.error(error); HTTPResponse.internalServerError( res, "An unexpected error has occurred", diff --git a/src/controller/message.controller.ts b/src/controller/message.controller.ts index 8f3decb..886de32 100644 --- a/src/controller/message.controller.ts +++ b/src/controller/message.controller.ts @@ -9,6 +9,7 @@ import { generateResponse, } from "../service/prompt-service"; import { Types } from "mongoose"; +import logger from "../util/logger"; export const createMessage = async (req: AuthRequest, res: Response) => { try { @@ -69,12 +70,8 @@ export const createMessage = async (req: AuthRequest, res: Response) => { HTTPResponse.created(res, "Message successfully created", message); } catch (error) { - console.log(error); - HTTPResponse.internalServerError( - res, - "An unexpected error has occurred", - error - ); + logger.error(error); + HTTPResponse.internalServerError(res, "An unexpected error has occurred"); } }; @@ -93,12 +90,8 @@ export const getMessagesByChatId = async (req: AuthRequest, res: Response) => { } HTTPResponse.ok(res, `Messages found for chatId ${chatId}`, messages); - } catch (error: unknown) { - console.log(error); - HTTPResponse.internalServerError( - res, - "An unexpected error has occurred", - error - ); + } catch (error) { + logger.error(error); + HTTPResponse.internalServerError(res, "An unexpected error has occurred"); } }; diff --git a/src/index.ts b/src/index.ts index 1c742ec..e96a701 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ import app from "./app"; import { connectDB } from "./util/database"; +import logger from "./util/logger"; const PORT = process.env.PORT || 8000; @@ -8,7 +9,7 @@ const initServer = async () => { await connectDB(); app.listen(PORT, () => { - console.log(`👍 Server running on port ${PORT}`); + logger.info(`Server running on port ${PORT}`); }); }; diff --git a/src/service/llms/gemini-service.ts b/src/service/llms/gemini-service.ts index 02dfdab..dfaea82 100644 --- a/src/service/llms/gemini-service.ts +++ b/src/service/llms/gemini-service.ts @@ -1,6 +1,7 @@ import { createPartFromUri, GoogleGenAI } from "@google/genai"; import { ConvoGenParam, GenAI } from "../llm-service"; import { GeminiConfig } from "../templates/gemini-configs"; +import logger from "../../util/logger"; const BASE_MODEL = "gemini-2.0-flash"; @@ -22,7 +23,7 @@ export class GeminiLLM implements GenAI { try { return await client.files.upload({ file: url }); } catch (err) { - console.error( + logger.error( `Error fetching or uploading file from URL: ${url}`, err ); @@ -62,7 +63,7 @@ export class GeminiLLM implements GenAI { ], }) .catch((error) => { - console.error(`[gemini-sercice] Error generating response: `, error); + logger.error(`[gemini-sercice] Error generating response: ${error}`); return null; }); @@ -70,8 +71,7 @@ export class GeminiLLM implements GenAI { return { text: "", image: "" }; } - console.info("[gemini-service] Generated response: " + geminiResponse.text); - + logger.info(`[gemini-service] generated response: ${geminiResponse.text}`); return { text: geminiResponse?.text?.trim() || "", image: "", diff --git a/src/service/prompt-service.ts b/src/service/prompt-service.ts index e3cdcd0..cabecce 100644 --- a/src/service/prompt-service.ts +++ b/src/service/prompt-service.ts @@ -1,3 +1,4 @@ +import logger from "../util/logger"; import { ConvoGenParam, GenAI, LLM } from "./llm-service"; import { Template, @@ -15,17 +16,15 @@ export async function generateResponse( llm: GenAI = LLM.GEMINI ) { if (!prompt) { - console.warn( - "PromptResponseGenerator: generateResponse called without user input" - ); + logger.warn("[prompt-service] generateResponse called without user input"); throw new Error("User input is required"); } - console.info(`[prompt-service] Generating response for prompt: ${prompt}`); - console.debug( - `Attachments: ${JSON.stringify( + logger.info(`[prompt-service] generating response for prompt: ${prompt}`); + logger.info( + `[prompt-service] attachments: ${JSON.stringify( attachmentUrls - )}, Template: ${template}, History length: ${history?.length || 0}` + )}, template: ${template}, history length: ${history?.length || 0}` ); try { @@ -36,13 +35,12 @@ export async function generateResponse( history: history || [], }); - console.info("[prompt-service] Returning text response: ", response.text); return { text: response?.text, image: "", }; } catch (error) { - console.error(error); + logger.error(error); return { response: JSON.parse( JSON.stringify({ error: "Failed to generate response" }) diff --git a/src/util/database.ts b/src/util/database.ts index eb773a1..762ddb7 100644 --- a/src/util/database.ts +++ b/src/util/database.ts @@ -1,4 +1,5 @@ import mongoose from "mongoose"; +import logger from "./logger"; export const connectDB = async () => { const IS_DOCKERIZED = process.env.IS_DOCKERIZED === "true"; @@ -8,17 +9,17 @@ export const connectDB = async () => { : process.env.MONGO_URI_ATLAS; try { - console.debug( + logger.info( `${ IS_DOCKERIZED - ? "ℹ️ Connecting to containerized mongodb" - : "ℹ️ Connecting to mongodb atlas" + ? "Connecting to containerized mongodb" + : "Connecting to mongodb atlas" }` ); await mongoose.connect(MONGO_URI!); - console.log(`✅ Successfully connected to ${MONGO_URI}`); + logger.info(`Successfully connected to ${MONGO_URI}`); } catch (err) { - console.error("👎 MongoDB connection error:", err); + logger.error(`Error connecting to ${MONGO_URI}`, err); process.exit(1); } }; From e9f8f34e8835c2c795227a8b12884747b20fdee5 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:45:41 +0800 Subject: [PATCH 45/84] chore: Add winston and morgan to project dependencies --- package.json | 9 +- pnpm-lock.yaml | 254 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5c7578f..15b8ec4 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "start": "tsx src/index.ts", "dev": "tsx watch src/index.ts", "compose": "docker-compose up --build", - "lint": "eslint 'src/**/*.ts'", - "lint:fix": "eslint 'src/**/*.ts' --fix", + "lint": "eslint src/**/*.ts", + "lint:fix": "eslint src/**/*.ts --fix", "test": "jest", "test:coverage": "jest --coverage", "build": "tsc", @@ -20,6 +20,7 @@ "packageManager": "pnpm@10.10.0", "dependencies": { "@google/genai": "^1.5.1", + "@types/morgan": "^1.9.10", "bcryptjs": "^3.0.2", "cookie-parser": "^1.4.7", "cors": "^2.8.5", @@ -27,7 +28,9 @@ "express-validator": "^7.2.1", "jsonwebtoken": "^9.0.2", "mongodb": "^6.17.0", - "mongoose": "^8.16.0" + "mongoose": "^8.16.0", + "morgan": "^1.10.0", + "winston": "^3.17.0" }, "devDependencies": { "@eslint/js": "^9.29.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cdb573..43ba7bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@google/genai': specifier: ^1.5.1 version: 1.5.1 + '@types/morgan': + specifier: ^1.9.10 + version: 1.9.10 bcryptjs: specifier: ^3.0.2 version: 3.0.2 @@ -35,6 +38,12 @@ importers: mongoose: specifier: ^8.16.0 version: 8.16.0 + morgan: + specifier: ^1.10.0 + version: 1.10.0 + winston: + specifier: ^3.17.0 + version: 3.17.0 devDependencies: '@eslint/js': specifier: ^9.29.0 @@ -258,6 +267,13 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@emnapi/core@1.4.3': resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} @@ -710,6 +726,9 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/morgan@1.9.10': + resolution: {integrity: sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==} + '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -737,6 +756,9 @@ packages: '@types/supertest@6.0.3': resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/webidl-conversions@7.0.3': resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} @@ -1010,6 +1032,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + basic-auth@2.0.1: + resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} + engines: {node: '>= 0.8'} + bcryptjs@3.0.2: resolution: {integrity: sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==} hasBin: true @@ -1109,13 +1135,28 @@ packages: collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1166,6 +1207,14 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -1240,6 +1289,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -1382,6 +1434,9 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -1416,6 +1471,9 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} @@ -1611,6 +1669,9 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1856,6 +1917,9 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -1905,6 +1969,10 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + logform@2.7.0: + resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} + engines: {node: '>= 12.0.0'} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2037,6 +2105,10 @@ packages: resolution: {integrity: sha512-gLuAZsbwY0PHjrvfuXvUkUq9tXjyAjN3ioXph5Y6Seu7/Uo8xJaM+rrMbL/x34K4T3UTgtXRyfoq1YU16qKyIw==} engines: {node: '>=16.20.1'} + morgan@1.10.0: + resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} + engines: {node: '>= 0.8.0'} + mpath@0.9.0: resolution: {integrity: sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==} engines: {node: '>=4.0.0'} @@ -2045,6 +2117,9 @@ packages: resolution: {integrity: sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==} engines: {node: '>=14.0.0'} + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2095,13 +2170,24 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -2224,6 +2310,10 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -2254,9 +2344,16 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -2314,6 +2411,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -2331,6 +2431,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -2358,6 +2461,9 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2408,6 +2514,9 @@ packages: text-decoder@1.2.3: resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} @@ -2426,6 +2535,10 @@ packages: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -2518,6 +2631,9 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -2556,6 +2672,14 @@ packages: engines: {node: '>= 8'} hasBin: true + winston-transport@4.9.0: + resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==} + engines: {node: '>= 12.0.0'} + + winston@3.17.0: + resolution: {integrity: sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==} + engines: {node: '>= 12.0.0'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -2812,6 +2936,14 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@colors/colors@1.6.0': {} + + '@dabh/diagnostics@2.0.3': + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + '@emnapi/core@1.4.3': dependencies: '@emnapi/wasi-threads': 1.0.2 @@ -3323,6 +3455,10 @@ snapshots: '@types/mime@1.3.5': {} + '@types/morgan@1.9.10': + dependencies: + '@types/node': 24.0.3 + '@types/ms@2.1.0': {} '@types/node@24.0.3': @@ -3358,6 +3494,8 @@ snapshots: '@types/methods': 1.1.4 '@types/superagent': 8.1.9 + '@types/triple-beam@1.3.5': {} + '@types/webidl-conversions@7.0.3': {} '@types/whatwg-url@11.0.5': @@ -3643,6 +3781,10 @@ snapshots: base64-js@1.5.1: {} + basic-auth@2.0.1: + dependencies: + safe-buffer: 5.1.2 + bcryptjs@3.0.2: {} bignumber.js@9.3.0: {} @@ -3738,12 +3880,33 @@ snapshots: collect-v8-coverage@1.0.2: {} + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + + colorspace@1.1.4: + dependencies: + color: 3.2.1 + text-hex: 1.0.0 + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -3786,6 +3949,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + debug@2.6.9: + dependencies: + ms: 2.0.0 + debug@4.4.1: dependencies: ms: 2.1.3 @@ -3835,6 +4002,8 @@ snapshots: emoji-regex@9.2.2: {} + enabled@2.0.0: {} + encodeurl@2.0.0: {} error-ex@1.3.2: @@ -4051,6 +4220,8 @@ snapshots: dependencies: bser: 2.1.1 + fecha@4.2.3: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -4097,6 +4268,8 @@ snapshots: flatted@3.3.3: {} + fn.name@1.1.0: {} + follow-redirects@1.15.9(debug@4.4.1): optionalDependencies: debug: 4.4.1 @@ -4303,6 +4476,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.2: {} + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -4744,6 +4919,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kuler@2.0.0: {} + leven@3.1.0: {} levn@0.4.1: @@ -4781,6 +4958,15 @@ snapshots: lodash@4.17.21: {} + logform@2.7.0: + dependencies: + '@colors/colors': 1.6.0 + '@types/triple-beam': 1.3.5 + fecha: 4.2.3 + ms: 2.1.3 + safe-stable-stringify: 2.5.0 + triple-beam: 1.4.1 + lru-cache@10.4.3: {} lru-cache@5.1.1: @@ -4918,6 +5104,16 @@ snapshots: - socks - supports-color + morgan@1.10.0: + dependencies: + basic-auth: 2.0.1 + debug: 2.6.9 + depd: 2.0.0 + on-finished: 2.3.0 + on-headers: 1.0.2 + transitivePeerDependencies: + - supports-color + mpath@0.9.0: {} mquery@5.0.0: @@ -4926,6 +5122,8 @@ snapshots: transitivePeerDependencies: - supports-color + ms@2.0.0: {} + ms@2.1.3: {} napi-postinstall@0.2.4: {} @@ -4958,14 +5156,24 @@ snapshots: object-inspect@1.13.4: {} + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 + on-headers@1.0.2: {} + once@1.4.0: dependencies: wrappy: 1.0.2 + one-time@1.0.0: + dependencies: + fn.name: 1.1.0 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -5073,6 +5281,12 @@ snapshots: react-is@18.3.1: {} + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + require-directory@2.1.1: {} resolve-cwd@3.0.0: @@ -5101,8 +5315,12 @@ snapshots: dependencies: queue-microtask: 1.2.3 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} + safe-stable-stringify@2.5.0: {} + safer-buffer@2.1.2: {} semver@6.3.1: {} @@ -5176,6 +5394,10 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + slash@3.0.0: {} source-map-support@0.5.13: @@ -5191,6 +5413,8 @@ snapshots: sprintf-js@1.0.3: {} + stack-trace@0.0.10: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -5223,6 +5447,10 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -5286,6 +5514,8 @@ snapshots: dependencies: b4a: 1.6.7 + text-hex@1.0.0: {} + tmpl@1.0.5: {} to-regex-range@5.0.1: @@ -5300,6 +5530,8 @@ snapshots: dependencies: punycode: 2.3.1 + triple-beam@1.4.1: {} + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -5399,6 +5631,8 @@ snapshots: dependencies: punycode: 2.3.1 + util-deprecate@1.0.2: {} + uuid@9.0.1: {} v8-to-istanbul@9.3.0: @@ -5433,6 +5667,26 @@ snapshots: dependencies: isexe: 2.0.0 + winston-transport@4.9.0: + dependencies: + logform: 2.7.0 + readable-stream: 3.6.2 + triple-beam: 1.4.1 + + winston@3.17.0: + dependencies: + '@colors/colors': 1.6.0 + '@dabh/diagnostics': 2.0.3 + async: 3.2.6 + is-stream: 2.0.1 + logform: 2.7.0 + one-time: 1.0.0 + readable-stream: 3.6.2 + safe-stable-stringify: 2.5.0 + stack-trace: 0.0.10 + triple-beam: 1.4.1 + winston-transport: 4.9.0 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: From f7b069b575ac0477572d8ba49fee69a68b17728c Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:46:00 +0800 Subject: [PATCH 46/84] feat: Implement morgan middleware to log http requests --- src/app.ts | 2 ++ src/middleware/httpLogger.ts | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/middleware/httpLogger.ts diff --git a/src/app.ts b/src/app.ts index 82f0df3..51fc7c9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -9,6 +9,7 @@ import messageRoutes from "./routes/message.routes"; import { HTTPResponse } from "./util/http-response"; import { authenticateToken } from "./middleware/authenticate-token"; import { Request, Response } from "express"; +import httpLogger from "./middleware/httpLogger"; dotenv.config(); @@ -25,6 +26,7 @@ if (process.env.ENABLE_CORS === "true") { ); } app.use(cookieParser()); +app.use(httpLogger); // Routes app.get("/health", (_req: Request, res: Response) => { diff --git a/src/middleware/httpLogger.ts b/src/middleware/httpLogger.ts new file mode 100644 index 0000000..8a98437 --- /dev/null +++ b/src/middleware/httpLogger.ts @@ -0,0 +1,10 @@ +import morgan from "morgan"; +import logger from "../util/logger"; + +const httpLogger = morgan("combined", { + stream: { + write: (message: string) => logger.http(message.trim()), + }, +}); + +export default httpLogger; From ece74feea508b6e668f7f76c8cdd6fc92f3c73d6 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 29 Jun 2025 23:47:52 +0800 Subject: [PATCH 47/84] refactor: Update systemInstruction for improved title and subject generation accuracy --- src/service/templates/gemini-configs.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/service/templates/gemini-configs.ts b/src/service/templates/gemini-configs.ts index ffef5a5..dfd1b6c 100644 --- a/src/service/templates/gemini-configs.ts +++ b/src/service/templates/gemini-configs.ts @@ -20,8 +20,7 @@ export const GeminiConfig: TemplateTypeConfig = { maxOutputTokens: 128, temperature: 0.8, systemInstruction: - AI_TUTOR_INSTRUCTION + - "For this task you are expcted to generated a title / name (short and catchy) for the following text. And choose the right topic.", + "For this task you are expcted to generate a title or name (short and catchy) for the following text. And categorize the text based on the subject.", responseMimeType: "application/json", responseSchema: { type: Type.OBJECT, From e27c4c3da28bc0fd3be6726f355b2ca3362dbcd8 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:22:10 +0800 Subject: [PATCH 48/84] test: Add unit tests for prompt-service --- src/test/unit/prompt-service.test.ts | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/test/unit/prompt-service.test.ts diff --git a/src/test/unit/prompt-service.test.ts b/src/test/unit/prompt-service.test.ts new file mode 100644 index 0000000..aeb685f --- /dev/null +++ b/src/test/unit/prompt-service.test.ts @@ -0,0 +1,50 @@ +import { ConvoGenParam } from "./../../service/llm-service"; +import { generateResponse } from "../../service/prompt-service"; + +const mockPrompt: ConvoGenParam = { + prompt: "Mock prompt", + template: "default", +}; + +const mockResponse = { + text: "Mock Response", + image: "", +}; + +// Mock LLM with no prompt +const mockLLM = { + invoke: jest.fn(), +}; + +const mockNoPrompt: ConvoGenParam = { prompt: "", template: "default" }; + +describe("generate prompt", () => { + // Missing prompt + test("should return error when prompt is missing", async () => { + await expect(generateResponse(mockNoPrompt, mockLLM)).rejects.toThrow( + "User input is required" + ); + }); + + // With prompt + test("should return response when prompt is provided", async () => { + // Configure `invoke` as a Jest mock so we can set its return value + (mockLLM.invoke as jest.Mock).mockResolvedValue(mockResponse); + + const response = await generateResponse(mockPrompt, mockLLM); + expect(response).toEqual(mockResponse); + }); + + // With prompt and invoke fails + test("should return error when llm fails", async () => { + (mockLLM.invoke as jest.Mock).mockRejectedValue(new Error("LLM Failure")); + const response = await generateResponse(mockPrompt, mockLLM); + + expect(response).toEqual({ + response: JSON.parse( + JSON.stringify({ error: "Failed to generate response" }) + ), + image: "", + }); + }); +}); From 34d585b76aefc681448390f80ff2f2ee17bb717c Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:13:39 +0800 Subject: [PATCH 49/84] refactor: rename response field to json_response for clarity --- src/model/message.model.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/model/message.model.ts b/src/model/message.model.ts index 0375c1c..d162081 100644 --- a/src/model/message.model.ts +++ b/src/model/message.model.ts @@ -4,7 +4,7 @@ export interface IMessage { _id: mongoose.Schema.Types.ObjectId; chat: mongoose.Schema.Types.ObjectId; prompt: string; - response?: JSON; + json_response?: JSON; image?: string; } @@ -20,7 +20,7 @@ const messageSchema = new mongoose.Schema( required: true, trim: true, }, - response: { + json_response: { type: mongoose.Schema.Types.Mixed, required: false, }, From 5358fdc648d17009f568bffe40855fed9e9315b3 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:14:24 +0800 Subject: [PATCH 50/84] refactor: unify response format and return structured PromptResponse --- src/service/prompt-service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/service/prompt-service.ts b/src/service/prompt-service.ts index cabecce..3ab20d1 100644 --- a/src/service/prompt-service.ts +++ b/src/service/prompt-service.ts @@ -11,10 +11,15 @@ export interface ConversationHistory { response: string; } +export interface PromptResponse { + response: TemplateResponseMap[T]; + image: string; +} + export async function generateResponse( { prompt, attachmentUrls, template, history }: ConvoGenParam, llm: GenAI = LLM.GEMINI -) { +): Promise> { if (!prompt) { logger.warn("[prompt-service] generateResponse called without user input"); throw new Error("User input is required"); @@ -28,7 +33,7 @@ export async function generateResponse( ); try { - const response = await llm.invoke({ + const llmResponse = await llm.invoke({ prompt: prompt, attachmentUrls: attachmentUrls ?? [], template: template || Template.DEFAULT, @@ -36,7 +41,7 @@ export async function generateResponse( }); return { - text: response?.text, + response: JSON.parse(llmResponse.text), image: "", }; } catch (error) { From 44c2b20290014b0227f751d8fa350f4d0d716e85 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:15:24 +0800 Subject: [PATCH 51/84] refactor: use structured PromptResponse instead of parsing raw text --- src/controller/chat.controller.ts | 7 +++---- src/controller/message.controller.ts | 10 +++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/controller/chat.controller.ts b/src/controller/chat.controller.ts index 3fdc724..57727f8 100644 --- a/src/controller/chat.controller.ts +++ b/src/controller/chat.controller.ts @@ -22,7 +22,7 @@ export const createChat = async (req: AuthRequest, res: Response) => { template: Template.GENERATE_TITLE, }); - const chatMetaData = JSON.parse(chatMetaDataGenerator.text!); + const chatMetaData = chatMetaDataGenerator.response; if (!chatMetaData) { logger.error("Failed to generate chat metadata"); @@ -41,17 +41,16 @@ export const createChat = async (req: AuthRequest, res: Response) => { return; } - const ai = await generateResponse({ + const promptResponse = await generateResponse({ prompt: prompt, attachmentUrls: attachmentUrls, template: templateType, }); - const parsed = JSON.parse(ai.text!); const message = await Message.create({ chat: chat.id, prompt: prompt, - response: parsed.response, + json_response: promptResponse.response, }); HTTPResponse.created(res, "Chat successfully created", { diff --git a/src/controller/message.controller.ts b/src/controller/message.controller.ts index 886de32..df7dc06 100644 --- a/src/controller/message.controller.ts +++ b/src/controller/message.controller.ts @@ -37,30 +37,30 @@ export const createMessage = async (req: AuthRequest, res: Response) => { const messages = await Message.find({ chat: chat.id }) .sort({ createdAt: 1 }) .lean(); + const history: ConversationHistory[] = messages.map((msg) => { return { prompt: msg.prompt, - response: JSON.stringify(msg.response), + response: JSON.stringify(msg.json_response), }; }); - const ai = await generateResponse({ + const promptResponse = await generateResponse({ prompt: prompt, attachmentUrls: attachmentUrls, template: templateType, history: history, }); - if (!ai) { + if (!promptResponse) { HTTPResponse.internalServerError(res, "Failed to generate response"); return; } - const parsed = JSON.parse(ai.text!); const message = await Message.create({ chat: chat.id, prompt: prompt, - response: parsed.response, + json_response: promptResponse.response, }); if (!message) { From 636f9aa7fcc30103265c75a3e66f446bf5e2e3e8 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:15:48 +0800 Subject: [PATCH 52/84] refactor: rename generate_title response field from name to title --- src/service/template-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/service/template-config.ts b/src/service/template-config.ts index e15d134..6d31d23 100644 --- a/src/service/template-config.ts +++ b/src/service/template-config.ts @@ -20,7 +20,7 @@ export type TemplateTypeConfig = { }; type GenerateTitleResponse = { - name: string; + title: string; subject: Subject; }; From c645db77c5cf877cfe35e4d72a4eee3877711b8d Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 30 Jun 2025 23:16:18 +0800 Subject: [PATCH 53/84] test: update tests to use typed PromptResponse and json_response field --- src/test/integration/chat.test.ts | 27 +++++++++++++++------------ src/test/integration/message.test.ts | 11 +++++++---- src/test/unit/prompt-service.test.ts | 7 +++++-- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/test/integration/chat.test.ts b/src/test/integration/chat.test.ts index fad6372..5a1b54c 100644 --- a/src/test/integration/chat.test.ts +++ b/src/test/integration/chat.test.ts @@ -4,6 +4,7 @@ import request from "supertest"; import User, { IUser } from "../../model/user.model"; import * as promptService from "../../service/prompt-service"; import Chat, { Subject } from "../../model/chat.model"; +import { PromptResponse } from "../../service/prompt-service"; const testUser = { firstName: "Test", @@ -12,19 +13,19 @@ const testUser = { password: "testpassword", }; -const mockChatMetaData = { - text: JSON.stringify({ - title: "Mock Title", - subject: Subject.GENERAL_KNOWLEDGE, - }), +const mockChatMetaData: PromptResponse<"generate_title"> = { + response: { + title: "Mock title", + subject: Subject.SCIENCE, + }, image: "", }; -const mockResponse = { - text: JSON.stringify({ +const mockResponse: PromptResponse<"tutor"> = { + response: { response: "Photosynthesis is the process by which plants use sunlight, water, and carbon dioxide to create oxygen and energy in the form of sugar.", - }), + }, image: "", }; @@ -76,10 +77,12 @@ describe("/chat", () => { expect(res.status).toEqual(201); expect(res.body.success).toEqual(true); expect(res.body.data).toBeDefined(); - expect(res.body.data.chat.title).toEqual("Mock Title"); - expect(res.body.data.chat.subject).toEqual("general_knowledge"); - expect(res.body.data.message.response).toEqual( - JSON.parse(mockResponse.text).response + expect(res.body.data.chat.title).toEqual(mockChatMetaData.response.title); + expect(res.body.data.chat.subject).toEqual( + mockChatMetaData.response.subject + ); + expect(res.body.data.message.json_response.response).toEqual( + mockResponse.response.response ); }); diff --git a/src/test/integration/message.test.ts b/src/test/integration/message.test.ts index 49d2dcb..9aff8e1 100644 --- a/src/test/integration/message.test.ts +++ b/src/test/integration/message.test.ts @@ -4,6 +4,7 @@ import { clearDatabase, closeDatabase, connectDatabase } from "../util/test-db"; import User, { IUser } from "../../model/user.model"; import Chat, { IChat, Subject } from "../../model/chat.model"; import * as promptService from "../../service/prompt-service"; +import { PromptResponse } from "../../service/prompt-service"; const testUser = { firstName: "Test", @@ -12,8 +13,10 @@ const testUser = { password: "testpassword", }; -const mockResponse = { - text: JSON.stringify({ response: "2" }), +const mockResponse: PromptResponse<"default"> = { + response: { + response: "Mock response", + }, image: "", }; @@ -60,8 +63,8 @@ describe("/message", () => { expect(res.body.success).toEqual(true); expect(res.body.data).toBeDefined(); expect(res.body.data.chat).toEqual(testChat.id); - expect(res.body.data.response).toEqual( - JSON.parse(mockResponse.text).response + expect(res.body.data.json_response.response).toEqual( + mockResponse.response.response ); }); diff --git a/src/test/unit/prompt-service.test.ts b/src/test/unit/prompt-service.test.ts index aeb685f..b37aaa7 100644 --- a/src/test/unit/prompt-service.test.ts +++ b/src/test/unit/prompt-service.test.ts @@ -7,7 +7,7 @@ const mockPrompt: ConvoGenParam = { }; const mockResponse = { - text: "Mock Response", + text: JSON.stringify({ response: "Mock response" }), image: "", }; @@ -32,7 +32,10 @@ describe("generate prompt", () => { (mockLLM.invoke as jest.Mock).mockResolvedValue(mockResponse); const response = await generateResponse(mockPrompt, mockLLM); - expect(response).toEqual(mockResponse); + expect(response).toEqual({ + response: JSON.parse(mockResponse.text), + image: "", + }); }); // With prompt and invoke fails From 348cd164abe023c4634387f2bc7d8d859c5f1121 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:08:16 +0800 Subject: [PATCH 54/84] chore: rename docker-compose.yml to dev.compose.yml for env-specific setup --- docker-compose.yaml => dev.compose.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) rename docker-compose.yaml => dev.compose.yaml (86%) diff --git a/docker-compose.yaml b/dev.compose.yaml similarity index 86% rename from docker-compose.yaml rename to dev.compose.yaml index 96e8e0d..b4fbd66 100644 --- a/docker-compose.yaml +++ b/dev.compose.yaml @@ -1,17 +1,18 @@ # Define all services (containers) services: # MongoDB service - mongo: + mongodb: # Use official mongoDB image image: mongo:latest # Always restart unless forcibly stop the process. restart: unless-stopped ports: - "27017:27017" + # Create volume within src directory volumes: - - mongo-data:/data/db + - ./database/mongodb-data:/data/db # Backend service - expressjs: + express-server: # Build the docker image from the dockerfile in the current dir build: dockerfile: Dockerfile.dev @@ -29,10 +30,7 @@ services: # Wait for mongo servvice to start depends_on: - - mongo + - mongodb # Override the default command and run npm start command: pnpm start - -volumes: - mongo-data: From 5dcc7204e427e761c727594eb1aa0f0999bce947 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:16:41 +0800 Subject: [PATCH 55/84] chore: add Docker Compose scripts for development --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 15b8ec4..f66ffa7 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "scripts": { "start": "tsx src/index.ts", "dev": "tsx watch src/index.ts", - "compose": "docker-compose up --build", + "compose": "docker-compose -f dev.compose.yaml up", + "compose:build": "docker-compose -f dev.compose.yaml up --build", + "compose:down": "docker-compose -f dev.compose.yaml down", "lint": "eslint src/**/*.ts", "lint:fix": "eslint src/**/*.ts --fix", "test": "jest", From 30141c0bdff41b569887c6f3d2d9f0772c6e5e1d Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:34:44 +0800 Subject: [PATCH 56/84] chore: remove cors middleware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit – no cross-origin browser access required --- src/app.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/app.ts b/src/app.ts index 51fc7c9..3c470ac 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,5 @@ import express from "express"; import dotenv from "dotenv"; -import cors from "cors"; import cookieParser from "cookie-parser"; import authRoutes from "./routes/auth.routes"; @@ -17,14 +16,6 @@ const app = express(); // Middleware app.use(express.json()); -if (process.env.ENABLE_CORS === "true") { - app.use( - cors({ - origin: process.env.CLIENT_IP, - credentials: true, - }) - ); -} app.use(cookieParser()); app.use(httpLogger); From 5b62dad509b3ab4284889d286331eabb25986d27 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:35:51 +0800 Subject: [PATCH 57/84] chore: update dependencies - Removed cors (no longer needed without frontend/browser requests) - Moved @types/morgan to devDependencies --- package.json | 3 +-- pnpm-lock.yaml | 24 +++--------------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index f66ffa7..309c873 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,8 @@ "packageManager": "pnpm@10.10.0", "dependencies": { "@google/genai": "^1.5.1", - "@types/morgan": "^1.9.10", "bcryptjs": "^3.0.2", "cookie-parser": "^1.4.7", - "cors": "^2.8.5", "express": "^5.1.0", "express-validator": "^7.2.1", "jsonwebtoken": "^9.0.2", @@ -41,6 +39,7 @@ "@types/express": "^5.0.3", "@types/jest": "^30.0.0", "@types/jsonwebtoken": "^9.0.10", + "@types/morgan": "^1.9.10", "@types/node": "^24.0.3", "@types/supertest": "^6.0.3", "dotenv": "^16.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 43ba7bd..480d021 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,18 +11,12 @@ importers: '@google/genai': specifier: ^1.5.1 version: 1.5.1 - '@types/morgan': - specifier: ^1.9.10 - version: 1.9.10 bcryptjs: specifier: ^3.0.2 version: 3.0.2 cookie-parser: specifier: ^1.4.7 version: 1.4.7 - cors: - specifier: ^2.8.5 - version: 2.8.5 express: specifier: ^5.1.0 version: 5.1.0 @@ -63,6 +57,9 @@ importers: '@types/jsonwebtoken': specifier: ^9.0.10 version: 9.0.10 + '@types/morgan': + specifier: ^1.9.10 + version: 1.9.10 '@types/node': specifier: ^24.0.3 version: 24.0.3 @@ -1199,10 +1196,6 @@ packages: cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -2162,10 +2155,6 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -3938,11 +3927,6 @@ snapshots: cookiejar@2.1.4: {} - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -5152,8 +5136,6 @@ snapshots: dependencies: path-key: 3.1.1 - object-assign@4.1.1: {} - object-inspect@1.13.4: {} on-finished@2.3.0: From 4a6e898100628e90608a93c4ce9d3a51969af163 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:36:23 +0800 Subject: [PATCH 58/84] chore: relax type checks and refine exclusions --- tsconfig.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index c059485..bc05ddd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,9 @@ "outDir": "dist", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, - "strict": true + "strict": true, + "skipLibCheck": true, + "moduleResolution": "node" }, - "exclude": ["node_modules", "**/*.test.ts"] + "exclude": ["node_modules", "**/*.test.ts", "tests", "coverage"] } From 7c5810bd6293afd2c1cff2cdd9b89882afce4a37 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:37:57 +0800 Subject: [PATCH 59/84] chore: update .gitignore and .dockerignore for cleaner workspace - Ignore build outputs, dependencies, env files, and coverage - Added Docker-related and local dev/testing files to ignore lists --- .dockerignore | 17 ++++++++++++++++- .gitignore | 20 ++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.dockerignore b/.dockerignore index cb95608..a8f116e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,20 @@ +# Ignore dependencies and build outputs node_modules dist +coverage + +# Ignore environment and config files +.env + +# Ignore Docker files themselves (optional) Dockerfile +Dockerfile.dev +docker-compose.yml +dev.compose.yml .dockerignore -.env \ No newline at end of file + +# Ignore local database or test assets +database + +# Ignore local API testing/debugging tools like Bruno collections +BrainBytes \ No newline at end of file diff --git a/.gitignore b/.gitignore index 99f3a73..a8f116e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,20 @@ +# Ignore dependencies and build outputs node_modules -.env +dist coverage -dist \ No newline at end of file + +# Ignore environment and config files +.env + +# Ignore Docker files themselves (optional) +Dockerfile +Dockerfile.dev +docker-compose.yml +dev.compose.yml +.dockerignore + +# Ignore local database or test assets +database + +# Ignore local API testing/debugging tools like Bruno collections +BrainBytes \ No newline at end of file From 394b6bd89377d430c9dc309c17ffc79fdccb79ba Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Fri, 4 Jul 2025 14:38:28 +0800 Subject: [PATCH 60/84] chore: update .env.template to remove unused CORS config --- .env.template | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.env.template b/.env.template index 0b4eb15..ec7de86 100644 --- a/.env.template +++ b/.env.template @@ -1,7 +1,6 @@ PORT= NODE_ENV= -ENABLE_CORS= -CLIENT_IP= + JWT_SECRET= IS_DOCKERIZED= MONGO_URI_ATLAS= From e65c7b46eca6536c207d128bba5da1d470c90b2b Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 5 Jul 2025 13:09:57 +0800 Subject: [PATCH 61/84] chore: rename backend service to expressjs for consistency --- dev.compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev.compose.yaml b/dev.compose.yaml index b4fbd66..feae0c8 100644 --- a/dev.compose.yaml +++ b/dev.compose.yaml @@ -12,7 +12,7 @@ services: volumes: - ./database/mongodb-data:/data/db # Backend service - express-server: + expressjs: # Build the docker image from the dockerfile in the current dir build: dockerfile: Dockerfile.dev From cf44b8c1479538ba4fdf4b33996f053bbdf8c021 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 5 Jul 2025 13:42:00 +0800 Subject: [PATCH 62/84] chore: Add Dockerfile and docker compose for production environment --- Dockerfile.prod | 31 +++++++++++++++++++++++++++++++ prod.compose.yaml | 23 +++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 Dockerfile.prod create mode 100644 prod.compose.yaml diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 0000000..370f46c --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,31 @@ +# Stage 1: Build +FROM node:24-alpine AS builder + +WORKDIR /app + +# Install pnpm globally +RUN npm install -g pnpm + +# Copy only package files first (to cache install layers) +COPY package.json pnpm-lock.yaml ./ + +RUN pnpm install + +# Then copy rest of the source +COPY . . + +RUN pnpm build + +# Stage 2: Run only compiled code +FROM node:24-alpine + +WORKDIR /app + +# Copy only the necessary runtime files from the first stage into the second stage +COPY --from=builder /app/package.json ./ +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/dist ./dist + +EXPOSE 8000 + +CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/prod.compose.yaml b/prod.compose.yaml new file mode 100644 index 0000000..1890824 --- /dev/null +++ b/prod.compose.yaml @@ -0,0 +1,23 @@ +services: + mongodb: + image: mongo:6.0 + restart: unless-stopped + volumes: + - mongodb-data:/data/db + expressjs: + build: + context: . + dockerfile: Dockerfile.prod + ports: + - "8000:8000" + env_file: + - .env + environment: + - IS_DOCKERIZED=true + - NODE_ENV=prod + depends_on: + - mongodb + command: ["node", "dist/index.js"] + +volumes: + mongodb-data: From 9aac1d7b15326bbeca5aa950be64216be0c887ce Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:35:59 +0800 Subject: [PATCH 63/84] ci: setup GitHub Actions workflow with workflow_dispatch for testing --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3b6fa68 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: BrainBytes CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: "pnpm" + + - name: Install pnpm + run: npm install -g pnpm + + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + + - name: Run backend tests + working-directory: ./src + run: pnpm test From 863dd740115e226ed88ec053f7c994ebb37574ca Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:42:33 +0800 Subject: [PATCH 64/84] ci: updated GitHub Actions workflow for testing --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b6fa68..72d8727 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,19 +13,17 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 + - name: Check out repository + uses: actions/checkout@v2 - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: "pnpm" + uses: actions/setup-node@v2 - name: Install pnpm run: npm install -g pnpm - name: Install dependencies + working-directory: ./src run: pnpm install --no-frozen-lockfile - name: Run backend tests From f9050ee86648eb15bd77f75d68c02af1ae408f60 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:54:46 +0800 Subject: [PATCH 65/84] fix(ci): update GitHub Actions workflow for testing - Set JWT_SECRET and MONGO_URI_DOCKER as environment secrets - Added 'environment: GitHub actions' to job --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72d8727..57a88b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,10 @@ jobs: test: name: Run Tests runs-on: ubuntu-latest + environment: GitHub actions + env: + JWT_SECRET: ${{secrets.JWT_SECRET}} + MONGO_URI_DOCKER: ${{secrets.MONGO_URI_DOCKER}} steps: - name: Check out repository From 33ce5b3c892837bfab2642bb810d7a067882e83f Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:09:55 +0800 Subject: [PATCH 66/84] ci: add code quality check with lint job and improve test job with pnpm cache, node versioning --- .github/workflows/ci.yml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57a88b6..42e62a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,8 +8,36 @@ on: workflow_dispatch: jobs: + lint: + name: Check Code Quality + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v2 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: "24.1.0" + cache: "pnpm" + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: "10.10.0" + + - name: Install dependencies + working-directory: ./src + run: pnpm install --no-frozen-lockfile + + - name: Lint source code + working-directory: ./src + run: pnpm lint + test: name: Run Tests + needs: lint runs-on: ubuntu-latest environment: GitHub actions env: @@ -22,9 +50,14 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 + with: + node-version: "24.1.0" + cache: "pnpm" - name: Install pnpm - run: npm install -g pnpm + uses: pnpm/action-setup@v2 + with: + version: "10.10.0" - name: Install dependencies working-directory: ./src From d140e9dd312eb18a202a81ecd2109233d72f1df6 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 6 Jul 2025 13:15:37 +0800 Subject: [PATCH 67/84] fix(ci): use pnpm/action-setup to install Node.js and pnpm, removing setup-node to fix pnpm not found error --- .github/workflows/ci.yml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42e62a3..652a309 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,16 +16,18 @@ jobs: - name: Check out repository uses: actions/checkout@v2 - - name: Setup Node.js - uses: actions/setup-node@v2 + - name: Install pnpm and Node.js + uses: pnpm/action-setup@v2 with: + version: "10.10.0" node-version: "24.1.0" cache: "pnpm" - - name: Install pnpm - uses: pnpm/action-setup@v2 - with: - version: "10.10.0" + # - name: Setup Node.js + # uses: actions/setup-node@v2 + # with: + # node-version: "24.1.0" + # cache: "pnpm" - name: Install dependencies working-directory: ./src @@ -48,16 +50,18 @@ jobs: - name: Check out repository uses: actions/checkout@v2 - - name: Setup Node.js - uses: actions/setup-node@v2 + - name: Install pnpm and Node.js + uses: pnpm/action-setup@v2 with: + version: "10.10.0" node-version: "24.1.0" cache: "pnpm" - - name: Install pnpm - uses: pnpm/action-setup@v2 - with: - version: "10.10.0" + # - name: Setup Node.js + # uses: actions/setup-node@v2 + # with: + # node-version: "24.1.0" + # cache: "pnpm" - name: Install dependencies working-directory: ./src From e70796509b83bf9ca83002d788ee29edcb90113e Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 6 Jul 2025 19:52:59 +0800 Subject: [PATCH 68/84] chore(ci): update GitHub Actions workflow - Upgrade actions/checkout to v4 - Upgrade pnpm/action-setup to v4 - Add Docker build job using buildx and GHCR - Use docker/login-action and build-push-action for publishing - Ignore changes to Markdown and docs in CI triggers --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 652a309..b9e5486 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,14 @@ name: BrainBytes CI on: push: branches: [main, develop] + paths-ignore: + - "**.md" + - "docs/**" pull_request: branches: [main, develop] + paths-ignore: + - "**.md" + - "docs/**" workflow_dispatch: jobs: @@ -14,21 +20,15 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install pnpm and Node.js - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v4 with: version: "10.10.0" node-version: "24.1.0" cache: "pnpm" - # - name: Setup Node.js - # uses: actions/setup-node@v2 - # with: - # node-version: "24.1.0" - # cache: "pnpm" - - name: Install dependencies working-directory: ./src run: pnpm install --no-frozen-lockfile @@ -48,21 +48,15 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install pnpm and Node.js - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v4 with: version: "10.10.0" node-version: "24.1.0" cache: "pnpm" - # - name: Setup Node.js - # uses: actions/setup-node@v2 - # with: - # node-version: "24.1.0" - # cache: "pnpm" - - name: Install dependencies working-directory: ./src run: pnpm install --no-frozen-lockfile @@ -70,3 +64,32 @@ jobs: - name: Run backend tests working-directory: ./src run: pnpm test + + build: + name: Build Docker Image + needs: ["lint", "test"] + runs-on: ubuntu-latest + environment: GitHub actions + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Image + uses: docker/build-push-action@v4 + with: + context: ./src + push: true + tags: ghcr.io/${{ github.repository_owner }}-api:latest + cache-from: type=gha + cache-to: type=gha,mode=max From 375a504d8dfb2bcd39635a2f177d19dddb118409 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 6 Jul 2025 20:01:32 +0800 Subject: [PATCH 69/84] fix(ci): update build job to use Dockerfile.prod - Specify Dockerfile.prod as build target --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b9e5486..182e81a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: - name: Build and push Image uses: docker/build-push-action@v4 with: - context: ./src + file: ./Dockerfile.prod push: true tags: ghcr.io/${{ github.repository_owner }}-api:latest cache-from: type=gha From 8c4724e240cbda3fb9a81f32d8aba6d6020c8696 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 6 Jul 2025 20:05:03 +0800 Subject: [PATCH 70/84] fix(ci): correct GHCR image tag format in Docker build step - Use ghcr.io//:tag format --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 182e81a..4ee8687 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,6 +90,6 @@ jobs: with: file: ./Dockerfile.prod push: true - tags: ghcr.io/${{ github.repository_owner }}-api:latest + tags: ghcr.io/${{ github.repository_owner }}/brainbytes-api:latest cache-from: type=gha cache-to: type=gha,mode=max From 841eb53afa58ab46a6114ff9c5aa611e77fb24fc Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Sun, 6 Jul 2025 20:25:36 +0800 Subject: [PATCH 71/84] fix(ci): use personal access token for GHCR authentication - Replace GITHUB_TOKEN with GHCR_PAT to bypass permission issues - Fix "denied: installation not allowed to Create organization package" error --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ee8687..7d6eb8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + password: ${{ secrets.GHCR_PAT }} - name: Build and push Image uses: docker/build-push-action@v4 From f459ae9e3fa01426248fde80f482d1f931c8c4db Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:39:16 +0800 Subject: [PATCH 72/84] replace console.log with custom logger util --- src/util/database.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/database.ts b/src/util/database.ts index 762ddb7..bb95626 100644 --- a/src/util/database.ts +++ b/src/util/database.ts @@ -29,7 +29,7 @@ export const flushDB = async () => { const db = mongoose.connection.db; if (!db) throw new Error("Database connection not available"); const collections = await db.listCollections().toArray(); - console.log("🗑️ Flushing DB"); + logger.info("🗑️ Flushing DB"); for (const collection of collections) { await mongoose.connection.db?.dropCollection(collection.name); } From a0983801aba7e99ec9f613c74712961ee52223a4 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:40:30 +0800 Subject: [PATCH 73/84] chore: update NODE_ENV from 'prod' to 'production' in expressjs service --- prod.compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prod.compose.yaml b/prod.compose.yaml index 1890824..da978b1 100644 --- a/prod.compose.yaml +++ b/prod.compose.yaml @@ -14,7 +14,7 @@ services: - .env environment: - IS_DOCKERIZED=true - - NODE_ENV=prod + - NODE_ENV=production depends_on: - mongodb command: ["node", "dist/index.js"] From 6e62d81ad3916833b6f75c4800120d86aefbcf37 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:41:01 +0800 Subject: [PATCH 74/84] chore: update start script to run compiled code with Node.js --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 309c873..d9141af 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "", "main": "src/index.ts", "scripts": { - "start": "tsx src/index.ts", + "start": "node dist/index.js", "dev": "tsx watch src/index.ts", "compose": "docker-compose -f dev.compose.yaml up", "compose:build": "docker-compose -f dev.compose.yaml up --build", From 5ae19bae1568b83639bed8700c8f65cd38700ce6 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:04:16 +0800 Subject: [PATCH 75/84] fix(ci): update GitHub Actions to use 'master' instead of 'main' branch --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d6eb8f..0e46c85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,12 @@ name: BrainBytes CI on: push: - branches: [main, develop] + branches: [master, develop] paths-ignore: - "**.md" - "docs/**" pull_request: - branches: [main, develop] + branches: [master, develop] paths-ignore: - "**.md" - "docs/**" From 6189d7afbfce8f160a6f8812825bd17dd8c301be Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Thu, 10 Jul 2025 17:04:39 +0800 Subject: [PATCH 76/84] chore(docker): use prebuilt image for expressjs service from GHCR --- prod.compose.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/prod.compose.yaml b/prod.compose.yaml index da978b1..21f7dd2 100644 --- a/prod.compose.yaml +++ b/prod.compose.yaml @@ -5,9 +5,7 @@ services: volumes: - mongodb-data:/data/db expressjs: - build: - context: . - dockerfile: Dockerfile.prod + image: ghcr.io/iodsky/brainbytes-api:latest ports: - "8000:8000" env_file: From 2b4105213b34da5032cf7fd0d53f35ce87801679 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:36:49 +0800 Subject: [PATCH 77/84] ci: Optimize workflow by seperating node.js setup and add build job condition - Use actions/setup-node to explicitly install node.js with pnpm cache support - Use pnpm/action-setup only for installing pnpm CLI - Add conditional check to run docker image build only on push to master (post-merge). --- .github/workflows/ci.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e46c85..8648529 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,13 +22,17 @@ jobs: - name: Check out repository uses: actions/checkout@v4 - - name: Install pnpm and Node.js - uses: pnpm/action-setup@v4 + - name: Install Node.js and cache + uses: actions/setup-node@v4 with: - version: "10.10.0" node-version: "24.1.0" cache: "pnpm" + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: "10.10.0" + - name: Install dependencies working-directory: ./src run: pnpm install --no-frozen-lockfile @@ -50,13 +54,17 @@ jobs: - name: Check out repository uses: actions/checkout@v4 - - name: Install pnpm and Node.js - uses: pnpm/action-setup@v4 + - name: Install Node.js and cache + uses: actions/setup-node@v4 with: - version: "10.10.0" node-version: "24.1.0" cache: "pnpm" + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: "10.10.0" + - name: Install dependencies working-directory: ./src run: pnpm install --no-frozen-lockfile @@ -70,6 +78,7 @@ jobs: needs: ["lint", "test"] runs-on: ubuntu-latest environment: GitHub actions + if: github.event_name == 'push' && github.ref == 'refs/heads/master' steps: - name: Check out repository From e109610e05dfba6bac95dccb918b549e0a66b8f3 Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:41:53 +0800 Subject: [PATCH 78/84] fix: ci install pnpm globally to resolve executable error - Replaced pnpm/action-setup with npm global install --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8648529..980d28d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,10 +28,8 @@ jobs: node-version: "24.1.0" cache: "pnpm" - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: "10.10.0" + - name: Install pnpm + run: npm install -g pnpm - name: Install dependencies working-directory: ./src @@ -60,10 +58,8 @@ jobs: node-version: "24.1.0" cache: "pnpm" - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: "10.10.0" + - name: Install pnpm + run: npm install -g pnpm - name: Install dependencies working-directory: ./src From c8870625b1788ceae971c28e1e2d29f1ddc9270f Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:45:07 +0800 Subject: [PATCH 79/84] fix(ci): remove cache pnpm to resolve missing executable file error --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 980d28d..b73d07b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: "24.1.0" - cache: "pnpm" - name: Install pnpm run: npm install -g pnpm @@ -56,7 +55,6 @@ jobs: uses: actions/setup-node@v4 with: node-version: "24.1.0" - cache: "pnpm" - name: Install pnpm run: npm install -g pnpm From 58f0fde6d105a98a1ae075b04dd6e8d322e8680e Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:59:35 +0800 Subject: [PATCH 80/84] fix(ci): properly configure pnpm setup and caching order in GitHub Actions - Use pnpm/action-setup before setup-node to ensure pnpm is available for caching --- .github/workflows/ci.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b73d07b..356be03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,13 +22,16 @@ jobs: - name: Check out repository uses: actions/checkout@v4 - - name: Install Node.js and cache + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: "10.10.0" + + - name: Install Node.js uses: actions/setup-node@v4 with: node-version: "24.1.0" - - - name: Install pnpm - run: npm install -g pnpm + cache: "pnpm" - name: Install dependencies working-directory: ./src @@ -51,10 +54,16 @@ jobs: - name: Check out repository uses: actions/checkout@v4 - - name: Install Node.js and cache + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: "10.10.0" + + - name: Install Node.js uses: actions/setup-node@v4 with: node-version: "24.1.0" + cache: "pnpm" - name: Install pnpm run: npm install -g pnpm From 8645fd89c138aa80f145900251eedc4abbb99f3e Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:06:03 +0800 Subject: [PATCH 81/84] fix(ci): Remove redundant pnpm install in test job --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 356be03..7c6b460 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,9 +65,6 @@ jobs: node-version: "24.1.0" cache: "pnpm" - - name: Install pnpm - run: npm install -g pnpm - - name: Install dependencies working-directory: ./src run: pnpm install --no-frozen-lockfile From 218c5b7ec8957057fc8dc1f43780af038ff9d81b Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Mon, 14 Jul 2025 17:06:54 +0800 Subject: [PATCH 82/84] fix(test): add testTimout config and set to 30 seconds --- jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest.config.js b/jest.config.js index 0f701df..8dbc2ff 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,4 +3,5 @@ export default { testEnvironment: "node", testMatch: ["**/test/**/*test.ts"], testPathIgnorePatterns: ["/node_modules"], + testTimeout: 30000, }; From 50bca8506a51a553e874c918ecf1ab753ce21fed Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:53:37 +0800 Subject: [PATCH 83/84] feat: add explicit Docker networks for prod services and Traefik integration - Defined 'brainbytes_net' as a project-scope network for internal service communication - Connected mongodb, expressjs, and watchtower to 'brainbytes_net' - Added 'traefik' external network for reverse proxy routing to expressjs - Removed unnecessary port binding from expressjs (relying on Traefik routing) - Improved network isolation and readiness for production deployment --- prod.compose.yaml | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/prod.compose.yaml b/prod.compose.yaml index 21f7dd2..2b00c8b 100644 --- a/prod.compose.yaml +++ b/prod.compose.yaml @@ -4,10 +4,11 @@ services: restart: unless-stopped volumes: - mongodb-data:/data/db + networks: + - brainbytes_net # Shared network with expressjs + expressjs: image: ghcr.io/iodsky/brainbytes-api:latest - ports: - - "8000:8000" env_file: - .env environment: @@ -16,6 +17,43 @@ services: depends_on: - mongodb command: ["node", "dist/index.js"] + # Metadata for reverse proxy and auto-updating + labels: + # Allow traefik to route to this service + - "traefik.enable=true" + # Route requests from brainbytes.iodsky.com to this service + - "traefik.http.routers.brainbytes.rule=Host(`brainbytes.iodsky.com`)" + # Enforce HTTPS + - "traefik.http.routers.brainbytes.entrypoints=websecure" + # Automatically request TLS certificates using Let's Encrypt + - "traefik.http.routers.brainbytes.tls.certresolver=myresolver" + # Route requests to port 8000 + - "traefik.http.services.expressjs.loadbalancer.server.port=8000" + + # Enable auto updates via watchtower + - "com.centurylinklabs.watchtower.enable=true" + networks: + - brainbytes_net + - traefik # Ensure connection with traefik + + watchtower: + image: containrrr/watchtower + command: + - "--label-enable" # Monitor contianers with explicit watchtower label + - "--interval=3600" + environment: + WATCHTOWER_CLEANUP: true # Remove old images after update + volumes: + - /var/run/docker.sock:/var/run/docker.sock + restart: always + networks: + - brainbytes_net volumes: mongodb-data: + +networks: + brainbytes_net: # Project-scope network + name: brainbytes_net + traefik: + external: true From 0c9ccc9f0af5233bb08d7df191230a7097c9df4b Mon Sep 17 00:00:00 2001 From: ibrahim <129620134+IbraDoesCode@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:54:11 +0800 Subject: [PATCH 84/84] feat: add Traefik reverse proxy with Docker provider and HTTPS support - Added Traefik service with dynamic config from Docker - Enabled HTTP/HTTPS entrypoints and Let's Encrypt TLS via ACME - Configured Traefik dashboard and secure service exposure - Mounted Docker socket and cert storage for automation - Declared external 'traefik' network for container routing --- traefik.compose.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 traefik.compose.yaml diff --git a/traefik.compose.yaml b/traefik.compose.yaml new file mode 100644 index 0000000..eb04ac0 --- /dev/null +++ b/traefik.compose.yaml @@ -0,0 +1,30 @@ +# This Traefik service acts as a reverse proxy, running in a container on the VPS. +services: + traefik: + image: traefik:latest + container_name: traefik + restart: unless-stopped + ports: + - "80:80" + - "443:443" + command: + - "--api.dashboard=true" # Enable Traefik dashboard at /dashboard + - "--api.insecure=false" + - "--log.level=DEBUG" + - "--providers.docker.network=traefik" + - "--providers.docker=true" # Use Docker as a dynamic configuration source + - "--providers.docker.exposedbydefault=false" # Only expose services explicitly labeled + - "--entrypoints.web.address=:80" # HTTP entrypoint + - "--entrypoints.websecure.address=:443" # HTTPS entrypoint + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" # Use TLS challenge for Let's Encrypt + - "--certificatesresolvers.myresolver.acme.email=iharby2002@gmail.com" # Email for Let's Encrypt registration + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" # Store certs here + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" # Read-only access to Docker API + - "./letsencrypt:/letsencrypt" # Persist Let's Encrypt certs + networks: + - traefik + +networks: + traefik: # External network used to connect Traefik with project-specific containers + external: true