diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..d7315c6d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,59 @@ +# Plantilla PR BackEnd TEXT & LINES + +--- + +## Descripción + + + +--- + +## Tipo de cambio + +- [ ] Corrección de error (cambio no disruptivo que soluciona un problema) +- [ ] Nueva funcionalidad (cambio no disruptivo que agrega funcionalidad) +- [ ] Cambio disruptivo (corrección o función que afecta la compatibilidad existente) +- [ ] Este cambio requiere actualización en la documentación +- [ ] Cambio mínimo (cambio estructural o visual sin impacto en la lógica principal) + +--- + +## Nueva funcionalidad (si aplica) + +- [ ] Controlador +- [ ] Ruta +- [ ] Repositorio +- [ ] Consulta +- [ ] Mensaje +- [ ] Middleware +- [ ] Servicio + +Si agregaste otro archivo relevante, indícalo aquí: +- `ejemploArchivo.js` + +--- + +## ¿Cómo se ha probado? + +- [ ] Se probaron manualmente los endpoints con Postman +- [ ] Pasaron las pruebas unitarias (si aplica) +- [ ] Se probaron integraciones con otros módulos +- [ ] No se mostraron errores en consola ni logs +- [ ] Validado en base de datos que el comportamiento es correcto + +--- + +### Cambios menores + +- [ ] Este PR realiza un cambio mínimo que no afecta la lógica del sistema +- [ ] Se validó manualmente que el cambio no afecta funcionalidades existentes +- [ ] No se realizaron pruebas automatizadas porque no aplica + +--- + +## Checklist del evaluador: + +- [ ] He determinado que el autor cumplió con todos los puntos mencionados +- [ ] He determinado que los cambios no tienen un impacto negativo en la aplicación +- [ ] He notificado al autor del PR en caso de dudas o ajustes necesarios +- [ ] Una vez que acepte el PR, eliminaré la rama correspondiente \ No newline at end of file diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 00000000..b9819b4b --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,68 @@ +name: Node.js CI/CD + +on: + push: + branches: + - main + - staging + +jobs: + deploy-production: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-22.04 + steps: + - name: Setup SSH + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + SERVER_IP: ${{ secrets.SERVER_IP }} + run: | + mkdir -p ~/.ssh + echo "$DEPLOY_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H $SERVER_IP >> ~/.ssh/known_hosts + ssh-keyscan -H github.com >> ~/.ssh/known_hosts + + - name: Deploy to Production + env: + SERVER_IP: ${{ secrets.SERVER_IP }} + PROJECT_PATH: ${{ secrets.PROJECT_PATH_PRODUCTION }} + GIT_REPO: ${{ secrets.GIT_REPO }} + PM2_PROCESS: ${{ secrets.PM2_PROCESS_PRODUCTION }} + run: | + ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@$SERVER_IP " + cd $PROJECT_PATH && + git checkout main && + git pull origin main && + npm install && + pm2 reload ecosystem-production.config.js --only $PM2_PROCESS + " + + deploy-staging: + if: github.ref == 'refs/heads/staging' + runs-on: ubuntu-22.04 + steps: + - name: Setup SSH + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + SERVER_IP: ${{ secrets.SERVER_IP }} + run: | + mkdir -p ~/.ssh + echo "$DEPLOY_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan -H $SERVER_IP >> ~/.ssh/known_hosts + ssh-keyscan -H github.com >> ~/.ssh/known_hosts + + - name: Deploy to Staging + env: + SERVER_IP: ${{ secrets.SERVER_IP }} + PROJECT_PATH: ${{ secrets.PROJECT_PATH_STAGING }} + GIT_REPO: ${{ secrets.GIT_REPO }} + PM2_PROCESS: ${{ secrets.PM2_PROCESS_STAGING }} + run: | + ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no ubuntu@$SERVER_IP " + cd $PROJECT_PATH && + git checkout staging && + git pull origin staging && + npm install && + pm2 reload ecosystem-staging.config.js --only $PM2_PROCESS + " diff --git a/.github/workflows/on-pr.yaml b/.github/workflows/on-pr.yaml new file mode 100644 index 00000000..8f4801c2 --- /dev/null +++ b/.github/workflows/on-pr.yaml @@ -0,0 +1,45 @@ +name: Lint on Pull Request + +on: + pull_request: + branches: + - main + - staging + - develop + - MBI-1 + +jobs: + lint: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.14' + + - name: Install dependencies + run: npm install + + - name: Run Linter + run: npm run lint + pruebas-automaticas: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22.14' + + - name: Install dependencies + run: npm install + + - name: Run Jest + run: npm run test diff --git a/.gitignore b/.gitignore index 814d1d97..9d2b5800 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* - +.idea # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/aws.xml b/.idea/aws.xml new file mode 100644 index 00000000..03f1bb6e --- /dev/null +++ b/.idea/aws.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..6ea3fa32 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,60 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 00000000..79ee123c --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 00000000..913be6d2 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://altertex.cqns2oecgotm.us-east-1.rds.amazonaws.com:3306/Altertex + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 00000000..47877842 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000..26a57286 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/.idea/material_theme_project_new.xml b/.idea/material_theme_project_new.xml new file mode 100644 index 00000000..94763096 --- /dev/null +++ b/.idea/material_theme_project_new.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/prettier.xml b/.idea/prettier.xml new file mode 100644 index 00000000..60cfc994 --- /dev/null +++ b/.idea/prettier.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/webResources.xml b/.idea/webResources.xml new file mode 100644 index 00000000..2d5e98e8 --- /dev/null +++ b/.idea/webResources.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..d45243ca --- /dev/null +++ b/.prettierrc @@ -0,0 +1,16 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "quoteProps": "as-needed", + "jsxSingleQuote": false, + "trailingComma": "es5", + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always", + "proseWrap": "preserve", + "endOfLine": "lf", + "operatorPosition": "before" +} diff --git a/Autenticacion/Controladores/activar2FA.controller.js b/Autenticacion/Controladores/activar2FA.controller.js new file mode 100644 index 00000000..76a67426 --- /dev/null +++ b/Autenticacion/Controladores/activar2FA.controller.js @@ -0,0 +1,48 @@ +const speakeasy = require('speakeasy'); +const qrcode = require('qrcode'); +const db = require('@altertex/util/bd/db'); + +/** + * Controlador que genera el secreto TOTP y QR para activación de Google Authenticator. + * Guarda el secreto en la tabla usuarios_2fa. + * + * @async + * @param {object} req - La solicitud HTTP. + * @param {object} res - La respuesta HTTP. + * @returns {Promise} - Respuesta HTTP con el QR y mensaje de éxito. + * @throws {Error} - Si ocurre un error durante la activación. + * @throws {Error} - Si ocurre un error durante la activación. + * + */ +const activar2FA = async (req, res) => { + try { + const { idUsuario, nombre, correo } = req.body; + + // 1. Generar secreto con nombre legible + const secret = speakeasy.generateSecret({ + name: `Altertex (${nombre} - ${correo})`, + }); + + // 2. Convertir el secret en código QR (para Google Authenticator) + const otpauthURL = secret.otpauth_url; + const qrCodeBase64 = await qrcode.toDataURL(otpauthURL); + + // 3. Insertar o actualizar en tabla usuarios_2fa + await db.query(` + INSERT INTO usuarios_2fa (idUsuario, tiene2FA, secret2FA, puedeEliminarSuperadmins) + VALUES (?, ?, ?, ?) + ON DUPLICATE KEY UPDATE tiene2FA = VALUES(tiene2FA), secret2FA = VALUES(secret2FA) + `, [idUsuario, true, secret.base32, true]); + + // 4. Devolver imagen del QR al frontend + res.status(200).json({ + mensaje: 'Se generó el QR de activación.', + qrCode: qrCodeBase64, + }); + } catch (error) { + console.error('Error al activar 2FA:', error); + res.status(500).json({ mensaje: 'Error al activar autenticación 2FA.' }); + } +}; + +module.exports = { activar2FA}; \ No newline at end of file diff --git a/Autenticacion/Controladores/cerrarSesion.controller.js b/Autenticacion/Controladores/cerrarSesion.controller.js new file mode 100644 index 00000000..93cf3874 --- /dev/null +++ b/Autenticacion/Controladores/cerrarSesion.controller.js @@ -0,0 +1,46 @@ +const MENSAJES_AUTENTICACION = require('@altertex/util/const/mensajesAutenticacion'); + +/** + * Controlador para el cierre de sesión de un usuario. + * + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + * + * @async + * @function cerrarSesion + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.cookies - Cookies enviadas con la solicitud. + * @param {string} req.cookies.token - Token JWT almacenado en las cookies. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si el cierre de sesión es exitoso. + * - 400 si no existe una sesión activa (no hay token). + * - 500 si ocurre un error en el servidor al intentar cerrar la sesión. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.cerrarSesion = async (req, res) => { + try { + const token = req.cookies.token; + + if (!token) { + return res + .status(MENSAJES_AUTENTICACION.SESION_NO_EXISTENTE.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.SESION_NO_EXISTENTE.mensaje }); + } + + res.clearCookie('token', { + httpOnly: true, + secure: true, + sameSite: 'None', + }); + + return res + .status(MENSAJES_AUTENTICACION.CIERRE_SESION_EXITOSO.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.CIERRE_SESION_EXITOSO.mensaje }); + } catch { + return res + .status(MENSAJES_AUTENTICACION.ERROR_CIERRE_SESION.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.ERROR_CIERRE_SESION.mensaje }); + } +}; diff --git a/Autenticacion/Controladores/inicioSesion.controller.js b/Autenticacion/Controladores/inicioSesion.controller.js new file mode 100644 index 00000000..6476f6b6 --- /dev/null +++ b/Autenticacion/Controladores/inicioSesion.controller.js @@ -0,0 +1,102 @@ +const repositorio = require('@altertex/aut/repos/repositorioInicioSesion'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); + +const MENSAJES_AUTENTICACION = require('@altertex/util/const/mensajesAutenticacion'); + +/** + * Controlador para el inicio de sesión de un usuario. + * + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + * + * @async + * @function inicioSesion + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {string} req.body.correo - Correo electrónico del usuario. + * @param {string} req.body.contrasenia - Contraseña proporcionada por el usuario. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si el inicio de sesión es exitoso, junto con un JWT. + * - 400 si faltan campos requeridos o el formato del correo es inválido. + * - 401 si las credenciales son incorrectas. + * - 500 si ocurre un error en el servidor. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.inicioSesion = async (req, res) => { + const { correo, contrasenia } = req.body; + + const tiempoExpiracion = 8 * 60 * 60 * 1000; + + if (!correo || !contrasenia) { + return res + .status(MENSAJES_AUTENTICACION.CAMPOS_OBLIGATORIOS.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.CAMPOS_OBLIGATORIOS.mensaje }); + } + + const formatoCorreoValido = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(correo); + if (!formatoCorreoValido) { + return res.status(MENSAJES_AUTENTICACION.FORMATO_CORREO_INVALIDO.codigo).json({ + mensaje: MENSAJES_AUTENTICACION.FORMATO_CORREO_INVALIDO.mensaje, + }); + } + + try { + const resultadoQuery = await repositorio.obtenerUsuario(correo); + + const usuario = resultadoQuery.infoUsuario[0]; + const permisos = resultadoQuery.permisos; + const clientesAsociados = resultadoQuery.clientesAsociados; + + if (!usuario) { + return res.status(MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.codigo).json({ + mensaje: MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.mensaje, + }); + } + + const contraCorrecta = await bcrypt.compare(contrasenia, usuario.contrasenia); + + if (!contraCorrecta) { + return res.status(MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.codigo).json({ + mensaje: MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.mensaje, + }); + } + + const token = jwt.sign( + { + idUsuario: usuario.idUsuario, + correo: usuario.correoElectronico, + permisos, + clientesAsociados, + nombre: usuario.nombreCompleto, + }, + process.env.JWT_SECRET, + { + expiresIn: '8h', + }, + ); + + res.cookie('token', token, { + httpOnly: true, + secure: true, + sameSite: 'None', + }); + + res.cookie('nombreUsuario', usuario.nombreCompleto, { + httpOnly: false, + secure: true, + sameSite: 'None', + maxAge: tiempoExpiracion, + }); + + return res.status(MENSAJES_AUTENTICACION.INICIO_SESION_EXITOSO.codigo).json({ + mensaje: MENSAJES_AUTENTICACION.INICIO_SESION_EXITOSO.mensaje, + }); + } catch { + return res + .status(MENSAJES_AUTENTICACION.ERROR_SERVIDOR.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.ERROR_SERVIDOR.mensaje }); + } +}; diff --git a/Autenticacion/Controladores/verificar2FA.controller.js b/Autenticacion/Controladores/verificar2FA.controller.js new file mode 100644 index 00000000..b307996f --- /dev/null +++ b/Autenticacion/Controladores/verificar2FA.controller.js @@ -0,0 +1,62 @@ +// @file verificar2FA.controller.js +// @description Verifica el código 2FA de Google Authenticator para acciones críticas como eliminar superadmins. + +const speakeasy = require('speakeasy'); // Biblioteca para verificar códigos TOTP (2FA) +const db = require('@altertex/util/bd/db'); // Conexión a la base de datos + +/** + * Controlador que verifica el código 2FA de Google Authenticator. + * + * Se utiliza en operaciones críticas (ej. eliminación de Superadmins), + * validando el token TOTP generado desde una app de autenticación (como Google Authenticator). + * + * @async + * @function verificar2FA + * @param {object} req - Objeto de solicitud HTTP. + * @param {number} req.body.idUsuario - ID del usuario que solicita la operación. + * @param {string} req.body.codigo - Código TOTP ingresado por el usuario. + * @param {object} res - Objeto de respuesta HTTP. + * @returns {Promise} - Envía una respuesta JSON con el resultado de la verificación. + */ +const verificar2FA = async (req, res) => { + try { + const { idUsuario, codigo } = req.body; + + // Validación básica de parámetros + if (!idUsuario || !codigo) { + return res.status(400).json({ mensaje: 'Faltan campos requeridos.' }); + } + + // Consultar el secreto almacenado para el usuario con 2FA activado + const [resultado] = await db.query( + 'SELECT secret2FA FROM usuarios_2fa WHERE idUsuario = ? AND tiene2FA = true', + [idUsuario] + ); + + // Verificación de existencia de secreto + if (!resultado || resultado.length === 0) { + return res.status(403).json({ mensaje: 'No tienes activado 2FA o no estás autorizado.' }); + } + + const { secret2FA } = resultado[0]; + + // Verificación del código TOTP usando el secreto y un margen de 30s + const esValido = speakeasy.totp.verify({ + secret: secret2FA, + encoding: 'base32', + token: codigo, + window: 1, + }); + + if (!esValido) { + return res.status(401).json({ mensaje: 'Código inválido o expirado.' }); + } + + return res.status(200).json({ mensaje: 'Código verificado correctamente.' }); + } catch (error) { + console.error('Error al verificar 2FA:', error); + return res.status(500).json({ mensaje: 'Error al verificar autenticación 2FA.' }); + } +}; + +module.exports = { verificar2FA }; \ No newline at end of file diff --git a/Autenticacion/Datos/Repositorios/repositorioInicioSesion.js b/Autenticacion/Datos/Repositorios/repositorioInicioSesion.js new file mode 100644 index 00000000..73feab23 --- /dev/null +++ b/Autenticacion/Datos/Repositorios/repositorioInicioSesion.js @@ -0,0 +1,43 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios'); + +/** + * Obtiene la información y los permisos de un usuario a partir de su correo electrónico. + * + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + * + * @async + * @function obtenerUsuario + * @param {string} correoElectronico - Correo electrónico del usuario a buscar. + * + * @returns {Promise} Objeto con la siguiente estructura si se encuentra el usuario: + * - { infoUsuario: Array, permisos: Array } + * - Retorna un string con un mensaje de error si ocurre un fallo durante la operación. + * + * @throws {Error} Si no se encuentra el usuario o ocurre un error en la consulta. + */ +exports.obtenerUsuario = async (correoElectronico) => { + const queryUsuarios = CONSULTAS_USUARIOS.OBTENER_USUARIO; + const queryPermisos = CONSULTAS_USUARIOS.OBTENER_PERMISOS; + const queryClientesAsociados = CONSULTAS_USUARIOS.OBTENER_CLIENTES_ASOCIADOS; + + try { + const usuario = await correrQuery(queryUsuarios, [correoElectronico]); + const resultadoPermisos = await correrQuery(queryPermisos, [correoElectronico]); + const resultadoClientesAsociados = await correrQuery(queryClientesAsociados, [ + correoElectronico, + ]); + + const resultado = { + infoUsuario: usuario, + permisos: resultadoPermisos.map((objetosPermisos) => objetosPermisos.nombre), + clientesAsociados: resultadoClientesAsociados.map( + (objetosClientes) => objetosClientes.idCliente + ), + }; + + return resultado; + } catch { + return `Error obteniendo usuario`; + } +}; diff --git a/Autenticacion/Rutas/RutasIndividuales/activar2FA.routes.js b/Autenticacion/Rutas/RutasIndividuales/activar2FA.routes.js new file mode 100644 index 00000000..c074cd09 --- /dev/null +++ b/Autenticacion/Rutas/RutasIndividuales/activar2FA.routes.js @@ -0,0 +1,111 @@ +const express = require('express'); +const ruteador = express.Router(); + +const controlador = require('@altertex/aut/ctrl/activar2FA.controller'); + +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF## - Activar 2FA para Superadmin + */ + +/** + * @swagger + * /api/seguridad/superadmin/activar-2fa: + * post: + * summary: Activa autenticación 2FA para un Superadmin autorizado. + * tags: [Seguridad] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idUsuario + * - nombre + * - correo + * properties: + * idUsuario: + * type: integer + * example: 1 + * nombre: + * type: string + * example: Maria González + * correo: + * type: string + * format: email + * example: maria.gonzalez@example.com + * responses: + * 200: + * description: 2FA activado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Se generó el QR de activación. + * qrCode: + * type: string + * format: byte + * example: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA... + * 400: + * description: Solicitud incorrecta. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al activar autenticación 2FA. + * 401: + * description: Token no autorizado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Token no autorizado. + * 403: + * description: Usuario no autorizado para activar 2FA. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Usuario no autorizado para activar 2FA. + * 500: + * description: Error al generar el secreto. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al generar el secreto. + */ + +ruteador.post( + RUTAS.AUTENTICACION.ACTIVAR_2FA, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.ACTIVAR_2FA_SUPERADMIN), + controlador.activar2FA +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Autenticacion/Rutas/RutasIndividuales/autenticacionSesion.routes.js b/Autenticacion/Rutas/RutasIndividuales/autenticacionSesion.routes.js new file mode 100644 index 00000000..83982e49 --- /dev/null +++ b/Autenticacion/Rutas/RutasIndividuales/autenticacionSesion.routes.js @@ -0,0 +1,63 @@ +const express = require('express'); +const ruteador = express.Router(); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); + +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/autenticacion/autenticar: + * get: + * summary: Obtiene los datos del usuario autenticado mediante un token JWT. + * tags: + * - Autenticación + * security: + * - ApiKeyAuth: [] # Protegido por API key + * responses: + * 200: + * description: Usuario autenticado correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * user: + * type: object + * example: + * correo: usuario@correo.com + * permisos: + * - ADMIN + * - EDITOR + * 401: + * description: Token no proporcionado, expirado o inválido. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Token no proporcionado o inválido. + * 500: + * description: Error al validar el token. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error interno al validar el token. + */ + +ruteador.get( + RUTAS.AUTENTICACION.USUARIO_AUTENTICADO, + revisarApiKey(), + autorizarToken, + (req, res) => { + res.json({ user: req.user }); + } +); + +module.exports = ruteador; diff --git a/Autenticacion/Rutas/RutasIndividuales/cerrarSesion.routes.js b/Autenticacion/Rutas/RutasIndividuales/cerrarSesion.routes.js new file mode 100644 index 00000000..7e49736d --- /dev/null +++ b/Autenticacion/Rutas/RutasIndividuales/cerrarSesion.routes.js @@ -0,0 +1,56 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/aut/ctrl/cerrarSesion.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); + +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + */ + +/** + * @swagger + * /api/autenticacion/cerrar-sesion: + * post: + * summary: Cierra la sesión del usuario actual eliminando la cookie con el token. + * tags: + * - Autenticación + * security: + * - ApiKeyAuth: [] # Si usas revisión de API key + * responses: + * 200: + * description: Cierre de sesión exitoso. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Cierre de sesión exitoso. + * 400: + * description: No existe una sesión activa (no se encontró token). + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No hay sesión activa. + * 500: + * description: Error al intentar cerrar sesión. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error en el servidor al cerrar sesión. + */ + +ruteador.post(RUTAS.AUTENTICACION.CERRAR_SESION, revisarApiKey(), controlador.cerrarSesion); + +module.exports = ruteador; diff --git a/Autenticacion/Rutas/RutasIndividuales/inicioSesion.routes.js b/Autenticacion/Rutas/RutasIndividuales/inicioSesion.routes.js new file mode 100644 index 00000000..e72e6541 --- /dev/null +++ b/Autenticacion/Rutas/RutasIndividuales/inicioSesion.routes.js @@ -0,0 +1,89 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/aut/ctrl/inicioSesion.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); + + +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + */ + +/** + * @swagger + * /api/autenticacion/iniciar-sesion: + * post: + * summary: Inicia sesión con correo y contraseña. + * tags: + * - Autenticación + * security: + * - ApiKeyAuth: [] # Si estás usando autenticación por API key + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - correo + * - contrasenia + * properties: + * correo: + * type: string + * format: email + * example: maria.gonzalez@example.com + * contrasenia: + * type: string + * example: hola + * responses: + * 200: + * description: Inicio de sesión exitoso. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Inicio de sesión exitoso. + * 400: + * description: Faltan campos requeridos o formato inválido. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: El correo y la contraseña son obligatorios. + * 401: + * description: Credenciales inválidas. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Credenciales incorrectas. + * 500: + * description: Error interno del servidor. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error en el servidor. + */ +ruteador.post( + RUTAS.AUTENTICACION.INICIO_SESION, + validarYSanitizar, + revisarApiKey(), + controlador.inicioSesion, +); + +module.exports = ruteador; diff --git a/Autenticacion/Rutas/RutasIndividuales/verificar2FA.routes.js b/Autenticacion/Rutas/RutasIndividuales/verificar2FA.routes.js new file mode 100644 index 00000000..c267e191 --- /dev/null +++ b/Autenticacion/Rutas/RutasIndividuales/verificar2FA.routes.js @@ -0,0 +1,102 @@ +const express = require('express'); +const ruteador = express.Router(); + +const controlador = require('@altertex/aut/ctrl/verificar2FA.controller'); + +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF## - Verificar 2FA para Superadmin + */ + +/** + * @swagger + * /api/seguridad/superadmin/verificar-2fa: + * post: + * summary: Verifica el código 2FA proporcionado por el Superadmin. + * tags: [Seguridad] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idUsuario + * - codigo + * properties: + * idUsuario: + * type: integer + * example: 1 + * codigo: + * type: string + * example: 123456 + * responses: + * 200: + * description: Código 2FA verificado correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Autenticación 2FA exitosa. + * 400: + * description: Código inválido o ya expirado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Código 2FA inválido. + * 401: + * description: Token no autorizado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Token no autorizado. + * 403: + * description: Usuario no autorizado para verificar 2FA. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Usuario no autorizado para verificar 2FA. + * 500: + * description: Error al verificar el código 2FA. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error interno al verificar el código. + */ + +ruteador.post( + RUTAS.AUTENTICACION.VERIFICAR_2FA, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.VERIFICAR_2FA_SUPERADMIN), + controlador.verificar2FA +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Autenticacion/Rutas/indexAutenticacion.routes.js b/Autenticacion/Rutas/indexAutenticacion.routes.js new file mode 100644 index 00000000..69bd11ad --- /dev/null +++ b/Autenticacion/Rutas/indexAutenticacion.routes.js @@ -0,0 +1,19 @@ +const express = require("express"); +const ruteador = express.Router(); +const rutasAutenticacionSesion = require("@altertex/aut/rutasInd/autenticacionSesion.routes"); +const rutasInicioSesion = require("@altertex/aut/rutasInd/inicioSesion.routes"); +const rutasCerrarSesion = require("@altertex/aut/rutasInd/cerrarSesion.routes"); +const rutasVerificar2FA = require('@altertex/aut/rutasInd/verificar2FA.routes'); +const rutasActivar2FA = require('@altertex/aut/rutasInd/activar2FA.routes'); + +const RUTAS = require("@altertex/util/const/rutas"); + +ruteador.use(RUTAS.AUTENTICACION.BASE, rutasAutenticacionSesion); + +//RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 +ruteador.use(RUTAS.AUTENTICACION.BASE, rutasInicioSesion); +ruteador.use(RUTAS.AUTENTICACION.BASE, rutasCerrarSesion); +ruteador.use(RUTAS.AUTENTICACION.BASE, rutasVerificar2FA); +ruteador.use(RUTAS.AUTENTICACION.BASE, rutasActivar2FA); + +module.exports = ruteador; diff --git a/CRON_JOBS/Controladores/actualizarCuotaSet.controller.js b/CRON_JOBS/Controladores/actualizarCuotaSet.controller.js new file mode 100644 index 00000000..ae9b1830 --- /dev/null +++ b/CRON_JOBS/Controladores/actualizarCuotaSet.controller.js @@ -0,0 +1,32 @@ +/** + * + * RF31 - Crear Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF31 + * + * @file Cron job que se ejecuta cada 5 minutos para obtener información de los cuota sets. + * + * @module cron/actualizarCuotaSets + * + * @requires node-cron + * @requires @altertex/CRON/repos/actualizarCuotaSetsRepositorio + * + * @description + * Este cron job se ejecuta automáticamente cada 5 minutos. + * Llama al repositorio `obtenerCuota` para realizar operaciones sobre los cuota sets. + * Si ocurre un error durante la ejecución, se captura y se muestra en consola. + */ + +const cron = require('node-cron'); +const repositorio = require('@altertex/CRON/repos/actualizarCuotaSetsRepositorio'); + +/** + * Tarea programada que se ejecuta a las 00:00. + * Ejecuta `repositorio.obtenerCuota` para actualizar información relacionada con los cuota sets. + */ +module.exports = cron.schedule('0 0 * * *', async () => { + try { + const resultado = await repositorio.obtenerCuota(); + console.log('Resultado del cron:', resultado); + } catch (error) { + console.error('Error en el cron:', error); + } +}); diff --git a/CRON_JOBS/Datos/Repositorios/actualizarCuotaSetsRepositorio.js b/CRON_JOBS/Datos/Repositorios/actualizarCuotaSetsRepositorio.js new file mode 100644 index 00000000..0a3fee35 --- /dev/null +++ b/CRON_JOBS/Datos/Repositorios/actualizarCuotaSetsRepositorio.js @@ -0,0 +1,56 @@ +/** + * + * RF31 - Crear Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF31 + * + * @file Función encargada de actualizar los límites y fechas de los cuota sets. + * + * @module repositorio/obtenerCuota + * + * @requires @altertex/util/bd/db + * @requires @altertex/util/const/consultasCuotas + */ + +const db = require('@altertex/util/bd/db'); +const QUERY = require('@altertex/util/const/consultasCuotas'); + +/** + * Actualiza los límites de productos de los cuota sets y sus fechas de última actualización. + * + * - Primero intenta resetear los límites (`QUERY.RESETEAR_LIMITES`). + * - Si se realiza al menos una modificación, actualiza las fechas (`QUERY.ACTUALIZAR_FECHAS`). + * - Si no se modifica ninguna fila, la transacción es revertida. + * + * @async + * @function + * @returns {Promise} Resultado de la operación. Puede ser un mensaje de éxito o de error. + * + * @throws {Error} Si ocurre un fallo en la transacción de base de datos. + */ +exports.obtenerCuota = async () => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const [resultadoReseteo] = await conexion.execute(QUERY.RESETEAR_LIMITES); + + if (resultadoReseteo.changedRows === 0) { + await conexion.rollback(); + return { + error: 'Ninguna columna se actualizo. No se actualizara la fecha.', + }; + } + + await conexion.execute(QUERY.ACTUALIZAR_FECHAS); + + await conexion.commit(); + + return { exito: 'Actualizacion exitosa' }; + } catch (error) { + await conexion.rollback(); + console.error('Transacción fallida: ', error); + throw new Error('Error actualizando cuota sets'); + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Categorias/Controladores/actualizarCategoria.controller.js b/Categorias/Controladores/actualizarCategoria.controller.js new file mode 100644 index 00000000..8138e24d --- /dev/null +++ b/Categorias/Controladores/actualizarCategoria.controller.js @@ -0,0 +1,44 @@ +const { actualizarCategoria } = require('@altertex/cat/repos/repositorioActualizarCategorias'); +const MENSAJES = require('@altertex/util/const/mensajesCategorias'); + +/** + * RF49 - Actualizar categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF49 + * + * @param {express.Request} req + * @param {express.Response} res + * @returns {Promise} + */ +exports.actualizarCategoria = async (req, res) => { + try { + const { idCategoria } = req.params; + const { nombreCategoria, descripcion, productos } = req.body; + + if (!idCategoria) { + return res.status(400).json(MENSAJES.CATEGORIA_NO_ENCONTRADA); + } + + if (!nombreCategoria || typeof nombreCategoria !== 'string' || nombreCategoria.trim() === '') { + return res.status(400).json(MENSAJES.NOMBRE_CATEGORIA_INVALIDO); + } + + if (!Array.isArray(productos)) { + return res.status(400).json({ + codigo: 400, + mensaje: 'El campo productos debe ser un arreglo.', + }); + } + + if (descripcion && typeof descripcion !== 'string') { + return res.status(400).json(MENSAJES.DESCRIPCION_INVALIDA); + } + + await actualizarCategoria({ idCategoria, nombreCategoria, descripcion, productos }); + + return res.status(200).json({ + codigo: 200, + mensaje: 'Categoría actualizada correctamente.', + }); + } catch { + return res.status(500).json(MENSAJES.ERROR_CREAR_CATEGORIA); + } +}; \ No newline at end of file diff --git a/Categorias/Controladores/consultarDetalleCategoria.controller.js b/Categorias/Controladores/consultarDetalleCategoria.controller.js new file mode 100644 index 00000000..1bcd4163 --- /dev/null +++ b/Categorias/Controladores/consultarDetalleCategoria.controller.js @@ -0,0 +1,41 @@ +const repositorio = require('@altertex/cat/repos/repositorioLeerDetalleCategoria'); +const MENSAJES_CATEGORIAS = require('@altertex/util/const/mensajesCategorias'); + +/** + * Consulta el detalle de una categoría de productos, incluyendo sus productos asociados. + * + * @function + * @async + * @param {Express.Request} req - Objeto de solicitud HTTP con `req.params.idCategoria`. + * @param {Express.Response} res - Objeto de respuesta HTTP para enviar el resultado. + * + * @returns {Promise} Devuelve una respuesta HTTP con el detalle de la categoría o un mensaje de error. + * + * @description + * Implementa el RF48: Leer categoría de productos. + * Si no se encuentra la categoría, devuelve código 404. + * Si ocurre un error inesperado, devuelve código 500. + * + * @see [RF48 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF48) + */ +exports.consultarDetalleCategoria = async (req, res) => { + const idCategoria = parseInt(req.params.idCategoria); + + try { + const resultado = await repositorio.leerDetalleCategoria(idCategoria); + + if (!resultado) { + return res + .status(MENSAJES_CATEGORIAS.CATEGORIA_NO_ENCONTRADA.codigo) + .json({ mensaje: MENSAJES_CATEGORIAS.CATEGORIA_NO_ENCONTRADA.mensaje }); + } + + return res + .status(MENSAJES_CATEGORIAS.CATEGORIA_OBTENIDA.codigo) + .json({ mensaje: MENSAJES_CATEGORIAS.CATEGORIA_OBTENIDA.mensaje, categoria: resultado }); + } catch { + return res + .status(MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIA.codigo) + .json({ mensaje: MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIA.mensaje }); + } +}; \ No newline at end of file diff --git a/Categorias/Controladores/consultarListaCategorias.controller.js b/Categorias/Controladores/consultarListaCategorias.controller.js new file mode 100644 index 00000000..5a5b00d7 --- /dev/null +++ b/Categorias/Controladores/consultarListaCategorias.controller.js @@ -0,0 +1,37 @@ +const repositorio = require('@altertex/cat/repos/repositorioConsultarListaCategorias'); +const MENSAJES_CATEGORIAS = require('@altertex/util/const/mensajesCategorias'); + +/** + * Consulta la lista de categorías asociadas a un cliente. + * + * @function + * @async + * @param {Express.Request} req - Objeto de solicitud HTTP. Debe contener `req.user.clienteSeleccionado` como ID del cliente autenticado. + * @param {Express.Response} res - Objeto de respuesta HTTP para enviar los resultados. + * + * @returns {Promise} Devuelve una respuesta HTTP con la lista de categorías o un mensaje de error. + * + * @see [RF47 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF47) + */ +exports.consultarListaCategorias = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + + try { + const resultados = await repositorio.consultarListaCategorias(idCliente); + + if (!resultados || resultados.length === 0) { + return res + .status(MENSAJES_CATEGORIAS.CATEGORIAS_NO_ENCONTRADAS.codigo) + .json({ mensaje: MENSAJES_CATEGORIAS.CATEGORIAS_NO_ENCONTRADAS.mensaje }); + } + + return res.status(MENSAJES_CATEGORIAS.LISTA_CATEGORIAS_OBTENIDA.codigo).json({ + mensaje: MENSAJES_CATEGORIAS.LISTA_CATEGORIAS_OBTENIDA.mensaje, + listaCategoria: resultados, + }); + } catch { + return res + .status(MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIAS.codigo) + .json({ mensaje: MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIAS.mensaje }); + } +}; \ No newline at end of file diff --git a/Categorias/Controladores/crearCategoria.controller.js b/Categorias/Controladores/crearCategoria.controller.js new file mode 100644 index 00000000..3fef1073 --- /dev/null +++ b/Categorias/Controladores/crearCategoria.controller.js @@ -0,0 +1,40 @@ +// RF[46] Crear categoria - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF46] + +const MENSAJES = require('@altertex/util/const/mensajesCategorias'); +const repositorio = require('@altertex/cat/repos/repositorioCrearCategoria'); + +/** + * Crea una nueva categoría y la guarda en la base de datos. + * + * @function + * @async + * @param {Express.Request} req - Objeto de solicitud HTTP, debe contener `nombreCategoria` y `productos` en `req.body`. + * @param {Express.Response} res - Objeto de respuesta HTTP para enviar el resultado de la operación. + * + * @returns {Promise} Envía una respuesta HTTP con el resultado de la operación. + * + * @description + * Este endpoint implementa el RF[46] para la creación de una categoría con productos asociados. + * Valida que los datos requeridos estén presentes y maneja errores de forma controlada. + */ +exports.crearCategoria = async (req, res) => { + const categoria = req.body.categoria; + + if (!categoria.nombreCategoria || !categoria.productos) { + return res + .status(MENSAJES.NOMBRE_CATEGORIA_INVALIDO.codigo) + .json({ mensaje: MENSAJES.NOMBRE_CATEGORIA_INVALIDO.mensaje }); + } + + try { + await repositorio.crearCategoria(categoria); + + return res + .status(MENSAJES.CATEGORIA_CREADA.codigo) + .json({ exito: MENSAJES.CATEGORIA_CREADA.mensaje }); + } catch (errorRepo) { + return res + .status(MENSAJES.ERROR_CREAR_CATEGORIA.codigo) + .json({ mensaje: errorRepo.message }); + } +}; diff --git a/Categorias/Controladores/eliminarCategoria.controller.js b/Categorias/Controladores/eliminarCategoria.controller.js new file mode 100644 index 00000000..acfccdeb --- /dev/null +++ b/Categorias/Controladores/eliminarCategoria.controller.js @@ -0,0 +1,49 @@ +const repositorio = require('@altertex/cat/repos/repositorioEliminarCategoria'); +const MENSAJES_CATEGORIAS = require('@altertex/util/const/mensajesCategorias'); + +/** + * Controlador para eliminar una categoría de productos. + * RF[50] - Elimina categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF50 + * + * @async + * @function eliminarCategoria + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {number[]} req.body.idsCategoria - Array de IDs numéricos de las categorías a eliminar. + * @param {object} res - Objeto de respuesta de Express. + * @returns {Promise} Respuesta HTTP con estado: + * - 200 si las categorías fueron eliminadas correctamente. + * - 404 si no se encontraron las categorías. + * - 500 si ocurre un error en el servidor. + * @throws {Error} Si ocurre un error durante la eliminación. + */ +exports.eliminarCategoria = async (req, res) => { + try { + const idsCategorias = req.body.idsCategoria; + + if (!Array.isArray(idsCategorias) || idsCategorias.length === 0) { + return res.status(MENSAJES_CATEGORIAS.CATEGORIA_NO_ENCONTRADA.codigo).json({ + mensaje: MENSAJES_CATEGORIAS.CATEGORIA_NO_ENCONTRADA.mensaje, + }); + } + + await Promise.all( + idsCategorias.map(async (idCategoria) => { + await repositorio.eliminarProductoCategoria(idCategoria); + const resultadoCategoria = await repositorio.eliminarCategoria(idCategoria); + + if (resultadoCategoria.affectedRows === 0) { + throw new Error(`Categoría con ID ${idCategoria} no encontrada`); + } + }) + ); + + return res.status(MENSAJES_CATEGORIAS.CATEGORIA_ELIMINADA.codigo).json({ + mensaje: MENSAJES_CATEGORIAS.CATEGORIA_ELIMINADA.mensaje, + }); + } catch { + return res.status(MENSAJES_CATEGORIAS.ERROR_ELIMINAR_CATEGORIA.codigo).json({ + mensaje: MENSAJES_CATEGORIAS.ERROR_ELIMINAR_CATEGORIA.mensaje, + }); + } +}; diff --git a/Categorias/Datos/Repositorios/repositorioActualizarCategorias.js b/Categorias/Datos/Repositorios/repositorioActualizarCategorias.js new file mode 100644 index 00000000..b3c72b05 --- /dev/null +++ b/Categorias/Datos/Repositorios/repositorioActualizarCategorias.js @@ -0,0 +1,22 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS = require('@altertex/util/const/consultasCategorias'); + +/** + * Actualiza el nombre, descripción y productos de una categoría. + * + * @param {object} categoria - Objeto con los datos de la categoría. + * @param {number} categoria.idCategoria - ID de la categoría a actualizar. + * @param {string} categoria.nombreCategoria - Nuevo nombre de la categoría. + * @param {string} categoria.descripcion - Nueva descripción. + * @param {number[]} categoria.productos - IDs de productos asociados. + * @returns {Promise} + */ +exports.actualizarCategoria = async ({ idCategoria, nombreCategoria, descripcion, productos }) => { + await correrQuery(CONSULTAS.ACTUALIZAR_CATEGORIA, [nombreCategoria, descripcion, idCategoria]); + await correrQuery(CONSULTAS.ELIMINAR_PRODUCTOS_CATEGORIA, [idCategoria]); + + if (productos && productos.length > 0) { + const valores = productos.map((idProd) => [idCategoria, idProd]); + await correrQuery(CONSULTAS.ASIGNAR_PRODUCTOS_A_CATEGORIA, [valores]); + } +}; \ No newline at end of file diff --git a/Categorias/Datos/Repositorios/repositorioConsultarListaCategorias.js b/Categorias/Datos/Repositorios/repositorioConsultarListaCategorias.js new file mode 100644 index 00000000..27d5a8df --- /dev/null +++ b/Categorias/Datos/Repositorios/repositorioConsultarListaCategorias.js @@ -0,0 +1,19 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CATEGORIAS = require('@altertex/util/const/consultasCategorias'); + +/** + * Consulta la lista de categorías y sus productos asociadas a un cliente específico. + * + * @function + * @async + * @param {number} idCliente - ID del cliente cuyas categorías se desean consultar. + * @returns {Promise>} Arreglo con las categorías encontradas, cada una con sus productos asociados. + * + * @throws {Error} Lanza un error si ocurre un fallo al ejecutar la consulta a la base de datos. + * + * @see [RF47 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF47) + */ +exports.consultarListaCategorias = (idCliente) => { + const query = CONSULTAS_CATEGORIAS.OBTENER_CATEGORIAS_CON_PRODUCTOS; + return correrQuery(query, [idCliente]); +}; \ No newline at end of file diff --git a/Categorias/Datos/Repositorios/repositorioCrearCategoria.js b/Categorias/Datos/Repositorios/repositorioCrearCategoria.js new file mode 100644 index 00000000..46ec5fec --- /dev/null +++ b/Categorias/Datos/Repositorios/repositorioCrearCategoria.js @@ -0,0 +1,95 @@ +const CONSULTA = require('@altertex/util/const/consultasCategorias'); +const db = require('@altertex/util/bd/db'); +const MENSAJES = require('@altertex/util/const/mensajesCategorias'); + + +/** + * Crea una nueva categoría y la asocia con productos válidos en la base de datos. + * + * Este método valida que los parámetros sean correctos, verifica que el nombre + * de la categoría no esté duplicado, comprueba que los productos existan en la base de datos, + * y finalmente inserta la nueva categoría y sus asociaciones en la tabla correspondiente. + * + * @async + * @function + * @param {object} categoria - Objeto con los datos de la categoría a crear. + * @param {string} categoria.nombreCategoria - Nombre de la categoría (obligatorio). + * @param {string} [categoria.descripcion] - Descripción opcional de la categoría. + * @param {Array} categoria.productos - Lista de productos a asociar. + * @param {number} categoria.productos[].idProducto - ID del producto a asociar (obligatorio). + * + * @returns {Promise} El ID de la nueva categoría creada. + * + * @throws {Error} Si faltan parámetros, si el nombre es inválido o ya existe, + * o si uno o más productos no existen en la base de datos. + * + * @example + * const nuevaCategoriaId = await crearCategoria({ + * nombreCategoria: 'Promociones', + * descripcion: 'Categoría para productos en descuento', + * productos: [{ idProducto: 1 }, { idProducto: 2 }] + * }); + */ +exports.crearCategoria = async (categoria) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + if (!categoria) { + throw new Error(MENSAJES.PARAMETROS_INVALIDOS.mensaje); + } + + const { nombreCategoria, descripcion, productos } = categoria; + + if (!nombreCategoria || typeof nombreCategoria !== 'string') { + throw new Error(MENSAJES.NOMBRE_CATEGORIA_INVALIDO.mensaje); + } + + if (!productos || !Array.isArray(productos) || productos.length === 0) { + throw new Error(MENSAJES.PARAMETROS_INVALIDOS.mensaje); + } + + const [categoriasExistentes] = await conexion.execute( + CONSULTA.CATEGORIA_EXISTENTE_POR_NOMBRE, + [nombreCategoria], + ); + + if (categoriasExistentes.length > 0) { + throw new Error(`Ya existe una categoría con ese nombre.`); + } + + const idsProductos = productos.map(producto => producto.idProducto); + const [productosValidos] = await conexion.query( + CONSULTA.PRODUCTOS_EXISTENTES_POR_IDS, + [idsProductos], + ); + + const idsValidos = productosValidos.map(producto => producto.idProducto); + const idsInvalidos = idsProductos.filter(id => !idsValidos.includes(id)); + + if (idsInvalidos.length > 0) { + throw new Error(`Productos inválidos: ${idsInvalidos.join(', ')}`); + } + + const [resultado] = await conexion.execute(CONSULTA.CREAR_CATEGORIAS, [ + nombreCategoria, + descripcion, + ]); + + const categoriaId = resultado.insertId; + + for (const item of productos) { + await conexion.execute(CONSULTA.CREAR_CATEGORIA_PRODUCTOS, [categoriaId, item.idProducto]); + } + + await conexion.commit(); + + return categoriaId; + } catch (error) { + if (conexion) await conexion.rollback(); + throw error; + } finally { + if (conexion) conexion.release(); + } +}; diff --git a/Categorias/Datos/Repositorios/repositorioEliminarCategoria.js b/Categorias/Datos/Repositorios/repositorioEliminarCategoria.js new file mode 100644 index 00000000..c41bb34a --- /dev/null +++ b/Categorias/Datos/Repositorios/repositorioEliminarCategoria.js @@ -0,0 +1,40 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CATEGORIAS = require('@altertex/util/const/consultasCategorias'); + +/** + * Elimina la relación entre productos y una categoría. + * + * @async + * @function eliminarProductoCategoria + * @param {number} idCategoria - ID de la categoría a desvincular de productos. + * @returns {Promise} Objeto de resultado de la operación MySQL (por ejemplo, `affectedRows`). + * @throws {Error} Si ocurre un error durante la ejecución del query para eliminar la relación. + */ +exports.eliminarProductoCategoria = async (idCategoria) => { + const query = CONSULTAS_CATEGORIAS.ELIMINAR_CATEGORIA_PRODUCTO; + try { + const resultado = await correrQuery(query, [idCategoria]); + return resultado; + } catch { + throw new Error('Error eliminando el producto enlazado a la categoria'); + } +}; + +/** + * Elimina una categoría de la base de datos. + * + * @async + * @function eliminarCategoria + * @param {number} idCategoria - ID de la categoría a eliminar. + * @returns {Promise<{affectedRows: number}>} Resultado de la operación con el número de filas afectadas. + * @throws {Error} Si ocurre un error durante la operación. + */ +exports.eliminarCategoria = async (idCategoria) => { + const query = CONSULTAS_CATEGORIAS.ELIMINAR_CATEGORIA; + try { + const resultado = await correrQuery(query, [idCategoria]); + return resultado; + } catch { + throw new Error('Error eliminando categorias.'); + } +}; diff --git a/Categorias/Datos/Repositorios/repositorioLeerDetalleCategoria.js b/Categorias/Datos/Repositorios/repositorioLeerDetalleCategoria.js new file mode 100644 index 00000000..63b0ca0e --- /dev/null +++ b/Categorias/Datos/Repositorios/repositorioLeerDetalleCategoria.js @@ -0,0 +1,35 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS = require('@altertex/util/const/consultasCategorias'); + +/** + * Consulta el detalle de una categoría y sus productos asociados. + * + * @param {number} idCategoria - ID de la categoría a consultar. + * @returns {Promise} Objeto con la información de la categoría y sus productos, o null si no existe. + * + * @throws {Error} Si ocurre un error al ejecutar la consulta. + * + * @see [RF48 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF48) + */ +exports.leerDetalleCategoria = async (idCategoria) => { + const query = CONSULTAS.LEER_DETALLE_CATEGORIA; + const resultados = await correrQuery(query, [idCategoria]); + + if (!resultados || resultados.length === 0) return null; + + const { nombreCategoria, descripcion } = resultados[0]; + + const productos = resultados + .filter(resul => resul.idProducto !== null) + .map(produc => ({ + idProducto: produc.idProducto, + nombreComun: produc.nombreComun, + })); + + return { + idCategoria, + nombreCategoria, + descripcion, + productos, + }; +}; \ No newline at end of file diff --git a/Categorias/Rutas/RutasIndividuales/actualizarCategorias.routes.js b/Categorias/Rutas/RutasIndividuales/actualizarCategorias.routes.js new file mode 100644 index 00000000..67956f7d --- /dev/null +++ b/Categorias/Rutas/RutasIndividuales/actualizarCategorias.routes.js @@ -0,0 +1,72 @@ +/** + * RF49 - Actualizar categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF49 + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cat/ctrl/actualizarCategoria.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); + +/** + * @swagger + * /api/categorias/actualizar-categoria/{idCategoria}: + * put: + * summary: Actualiza una categoría y su lista de productos. + * description: Requiere autenticación y permisos adecuados. Valida que no se incluyan entradas maliciosas. + * tags: + * - Categorías + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * parameters: + * - in: path + * name: idCategoria + * required: true + * schema: + * type: integer + * description: ID de la categoría a actualizar + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * categoria: + * type: object + * properties: + * nombreCategoria: + * type: string + * descripcion: + * type: string + * productos: + * type: array + * items: + * type: object + * properties: + * idProducto: + * type: integer + * responses: + * 200: + * description: Categoría actualizada correctamente. + * 400: + * description: Datos inválidos o entrada maliciosa detectada. + * 500: + * description: Error interno al actualizar la categoría. + */ +ruteador.put( + `${RUTAS.CATEGORIAS.ACTUALIZAR}/:idCategoria`, + validarYSanitizar, + + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.ACTUALIZAR_CATEGORIA_PRODUCTOS), + controlador.actualizarCategoria +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Categorias/Rutas/RutasIndividuales/consultarDetalleCategoria.routes.js b/Categorias/Rutas/RutasIndividuales/consultarDetalleCategoria.routes.js new file mode 100644 index 00000000..0b755eaf --- /dev/null +++ b/Categorias/Rutas/RutasIndividuales/consultarDetalleCategoria.routes.js @@ -0,0 +1,22 @@ +/** + * RF48 Leer categoría de productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF48 + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cat/ctrl/consultarDetalleCategoria.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.get( + `${RUTAS.CATEGORIAS.LEER}/:idCategoria`, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.LEER_CATEGORIA_PRODUCTOS), + controlador.consultarDetalleCategoria +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js b/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js new file mode 100644 index 00000000..e5b99c36 --- /dev/null +++ b/Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js @@ -0,0 +1,95 @@ +/** + * RF[47] Consulta lista de categorías - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF47 + */ + +/** + * @swagger + * /api/categorias/consultar-lista-categorias: + * post: + * summary: Consulta la lista de categorías de productos asociadas a un cliente. + * description: | + * Este endpoint permite consultar las categorías de productos disponibles para el + * cliente autenticado. El ID del cliente se obtiene automáticamente del token de autenticación. + * tags: [Categorías] + * security: + * - ApiKeyAuth: [] + * requestBody: + * description: No requiere body. El ID del cliente se obtiene del token de autenticación. + * content: + * application/json: + * schema: + * type: object + * properties: {} + * responses: + * 200: + * description: Consulta exitosa. Se devuelve la lista de categorías. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Lista de categorías obtenida exitosamente." + * listaCategoria: + * type: array + * items: + * type: object + * properties: + * idCategoria: + * type: integer + * example: 1 + * nombreCategoria: + * type: string + * example: "Calzado" + * descripcion: + * type: string + * example: "Zapatos, botas y accesorios." + * cantidadProductos: + * type: integer + * example: 5 + * idCliente: + * type: integer + * example: 123 + * 204: + * description: No se encontraron categorías registradas para el cliente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No se encontraron categorías registradas." + * 400: + * description: Error en el servidor al intentar obtener la lista de categorías. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Ocurrió un error al obtener la lista de categorías." + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cat/ctrl/consultarListaCategorias.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.post( + RUTAS.CATEGORIAS.CONSULTAR_LISTA_CATEGORIAS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_CATEGORIAS_PRODUCTOS), + controlador.consultarListaCategorias +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Categorias/Rutas/RutasIndividuales/crearCategoria.routes.js b/Categorias/Rutas/RutasIndividuales/crearCategoria.routes.js new file mode 100644 index 00000000..1fb0cd91 --- /dev/null +++ b/Categorias/Rutas/RutasIndividuales/crearCategoria.routes.js @@ -0,0 +1,94 @@ +// RF[46] Crear categoria - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF46] + +const express = require('express'); +const ruteador = express.Router(); +const RUTAS = require('@altertex/util/const/rutas'); +const controlador = require('@altertex/cat/ctrl/crearCategoria.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermiso = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +/** + * @swagger + * /api/categorias/crear-categoria: + * post: + * summary: Crear una nueva categoría con productos asociados. + * description: Crea una categoría en la base de datos y asocia una lista de productos a ella. Requiere autenticación y permisos específicos. + * tags: + * - Categorías + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * categoria: + * type: object + * required: + * - nombreCategoria + * - productos + * properties: + * nombreCategoria: + * type: string + * example: "Nuevas Camisas" + * descripcion: + * type: string + * example: "Colección de camisas de temporada" + * productos: + * type: array + * items: + * type: object + * properties: + * idProducto: + * type: integer + * example: 101 + * responses: + * 201: + * description: Categoría creada exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * exito: + * type: string + * example: Categoría creada correctamente. + * 400: + * description: Datos inválidos proporcionados. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: El nombre de la categoría o la lista de productos no son válidos. + * 500: + * description: Error interno al crear la categoría. + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Error al crear la categoría. + */ +ruteador.post( + RUTAS.CATEGORIAS.CREAR_CATEGORIA, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermiso(PERMISOS.CREAR_CATEGORIA_PRODUCTOS), + controlador.crearCategoria +); + +module.exports = ruteador; diff --git a/Categorias/Rutas/RutasIndividuales/eliminarCategoria.routes.js b/Categorias/Rutas/RutasIndividuales/eliminarCategoria.routes.js new file mode 100644 index 00000000..2afba0b2 --- /dev/null +++ b/Categorias/Rutas/RutasIndividuales/eliminarCategoria.routes.js @@ -0,0 +1,79 @@ +//RF[50] Elimina categoría de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF50] + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cat/ctrl/eliminarCategoria.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/categorias/eliminar: + * post: + * summary: Eliminar categorías de productos. + * description: Elimina una o varias categorías de productos de la base de datos. Requiere autenticación y permisos específicos. + * tags: + * - Categorías + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idsCategoria: + * type: array + * items: + * type: integer + * example: [1, 2, 3] + * responses: + * 200: + * description: Categorías eliminadas exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Categorías eliminadas correctamente. + * 404: + * description: Categorías no encontradas. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No se encontraron las categorías especificadas. + * 500: + * description: Error interno al eliminar las categorías. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al eliminar las categorías. + */ + +ruteador.post( + RUTAS.CATEGORIAS.ELIMINAR_CATEGORIA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_CATEGORIA_PRODUCTOS), + controlador.eliminarCategoria +); + +module.exports = ruteador; diff --git a/Categorias/Rutas/indexCategorias.routes.js b/Categorias/Rutas/indexCategorias.routes.js new file mode 100644 index 00000000..d412692f --- /dev/null +++ b/Categorias/Rutas/indexCategorias.routes.js @@ -0,0 +1,17 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutasConsultarListaCategorias = require('@altertex/cat/rutasInd/consultarListaCategorias.routes'); +const rutasCrearCategoria = require('@altertex/cat/rutasInd/crearCategoria.routes'); +const rutasEliminarCategoria = require('@altertex/cat/rutasInd/eliminarCategoria.routes'); +const rutasLeerCategoria = require('@altertex/cat/rutasInd/consultarDetalleCategoria.routes'); +const rutasActualizarCategoria = require('@altertex/cat/rutasInd/actualizarCategorias.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.use(RUTAS.CATEGORIAS.BASE, rutasConsultarListaCategorias); +ruteador.use(RUTAS.CATEGORIAS.BASE, rutasCrearCategoria); +ruteador.use(RUTAS.CATEGORIAS.BASE, rutasEliminarCategoria); +ruteador.use(RUTAS.CATEGORIAS.BASE, rutasLeerCategoria); +ruteador.use(RUTAS.CATEGORIAS.BASE, rutasActualizarCategoria); + +module.exports = ruteador; diff --git a/Clientes/Controladores/actualizarClientes.controller.js b/Clientes/Controladores/actualizarClientes.controller.js new file mode 100644 index 00000000..7abd24ae --- /dev/null +++ b/Clientes/Controladores/actualizarClientes.controller.js @@ -0,0 +1,78 @@ +const MENSAJES = require('@altertex/util/const/mensajesClientes'); +const repositorio = require('@altertex/cli/repos/repositorioActualizarCliente'); +// RF14 - Actualiza Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF14 + +/** + * Controlador para actualizar los datos de un cliente. + * + * Extrae los datos de actualización y la imagen (si existe) desde la petición. + * Valida que el ID del cliente esté presente y delega la actualización al repositorio. + * Devuelve una respuesta JSON con el estado correspondiente según el resultado. + * + * @param {Express.Request} req - Objeto de solicitud HTTP de Express. + * @param {object} req.body - Cuerpo de la solicitud con los datos del cliente. + * @param {string} req.body.idCliente - ID único del cliente a actualizar. + * @param {string} [req.body.nombreLegal] - Nuevo nombre legal del cliente (opcional). + * @param {string} [req.body.nombreComercial] - Nuevo nombre comercial del cliente (opcional). + * @param {object} [req.file] - Archivo de imagen cargado, si se proporciona. + * @param {Buffer} req.file.buffer - Contenido de la imagen en buffer. + * @param {string} req.file.mimetype - Tipo MIME del archivo de imagen. + * + * @param {Express.Response} res - Objeto de respuesta HTTP de Express. + * + * @returns {Promise} No retorna un valor directamente; envía la respuesta HTTP. + */ +exports.actualizarClientes = async (req, res) => { + const datosActualizacion = req.body; + const imagenActualizacion = req.file; + + // Validación básica de la solicitud + if (!datosActualizacion.idCliente) { + return res + .status(MENSAJES.FORMATO_ID_CLIENTE_INVALIDO.codigo) + .json({ mensaje: MENSAJES.FORMATO_ID_CLIENTE_INVALIDO.mensaje }); + } + + // Validación de que al menos un dato para actualizar esté presente + if ( + !datosActualizacion.nombreLegal + && !datosActualizacion.nombreComercial + && !imagenActualizacion + ) { + return res.status(MENSAJES.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: + 'Se debe proporcionar al menos un dato para actualizar (nombre legal, nombre comercial o imagen)', + }); + } + + try { + const mensaje = await repositorio.actualizarCliente(datosActualizacion, imagenActualizacion); + + return res.status(MENSAJES.CLIENTE_ACTUALIZADO.codigo).json({ mensaje }); + } catch (error) { + // Determinar el código de error adecuado basado en el mensaje de error + let codigoError = MENSAJES.ERROR_CLIENTE_ACTUALIZADO.codigo; + const mensajeError = error.message || MENSAJES.ERROR_CLIENTE_ACTUALIZADO.mensaje; + + // Asignar códigos de error específicos según el tipo de error + if (error.message === MENSAJES.CLIENTE_NO_ENCONTRADO.mensaje) { + codigoError = MENSAJES.CLIENTE_NO_ENCONTRADO.codigo; + } else if (error.message === MENSAJES.CLIENTE_COMERCIAL_EXISTENTE.mensaje) { + codigoError = MENSAJES.CLIENTE_COMERCIAL_EXISTENTE.codigo; + } else if (error.message === MENSAJES.CLIENTE_FISCAL_EXISTENTE.mensaje) { + codigoError = MENSAJES.CLIENTE_FISCAL_EXISTENTE.codigo; + } else if (error.message.includes('No se encontró la imagen')) { + codigoError = 404; + } else if (error.message.includes('Error al actualizar la imagen')) { + codigoError = 500; + } else if (error.message.includes('Error al actualizar los datos')) { + codigoError = 500; + } else if (error.message.includes('No se pudo actualizar')) { + codigoError = 400; + } + + console.error(`[ERROR] ActualizarClientes: ${error.message}`); + + return res.status(codigoError).json({ mensaje: mensajeError }); + } +}; diff --git a/Clientes/Controladores/consultarClientes.controller.js b/Clientes/Controladores/consultarClientes.controller.js new file mode 100644 index 00000000..44c2b512 --- /dev/null +++ b/Clientes/Controladores/consultarClientes.controller.js @@ -0,0 +1,79 @@ +const repositorio = require('@altertex/cli/repos/repositorioObtenerLista'); +const obtenerImagenFolder = require('@altertex/util/ser/obtenerImagenFolder'); +const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes'); + +/** + * Controlador para consultar el sistema de un cliente específico. + * + * RF12 - Consulta Lista de Clientes - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF12 + * + * @async + * @function consultarSistema + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.user - Información del usuario autenticado (inyectada por middleware). + * @param {string} req.user.correo - Correo electrónico del usuario autenticado. + * @param {Array} req.user.permisos - Permisos del usuario. + * @param {Array} req.user.clientesAsociados - Lista de IDs de clientes a los que el usuario tiene acceso. + * @param {object} req.body - Cuerpo de la solicitud. + * @param {string|number} req.body.idCliente - ID del cliente que se desea consultar. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la consulta es exitosa y se emite un nuevo token con el cliente seleccionado. + * - 400 si el formato del ID del cliente no es válido. + * - 403 si el usuario no está autorizado para consultar ese cliente. + * - 404 si el cliente no tiene sistema asociado. + * - 500 si ocurre un error en el servidor al consultar el sistema. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.consultarLista = async (req, res) => { + let clientesAsociados = req.user.clientesAsociados; + + if (!Array.isArray(clientesAsociados)) { + return res.status(MENSAJES_CLIENTES.CLIENTES_ASOCIADOS_NO_PROPORCIONADOS.codigo).json({ + mensaje: MENSAJES_CLIENTES.CLIENTES_ASOCIADOS_NO_PROPORCIONADOS.mensaje, + }); + } + + clientesAsociados = clientesAsociados.map((id) => parseInt(id)).filter((id) => !isNaN(id)); + + if (clientesAsociados.length === 0) { + return res + .status(MENSAJES_CLIENTES.LISTA_CLIENTES_INVALIDA.codigo) + .json({ mensaje: MENSAJES_CLIENTES.LISTA_CLIENTES_INVALIDA.mensaje }); + } + + try { + const listaClientes = await repositorio.obtenerLista(clientesAsociados); + req.clientes = listaClientes; + + if (!Array.isArray(listaClientes) || listaClientes.length === 0) { + return res + .status(MENSAJES_CLIENTES.LISTA_CLIENTES_VACIA.codigo) + .json({ mensaje: MENSAJES_CLIENTES.LISTA_CLIENTES_VACIA.mensaje }); + } + + const folder = 'clientes/'; + + let listaClientesConImagen; + try { + listaClientesConImagen = await obtenerImagenFolder(req, folder); + } catch { + listaClientesConImagen = listaClientes.map((cliente) => ({ + ...cliente, + urlImagen: '/placeholder.png', + })); + } + + return res.status(MENSAJES_CLIENTES.CONSULTA_LISTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_CLIENTES.CONSULTA_LISTA_EXITOSA.mensaje, + clientes: listaClientesConImagen, + }); + } catch (error) { + return res.status(MENSAJES_CLIENTES.ERROR_CONSULTAR_LISTA_CLIENTES.codigo).json({ + mensaje: MENSAJES_CLIENTES.ERROR_CONSULTAR_LISTA_CLIENTES.mensaje, + error: error.message, + }); + } +}; diff --git a/Clientes/Controladores/consultarSistema.controller.js b/Clientes/Controladores/consultarSistema.controller.js new file mode 100644 index 00000000..03209dfa --- /dev/null +++ b/Clientes/Controladores/consultarSistema.controller.js @@ -0,0 +1,89 @@ +const jwt = require('jsonwebtoken'); +const repositorio = require('@altertex/cli/repos/repositorioObtenerCliente'); +const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes'); + +/** + * Controlador para consultar el sistema de un cliente específico. + * + * + * @async + * @function consultarSistema + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.user - Información del usuario autenticado (inyectada por middleware). + * @param {string} req.user.correo - Correo electrónico del usuario autenticado. + * @param {Array} req.user.permisos - Permisos del usuario. + * @param {Array} req.user.clientesAsociados - Lista de IDs de clientes a los que el usuario tiene acceso. + * @param {string} req.user.nombreCompleto - Nombre completo del usuario autenticado. + * @param {object} req.body - Cuerpo de la solicitud. + * @param {string|number} req.body.idCliente - ID del cliente que se desea consultar. + * + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la consulta es exitosa y se emite un nuevo token con el cliente seleccionado. + * - 400 si el formato del ID del cliente no es válido (idCliente no es un número). + * - 403 si el usuario no está autorizado para consultar ese cliente (no tiene acceso al cliente). + * - 404 si el cliente no tiene sistema asociado (no se encuentra en la base de datos). + * - 500 si ocurre un error en el servidor al consultar el sistema. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.consultarSistema = async (req, res) => { + const idCliente = req.body.idCliente; + const { correo, permisos, clientesAsociados, nombreCompleto } = req.user; + + const tiempoExpiracion = 8 * 60 * 60 * 1000; + + // if (isNaN(idCliente)) { + // return res + // .status(MENSAJES_CLIENTES.FORMATO_ID_CLIENTE_INVALIDO.codigo) + // .json({ mensaje: MENSAJES_CLIENTES.FORMATO_ID_CLIENTE_INVALIDO.mensaje }); + // } + + if (!clientesAsociados.includes(idCliente)) { + return res + .status(MENSAJES_CLIENTES.ACCESO_NO_AUTORIZADO.codigo) + .json({ mensaje: MENSAJES_CLIENTES.ACCESO_NO_AUTORIZADO.mensaje }); + } + + try { + const sistema = await repositorio.obtenerCliente(idCliente); + if (!sistema) { + return res + .status(MENSAJES_CLIENTES.CLIENTE_SIN_SISTEMA.codigo) + .json({ mensaje: MENSAJES_CLIENTES.CLIENTE_SIN_SISTEMA.mensaje }); + } + + const nuevoToken = jwt.sign( + { + correo, + permisos, + clientesAsociados, + clienteSeleccionado: idCliente, + }, + process.env.JWT_SECRET, + { expiresIn: '8h' } + ); + + res.cookie('token', nuevoToken, { + httpOnly: true, + secure: true, + sameSite: 'None', + }); + + res.cookie('nombreUsuario', nombreCompleto, { + httpOnly: false, + secure: true, + sameSite: 'None', + maxAge: tiempoExpiracion, + }); + + return res.status(MENSAJES_CLIENTES.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_CLIENTES.CONSULTA_EXITOSA.mensaje, + }); + } catch { + return res + .status(MENSAJES_CLIENTES.ERROR_CONSULTAR_SISTEMA.codigo) + .json({ mensaje: MENSAJES_CLIENTES.ERROR_CONSULTAR_SISTEMA.mensaje }); + } +}; diff --git a/Clientes/Controladores/crearCliente.controller.js b/Clientes/Controladores/crearCliente.controller.js new file mode 100644 index 00000000..ac929fe1 --- /dev/null +++ b/Clientes/Controladores/crearCliente.controller.js @@ -0,0 +1,91 @@ +const repositorio = require('@altertex/cli/repos/repositorioCrearCliente'); +const MENSAJES = require('@altertex/util/const/mensajesClientes'); +const subirImagen = require('@altertex/util/ser/subirImagen'); + +/** + * Controlador para crear un nuevo cliente. + * + * Este controlador realiza las siguientes validaciones y operaciones: + * - Verifica que el nombre comercial del cliente sea válido. + * - Verifica que el nombre fiscal del cliente sea válido. + * - Verifica que la imagen del cliente sea válida. + * - Valida que el nombre comercial del cliente no esté duplicado en la base de datos. + * - Valida que el nombre fiscal del cliente no esté duplicado en la base de datos. + * - Inserta el cliente si todas las validaciones son exitosas. + * + * @async + * @function crearRol + * @param {Express.Request} req - Objeto de solicitud HTTP. Se espera que el cuerpo (`req.body`) contenga: + * @param {string} req.body.cliente.nombreComercial - Nombre comercial del cliente. + * @param {string} req.body.cliente.nombreFiscal - Nombre fiscal del cliente. + * @param {number[]} req.body.cliente.imagen - Imagen del cliente. + * @param {Express.Response} res - Objeto de respuesta HTTP. + * @returns {Promise} - Respuesta JSON con el estado de la creación del rol. + */ +exports.crearCliente = async (req, res) => { + const { nombreComercial, nombreFiscal } = req.body; + const imagen = req.file; + + // Validación del nombre comercial del cliente + if (!nombreComercial || typeof nombreComercial !== 'string') { + return res.status(400).json({ mensaje: MENSAJES.CAMPO_OBLIGATORIO.mensaje }); + } + + // Validación del nombre fiscal del cliente + if (!nombreFiscal || typeof nombreFiscal !== 'string') { + return res.status(400).json({ mensaje: MENSAJES.CAMPO_OBLIGATORIO.mensaje }); + } + + if (!imagen) { + return res.status(400).json({ mensaje: MENSAJES.CAMPO_OBLIGATORIO.mensaje }); + } + + try { + // Verificar si ya existe un cliente con ese nombre comercial + const existeComercial = await repositorio.verificarNombreComercial(nombreComercial); + if (existeComercial) { + return res.status(400).json({ mensaje: MENSAJES.CLIENTE_COMERCIAL_EXISTENTE.mensaje }); + } + + // Verificar si ya existe un cliente con ese nombre comercial + const existeFiscal = await repositorio.verificarNombreFiscal(nombreFiscal); + if (existeFiscal) { + return res.status(400).json({ mensaje: MENSAJES.CLIENTE_FISCAL_EXISTENTE.mensaje }); + } + + // Crear el cliente + const resultadoCliente = await repositorio.crearCliente(nombreComercial, nombreFiscal); + const resultadoVincular = await repositorio.vincularUsuarioCliente(resultadoCliente.insertId); + + if (imagen) { + const nombreImagen = await subirImagen(imagen, 'clientes', nombreComercial); + + const resultadoImagen = await repositorio.crearImagenCliente( + nombreComercial, + nombreImagen.split('/')[1] + ); + + const resultado = await repositorio.vincularImagenCliente( + resultadoImagen.insertId, + resultadoCliente.insertId + ); + if ( + resultadoCliente.insertId + && resultadoVincular.affectedRows + && resultadoImagen.insertId + && resultado.affectedRows === 1 + ) { + return res.status(201).json({ mensaje: MENSAJES.CLIENTE_CREADO.mensaje }); + } + } else { + // ✅ manejar caso sin imagen + if (resultadoCliente.insertId && resultadoVincular.affectedRows === 1) { + return res.status(201).json({ mensaje: MENSAJES.CLIENTE_CREADO.mensaje }); + } else { + return res.status(500).json({ mensaje: MENSAJES.ERROR_CREACION.mensaje }); + } + } + } catch { + return res.status(500).json({ mensaje: MENSAJES.ERROR_CREACION.mensaje }); + } +}; diff --git a/Clientes/Controladores/eliminarCliente.controller.js b/Clientes/Controladores/eliminarCliente.controller.js new file mode 100644 index 00000000..a4098669 --- /dev/null +++ b/Clientes/Controladores/eliminarCliente.controller.js @@ -0,0 +1,61 @@ +// Importaciones necesarias +const repositorio = require('@altertex/cli/repos/repositorioEliminarCliente'); +const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes'); +const extraerNombreArchivoS3 = require('@altertex/util/ser/extraerNombreArchivoS3'); +const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3'); +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CLIENTES = require('@altertex/util/const/consultasClientes'); + +/** + * Controlador para eliminar un cliente de la base de datos y su imagen de S3. + * @see [RF15 - Elimina Cliente](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF15) + * + * @async + * @function eliminarCliente + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {number} req.body.idCliente - ID del cliente a eliminar. + * @param {object} res - Objeto de respuesta de Express. + * @returns {Promise} Respuesta HTTP con estado. + * @throws {Error} Si ocurre un error durante la eliminación. + */ +exports.eliminarCliente = async (req, res) => { + try { + const idCliente = parseInt(req.body.idCliente); + + if (isNaN(idCliente)) { + return res + .status(MENSAJES_CLIENTES.CLIENTE_INVALIDO.codigo) + .json({ mensaje: MENSAJES_CLIENTES.CLIENTE_INVALIDO.mensaje }); + } + + // Obtener nombre de la imagen asociada (si existe) + let nombreImagen = ''; + const resultadoImagen = await correrQuery(CONSULTAS_CLIENTES.OBTENER_NOMBRE_IMAGEN, [idCliente]); + if (resultadoImagen.length > 0 && resultadoImagen[0].urlImagen) { + nombreImagen = extraerNombreArchivoS3(resultadoImagen[0].urlImagen); + } + + // Eliminar cliente + const resultado = await repositorio.eliminarClientePorId(idCliente); + + if (resultado.affectedRows === 0) { + return res + .status(MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.codigo) + .json({ mensaje: MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.mensaje }); + } + + // Eliminar imagen si hay nombre válido + if (nombreImagen) { + await eliminarImagenS3('clientes/', nombreImagen); + } + + return res + .status(MENSAJES_CLIENTES.CLIENTE_ELIMINADO.codigo) + .json({ mensaje: MENSAJES_CLIENTES.CLIENTE_ELIMINADO.mensaje }); + } catch { + return res + .status(MENSAJES_CLIENTES.ERROR_ELIMINAR_CLIENTE.codigo) + .json({ mensaje: MENSAJES_CLIENTES.ERROR_ELIMINAR_CLIENTE.mensaje }); + } +}; \ No newline at end of file diff --git a/Clientes/Controladores/leerCliente.controller.js b/Clientes/Controladores/leerCliente.controller.js new file mode 100644 index 00000000..3a7ded03 --- /dev/null +++ b/Clientes/Controladores/leerCliente.controller.js @@ -0,0 +1,53 @@ +const repositorio = require('@altertex/cli/repos/repositorioLeerCliente'); +const obtenerImagenCliente = require('@altertex/util/ser/obtenerImagenCliente'); +const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes'); + +/** + * Lee los detalles de un cliente desde la base de datos utilizando su ID. + * + * Valida el parámetro `idCliente` y obtiene la información del cliente a través del repositorio. + * Si el cliente no es encontrado o el parámetro es inválido, retorna un error. + * + * @param {Express.Request} req - La solicitud HTTP que contiene el `idCliente` en el cuerpo. + * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente. + * @returns {Promise} Responde con el cliente encontrado o un mensaje de error. + * + * @see RF13 Leer cliente](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf13/) + */ +exports.leerCliente = async (req, res) => { + const idCliente = parseInt(req.body.idCliente); + + if (isNaN(idCliente)) { + return res + .status(MENSAJES_CLIENTES.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_CLIENTES.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const cliente = await repositorio.obtenerClientePorId(idCliente); + + if (!cliente) { + return res + .status(MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.codigo) + .json({ mensaje: MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.mensaje }); + } + let imagenCliente; + try { + imagenCliente = await obtenerImagenCliente(cliente.urlImagen); + } catch { + imagenCliente = '/placeholder.png'; // URL genérica de placeholder + } + + return res.status(MENSAJES_CLIENTES.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_CLIENTES.CONSULTA_EXITOSA.mensaje, + cliente: { + ...cliente, + imagenCliente, + }, + }); + } catch { + return res + .status(MENSAJES_CLIENTES.ERROR_CONSULTAR_CLIENTE.codigo) + .json({ mensaje: MENSAJES_CLIENTES.ERROR_CONSULTAR_CLIENTE.mensaje }); + } +}; \ No newline at end of file diff --git a/Clientes/Datos/Repositorios/repositorioActualizarCliente.js b/Clientes/Datos/Repositorios/repositorioActualizarCliente.js new file mode 100644 index 00000000..a18817f1 --- /dev/null +++ b/Clientes/Datos/Repositorios/repositorioActualizarCliente.js @@ -0,0 +1,109 @@ +const MENSAJES = require('@altertex/util/const/mensajesClientes'); +const CONSULTAS = require('@altertex/util/const/consultasClientes'); +const correrQuery = require('@altertex/util/ser/correrQuery'); +const enviarS3 = require('@altertex/util/ser/enviarS3'); +// RF14 - Actualiza Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF14 + +/** + * Actualiza los datos de un cliente, incluyendo su nombre legal y/o comercial, + * y la imagen asociada al cliente si se proporciona. + * + * @param {object} datosActualizacion - Datos de actualización del cliente. + * @param {string} datosActualizacion.idCliente - ID único del cliente a actualizar. + * @param {string} [datosActualizacion.nombreLegal] - Nuevo nombre legal del cliente (opcional). + * @param {string} [datosActualizacion.nombreComercial] - Nuevo nombre comercial del cliente (opcional). + * @param {object} [imagenActualizacion] - Información de la imagen a actualizar. + * @param {Buffer} imagenActualizacion.buffer - El buffer de la imagen a cargar. + * @param {string} imagenActualizacion.mimetype - Tipo MIME de la imagen (ej. 'image/jpeg'). + * + * @returns {string} Mensaje indicando si la actualización fue exitosa. + * + * @throws {Error} Lanza un error si ocurre un problema durante la actualización del cliente. + */ +exports.actualizarCliente = async (datosActualizacion, imagenActualizacion) => { + const { idCliente, nombreLegal, nombreComercial } = datosActualizacion; + + const resultadoCliente = await correrQuery(CONSULTAS.OBTENER_CLIENTE, [idCliente]); + if (!resultadoCliente || resultadoCliente.length === 0) { + throw new Error(MENSAJES.CLIENTE_NO_ENCONTRADO.mensaje); + } + + if (nombreComercial) { + const [resultadoNombreComercial] = await correrQuery(CONSULTAS.VERIFICAR_NOMBRE_COMERCIAL, [ + nombreComercial, + ]); + const existeNombreComercial = Object.values(resultadoNombreComercial)[0]; + + if (existeNombreComercial === 1) { + const [clienteActual] = await correrQuery(CONSULTAS.OBTENER_CLIENTE, [idCliente]); + if (clienteActual && clienteActual.nombreComercial !== nombreComercial) { + throw new Error(MENSAJES.CLIENTE_COMERCIAL_EXISTENTE.mensaje); + } + } + } + + if (nombreLegal) { + const [resultadoNombreFiscal] = await correrQuery(CONSULTAS.VERIFICAR_NOMBRE_FISCAL, [ + nombreLegal, + ]); + const existeNombreFiscal = Object.values(resultadoNombreFiscal)[0]; + + if (existeNombreFiscal === 1) { + const [clienteActual] = await correrQuery(CONSULTAS.OBTENER_CLIENTE, [idCliente]); + if (clienteActual && clienteActual.nombreFiscal !== nombreLegal) { + throw new Error(MENSAJES.CLIENTE_FISCAL_EXISTENTE.mensaje); + } + } + } + + if (imagenActualizacion) { + const resultadoImagen = await correrQuery(CONSULTAS.OBTENER_NOMBRE_IMAGEN, [idCliente]); + if (!resultadoImagen || resultadoImagen.length === 0 || !resultadoImagen[0].urlImagen) { + throw new Error('No se encontró la imagen del cliente para actualizar'); + } + + const nombreImagen = resultadoImagen[0]; + const parametros = { + Bucket: process.env.AWS_BUCKET_NAME, + Key: `clientes/${nombreImagen.urlImagen}`, + Body: imagenActualizacion.buffer, + ContentType: imagenActualizacion.mimetype, + }; + + await enviarS3(parametros); + } + + if (!nombreLegal && !nombreComercial) { + return MENSAJES.CLIENTE_ACTUALIZADO.mensaje; + } + + let resultadoQuery; + if (nombreLegal && nombreComercial) { + resultadoQuery = await correrQuery(CONSULTAS.ACTUALIZAR_AMBOS_NOMBRES, [ + nombreComercial, + nombreLegal, + idCliente, + ]); + if (!resultadoQuery || resultadoQuery.affectedRows === 0) { + throw new Error('No se pudo actualizar los nombres del cliente'); + } + } else if (nombreLegal) { + resultadoQuery = await correrQuery(CONSULTAS.ACTUALIZAR_NOMBRE_FISCAL, [ + nombreLegal, + idCliente, + ]); + if (!resultadoQuery || resultadoQuery.affectedRows === 0) { + throw new Error('No se pudo actualizar el nombre fiscal del cliente'); + } + } else if (nombreComercial) { + resultadoQuery = await correrQuery(CONSULTAS.ACTUALIZAR_NOMBRE_COMERCIAL, [ + nombreComercial, + idCliente, + ]); + if (!resultadoQuery || resultadoQuery.affectedRows === 0) { + throw new Error('No se pudo actualizar el nombre comercial del cliente'); + } + } + + return MENSAJES.CLIENTE_ACTUALIZADO.mensaje; +}; diff --git a/Clientes/Datos/Repositorios/repositorioCrearCliente.js b/Clientes/Datos/Repositorios/repositorioCrearCliente.js new file mode 100644 index 00000000..9c3ce440 --- /dev/null +++ b/Clientes/Datos/Repositorios/repositorioCrearCliente.js @@ -0,0 +1,111 @@ +const QUERY = require('@altertex/util/const/consultasClientes'); +const correrQuery = require('@altertex/util/ser/correrQuery'); + +/** + * Verifica si un cliente con el nombre comercial especificado ya existe en la base de datos. + * + * @async + * @function verificarNombreComercial + * @param {string} nombre - Nombre comercial del cliente a verificar. + * @returns {Promise} Retorna `true` si existe otro cliente con ese nombre comercial, `false` en caso contrario. + */ +exports.verificarNombreComercial = async (nombre) => { + const resultado = await correrQuery(QUERY.VERIFICAR_NOMBRE_COMERCIAL, [nombre]); + const valor = Object.values(resultado[0])[0]; + return valor == 1; +}; + +/** + * Verifica si un cliente con el nombre fiscal especificado ya existe en la base de datos. + * + * @async + * @function verificarNombreFiscal + * @param {string} nombre - Nombre fiscal del cliente a verificar. + * @returns {Promise} Retorna `true` si existe otro cliente con ese nombre fiscal, `false` en caso contrario. + */ +exports.verificarNombreFiscal = async (nombre) => { + const resultado = await correrQuery(QUERY.VERIFICAR_NOMBRE_FISCAL, [nombre]); + const valor = Object.values(resultado[0])[0]; + return valor == 1; +}; + +/** + * Crea un nuevo cliente en la base de datos. + * + * @async + * @function crearCliente + * @param {string} nombreComercial - Nombre comercial del nuevo cliente. + * @param {string} nombreFiscal - Nombre fiscal del nuevo cliente. + * @returns {Promise} Retorna el resultado de la operación de inserción, incluyendo el ID del nuevo cliente. + */ +exports.crearCliente = async (nombreComercial, nombreFiscal) => { + try { + const resultadoCliente = await correrQuery(QUERY.CREAR_CLIENTE, [ + nombreComercial, + nombreFiscal, + ]); + return resultadoCliente; + } catch (error) { + console.error('Error al crear cliente:', error); + throw new Error('Error al crear cliente'); + } +}; + +/** + * Vincula el usuario autenticado al cliente recién creado. + * + * @async + * @function vincularUsuarioCliente + * @param {number} idCliente - ID del cliente con el que se debe vincular el usuario. + * @returns {Promise} Resultado de la operación de vinculación. + */ +exports.vincularUsuarioCliente = async (idCliente) => { + try { + const resultadoVincular = await correrQuery(QUERY.VINCULAR_USUARIO_CLIENTE, [idCliente]); + return resultadoVincular; + } catch (error) { + console.error('Error al vincular usuario al cliente:', error); + throw new Error('Error al vincular usuario al cliente'); + } +}; + +/** + * Inserta una imagen asociada a un cliente en la base de datos. + * + * @async + * @function crearImagenCliente + * @param {string} nombreComercial - Nombre comercial del cliente para asociar en la descripción. + * @param {string} imagen - Ruta o identificador de la imagen a registrar. + * @returns {Promise} Resultado de la operación de inserción de imagen. + */ +exports.crearImagenCliente = async (nombreComercial, imagen) => { + try { + const resultadoImagen = await correrQuery(QUERY.CREAR_IMAGEN_CLIENTE, [ + imagen, + `Logo de ${nombreComercial}`, + ]); + return resultadoImagen; + } catch (error) { + console.error('Error al crear imagen del cliente:', error); + throw new Error('Error al crear imagen del cliente'); + } +}; + +/** + * Vincula una imagen previamente registrada con un cliente específico. + * + * @async + * @function vincularImagenCliente + * @param {number} imagenId - ID de la imagen a vincular. + * @param {number} clienteId - ID del cliente con el que se vinculará la imagen. + * @returns {Promise} Resultado de la operación de vinculación. + */ +exports.vincularImagenCliente = async (imagenId, clienteId) => { + try { + const resultado = await correrQuery(QUERY.VINCULAR_IMAGEN_CLIENTE, [imagenId, clienteId]); + return resultado; + } catch (error) { + console.error('Error al vincular imagen y cliente', error); + throw new Error('Error al vincular imagen y cliente'); + } +}; diff --git a/Clientes/Datos/Repositorios/repositorioEliminarCliente.js b/Clientes/Datos/Repositorios/repositorioEliminarCliente.js new file mode 100644 index 00000000..2d7c46b6 --- /dev/null +++ b/Clientes/Datos/Repositorios/repositorioEliminarCliente.js @@ -0,0 +1,22 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CLIENTES = require('@altertex/util/const/consultasClientes'); + +/** + * Elimina un cliente de la base de datos. + * + * @async + * @function eliminarClientePorId + * @param {number} idCliente - ID del cliente a eliminar. + * @returns {Promise<{affectedRows: number}>} Resultado de la operación con número de filas afectadas. + * @throws {Error} Si ocurre un error durante la eliminación. + * @see [RF15 - Elimina Cliente](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF15) + */ +exports.eliminarClientePorId = async (idCliente) => { + const query = CONSULTAS_CLIENTES.ELIMINAR_CLIENTE; + try { + const resultado = await correrQuery(query, [idCliente]); + return resultado; + } catch { + throw new Error('Ocurrio un error eliminando al cliente.'); + } +}; \ No newline at end of file diff --git a/Clientes/Datos/Repositorios/repositorioLeerCliente.js b/Clientes/Datos/Repositorios/repositorioLeerCliente.js new file mode 100644 index 00000000..6cacf62e --- /dev/null +++ b/Clientes/Datos/Repositorios/repositorioLeerCliente.js @@ -0,0 +1,37 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CLIENTES = require('@altertex/util/const/consultasClientes'); + +/** + * Obtiene un cliente desde la base de datos mediante su ID. + * + * Ejecuta una consulta SQL y retorna el primer cliente encontrado o `null` si no existe. + * + * @param {number|string} idCliente - ID del cliente a buscar. + * @returns {Promise} El cliente encontrado o `null` si no existe. + * @throws {Error} Si ocurre un error al ejecutar la consulta. + * + * @see [RF13 Leer cliente](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf13/) + */ +exports.obtenerClientePorId = async (idCliente) => { + const query = CONSULTAS_CLIENTES.LEER_CLIENTE; + + try { + const resultado = await correrQuery(query, [idCliente]); + + if (resultado.length === 0) return null; + + const cliente = { + idCliente: resultado[0].idCliente, + nombreLegal: resultado[0].nombreFiscal, + nombreVisible: resultado[0].nombreComercial, + empleados: resultado[0].empleados, + usuariosAsignados: resultado[0].usuariosAsignados, + numeroEmpleados: resultado[0].numeroEmpleados, + urlImagen: resultado[0].urlImagen, + }; + + return cliente; + } catch { + throw new Error('Ocurrio un error obteniendo el cliente.'); + } +}; \ No newline at end of file diff --git a/Clientes/Datos/Repositorios/repositorioObtenerCliente.js b/Clientes/Datos/Repositorios/repositorioObtenerCliente.js new file mode 100644 index 00000000..abfbeb24 --- /dev/null +++ b/Clientes/Datos/Repositorios/repositorioObtenerCliente.js @@ -0,0 +1,29 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CLIENTES = require('@altertex/util/const/consultasClientes'); + +/** + * Obtiene la información de un cliente a partir de su ID. + * + * RF12 - Consulta Lista de Clientes - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF12 + * + * @async + * @function obtenerCliente + * @param {number} idCliente - ID del cliente a buscar. + * + * @returns {Promise} Objeto con la información del cliente si se encuentra, + * o un string con un mensaje de error si ocurre un fallo durante la operación. + */ +exports.obtenerCliente = async (idCliente) => { + try { + const query = CONSULTAS_CLIENTES.OBTENER_CLIENTE; + const resultado = await correrQuery(query, [idCliente]); + + if (!resultado || resultado.length === 0) { + return `No se encontró un cliente con el ID ${idCliente}`; + } + + return resultado[0]; + } catch (error) { + return `Error obteniendo cliente: ${error}`; + } +}; diff --git a/Clientes/Datos/Repositorios/repositorioObtenerLista.js b/Clientes/Datos/Repositorios/repositorioObtenerLista.js new file mode 100644 index 00000000..92b664a3 --- /dev/null +++ b/Clientes/Datos/Repositorios/repositorioObtenerLista.js @@ -0,0 +1,23 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CLIENTES = require('@altertex/util/const/consultasClientes'); + +/** + * Obtiene la lista de clientes asociados según los IDs proporcionados. + * + * @async + * @function obtenerLista + * @param {number[]} clientesAsociados - Arreglo de IDs de clientes asociados al usuario. + * @returns {Promise} Arreglo con la información de los clientes, + * o un string con un mensaje de error si ocurre un fallo durante la operación. + */ +exports.obtenerLista = async (clientesAsociados) => { + try { + const query = CONSULTAS_CLIENTES.OBTENER_LISTA; + + const resultado = await correrQuery(query, [clientesAsociados]); + + return resultado; + } catch (error) { + return `Error obteniendo lista de clientes: ${error}`; + } +}; diff --git a/Clientes/Rutas/RutasIndividuales/actualizarClientes.routes.js b/Clientes/Rutas/RutasIndividuales/actualizarClientes.routes.js new file mode 100644 index 00000000..2cfd3f66 --- /dev/null +++ b/Clientes/Rutas/RutasIndividuales/actualizarClientes.routes.js @@ -0,0 +1,102 @@ +const express = require('express'); +const ruteador = express.Router(); +const multer = require('multer'); +const RUTAS = require('@altertex/util/const/rutas'); +const PERMISOS = require('@altertex/util/const/permisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const controlador = require('@altertex/cli/ctrl/actualizarClientes.controller'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const storage = multer.memoryStorage(); +const upload = multer({ storage }); + +// RF14 - Actualiza Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF14 +/** + * @swagger + * /api/clientes/actualizar-cliente: + * put: + * tags: + * - Clientes + * summary: Actualiza los datos de un cliente + * description: > + * Esta ruta permite actualizar los datos de un cliente, incluyendo su nombre legal, nombre comercial y una imagen opcional. + * Requiere autenticación mediante token JWT, una API key válida, y permisos específicos (ACTUALIZAR_CLIENTE). + * operationId: actualizarCliente + * security: + * - ApiKeyAuth: [] + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * required: + * - idCliente + * properties: + * idCliente: + * type: string + * description: ID del cliente a actualizar. + * example: "123" + * nombreLegal: + * type: string + * description: Nuevo nombre legal del cliente. + * example: "Altertex S.A. de C.V." + * nombreComercial: + * type: string + * description: Nuevo nombre comercial del cliente. + * example: "Altertex Textiles" + * imagen: + * type: string + * format: binary + * description: Imagen nueva del cliente (opcional). + * responses: + * 200: + * description: Cliente actualizado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Cliente actualizado correctamente. + * 400: + * description: Formato inválido del ID del cliente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: ID del cliente con formato inválido. + * 403: + * description: Permisos insuficientes o token inválido + * 500: + * description: Error del servidor al actualizar el cliente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Ocurrió un error al actualizar el cliente. + */ +ruteador.put( + RUTAS.CLIENTES.ACTUALIZAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ACTUALIZAR_CLIENTE), + validarYSanitizar, + upload.single('imagen'), + controlador.actualizarClientes +); + +module.exports = ruteador; diff --git a/Clientes/Rutas/RutasIndividuales/consultarClientes.routes.js b/Clientes/Rutas/RutasIndividuales/consultarClientes.routes.js new file mode 100644 index 00000000..1fdd80dc --- /dev/null +++ b/Clientes/Rutas/RutasIndividuales/consultarClientes.routes.js @@ -0,0 +1,83 @@ +const express = require("express"); +const ruteador = express.Router(); +const controlador = require("@altertex/cli/ctrl/consultarClientes.controller"); +const revisarApiKey = require("@altertex/util/inter/revisarApiKey"); +const autorizarToken = require("@altertex/util/inter/autorizarToken"); +const verificarPermisos = require("@altertex/util/inter/verificarPermisos"); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require("@altertex/util/const/permisos"); +const RUTAS = require("@altertex/util/const/rutas"); + +/** + * RF12 - Consulta Lista de Clientes - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF12 + */ + +/** + * @swagger + * /api/clientes/consultar-lista: + * get: + * summary: Consultar lista de clientes asociados al usuario autenticado + * tags: [Clientes] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Lista de clientes obtenida exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Lista de clientes obtenida exitosamente. + * clientes: + * type: array + * items: + * type: object + * properties: + * idCliente: + * type: integer + * example: 2 + * nombreComercial: + * type: string + * example: Toyota Motors México + * nombreFiscal: + * type: string + * example: Toyota de México, S.A. de C.V. + * idImagen: + * type: integer + * example: 1 + * urlImagen: + * type: string + * format: uri + * example: https://altertex.s3.us-east-2.amazonaws.com/clientes/toyota.jpg?... (URL firmada) + * tipoImagen: + * type: string + * example: Logo + * textoAlternativo: + * type: string + * example: Logo de Toyota Motors México + * 400: + * description: Datos inválidos o lista vacía + * 401: + * description: No autorizado, token o API key inválida + * 403: + * description: No tiene permisos para consultar clientes + * 500: + * description: Error interno al consultar los clientes + */ + +ruteador.get( + RUTAS.CLIENTES.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_CLIENTES), + controlador.consultarLista +); + +module.exports = ruteador; diff --git a/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js b/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js new file mode 100644 index 00000000..304c0c1f --- /dev/null +++ b/Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js @@ -0,0 +1,71 @@ +const express = require("express"); +const ruteador = express.Router(); +const controlador = require("@altertex/cli/ctrl/consultarSistema.controller"); +const revisarApiKey = require("@altertex/util/inter/revisarApiKey"); +const autorizarToken = require("@altertex/util/inter/autorizarToken"); +const verificarPermisos = require("@altertex/util/inter/verificarPermisos"); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require("@altertex/util/const/permisos"); +const RUTAS = require("@altertex/util/const/rutas"); + +/** + * @swagger + * /api/clientes/consultar-sistema: + * post: + * summary: Consultar sistema administrativo del cliente y emitir nuevo token + * tags: [Clientes] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idCliente + * properties: + * idCliente: + * type: integer + * example: 1 + * responses: + * 200: + * description: Consulta exitosa y nuevo token emitido + * headers: + * Set-Cookie: + * description: Token JWT actualizado para el cliente seleccionado + * schema: + * type: string + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Sistema consultado exitosamente. + * 400: + * description: Formato de ID de cliente inválido + * 401: + * description: No autorizado, token o API key inválida + * 403: + * description: El usuario no tiene acceso a ese cliente + * 404: + * description: El cliente no tiene sistema asociado + * 500: + * description: Error interno al consultar el sistema + */ + +ruteador.post( + RUTAS.CLIENTES.CONSULTAR_SISTEMA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_SISTEMA_ADMINISTRATIVO), + controlador.consultarSistema +); + +module.exports = ruteador; diff --git a/Clientes/Rutas/RutasIndividuales/crearCliente.routes.js b/Clientes/Rutas/RutasIndividuales/crearCliente.routes.js new file mode 100644 index 00000000..2cde7760 --- /dev/null +++ b/Clientes/Rutas/RutasIndividuales/crearCliente.routes.js @@ -0,0 +1,72 @@ +/** + * RF11 - Crear Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF11 + */ + +/** + * @swagger + * /api/clientes/crear-cliente: + * post: + * summary: Crear un nuevo cliente + * tags: [Clientes] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * parameters: + * - in: body + * name: nombreComercial + * required: true + * schema: + * type: integer + * description: Nombre comercial del cliente que se quiere crear. + * responses: + * 201: + * description: Cliente creado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Cliente creado. + * 400: + * description: Error al validar los datos. + * 401: + * description: No autorizado, token o API key inválida + * 403: + * description: No tiene permisos para crear clientes + * 500: + * description: Error interno al crear el cliente + */ + +const express = require('express'); +const ruteador = express.Router(); +const multer = require('multer'); +const controlador = require('@altertex/cli/ctrl/crearCliente.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const validarYSanitizarImagen = require('@altertex/util/inter/validarYSanitizarImagen'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +// Configuración de multer para manejar archivos en memoria +const storage = multer.memoryStorage(); +const upload = multer({ storage }); + +ruteador.post( + RUTAS.CLIENTES.CREAR_CLIENTE, + upload.single('imagen'), + validarYSanitizar, + validarYSanitizarImagen(), + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CREAR_CLIENTE), + controlador.crearCliente +); + +module.exports = ruteador; diff --git a/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js b/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js new file mode 100644 index 00000000..bd0e0b0d --- /dev/null +++ b/Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js @@ -0,0 +1,59 @@ +/** + * RF15 - Elimina Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF15 + */ + +/** + * @swagger + * /api/clientes/eliminar: + * post: + * summary: Eliminar un cliente registrado + * tags: [Clientes] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * parameters: + * - idCliente: int + * required: true + * schema: + * type: integer + * description: ID del cliente que se desea eliminar + * responses: + * 200: + * description: Cliente eliminado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Cliente eliminado + * 404: + * description: No se encontró un cliente con el ID proporcionado. + * 400: + * description: Error interno al eliminar el cliente + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cli/ctrl/eliminarCliente.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.post( + RUTAS.CLIENTES.ELIMINAR_CLIENTE, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_CLIENTE), + controlador.eliminarCliente +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js b/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js new file mode 100644 index 00000000..803bae07 --- /dev/null +++ b/Clientes/Rutas/RutasIndividuales/leerCliente.routes.js @@ -0,0 +1,102 @@ +/** + * RF13 - Consulta Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf13/ + */ + +/** + * @swagger + * /api/clientes/consultar-cliente: + * post: + * summary: Consulta la información de un cliente específico. + * description: | + * Este endpoint permite consultar los datos de un cliente por su ID. + * tags: [Clientes] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idCliente: + * type: integer + * example: 1 + * required: + * - idCliente + * responses: + * 200: + * description: Cliente encontrado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Información del cliente obtenida exitosamente." + * cliente: + * type: object + * properties: + * idCliente: + * type: integer + * example: 1 + * nombreLegal: + * type: string + * example: "Toyota Motors Corporation" + * nombreVisible: + * type: string + * example: "Toyota" + * empleados: + * type: integer + * example: 1902 + * usuariosAsignados: + * type: integer + * example: 5 + * imagenCliente: + * type: string + * example: "https://example.com/logo-toyota.png" + * 404: + * description: Cliente no encontrado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No se encontró un cliente con el ID proporcionado." + * 400: + * description: Error interno del servidor al consultar el cliente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Ocurrió un error al obtener los datos del cliente." + */ + +const express = require("express"); +const ruteador = express.Router(); +const controlador = require("@altertex/cli/ctrl/leerCliente.controller"); +const revisarApiKey = require("@altertex/util/inter/revisarApiKey"); +const autorizarToken = require("@altertex/util/inter/autorizarToken"); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermisos = require("@altertex/util/inter/verificarPermisos"); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const PERMISOS = require("@altertex/util/const/permisos"); +const RUTAS = require("@altertex/util/const/rutas"); + +ruteador.post( + RUTAS.CLIENTES.LEER, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.LEER_CLIENTE), + controlador.leerCliente +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Clientes/Rutas/indexClientes.routes.js b/Clientes/Rutas/indexClientes.routes.js new file mode 100644 index 00000000..8ea2aab6 --- /dev/null +++ b/Clientes/Rutas/indexClientes.routes.js @@ -0,0 +1,24 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutasConsultarSistema = require("@altertex/cli/rutasInd/consultarSistema.routes"); +const rutasConsultarClientes = require("@altertex/cli/rutasInd/consultarClientes.routes"); +const rutasEliminarCliente = require("@altertex/cli/rutasInd/eliminarCliente.routes"); +const rutasCrearCliente = require("@altertex/cli/rutasInd/crearCliente.routes"); +const rutasActualizarCliente = require('@altertex/cli/rutasInd/actualizarClientes.routes'); +const rutasLeerCliente = require('@altertex/cli/rutasInd/leerCliente.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.use(RUTAS.CLIENTES.BASE, rutasConsultarSistema); +//RF12 - Consulta Lista de Clientes - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF12 +ruteador.use(RUTAS.CLIENTES.BASE, rutasConsultarClientes); +//RF15 - Elimina Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF15 +ruteador.use(RUTAS.CLIENTES.BASE, rutasEliminarCliente); + +//RF13 - Consulta Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf13/ +ruteador.use(RUTAS.CLIENTES.BASE, rutasLeerCliente); +//RF11 - Crear Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF11 +ruteador.use(RUTAS.CLIENTES.BASE, rutasCrearCliente); + +ruteador.use(RUTAS.CLIENTES.BASE, rutasActualizarCliente); +module.exports = ruteador; diff --git a/Configuracion/clienteRedis.js b/Configuracion/clienteRedis.js new file mode 100644 index 00000000..5f5f3a76 --- /dev/null +++ b/Configuracion/clienteRedis.js @@ -0,0 +1,10 @@ +const { createClient } = require('redis'); + +const redis = createClient({ + url: process.env.REDIS_URL, +}); + +redis.on('error', (err) => console.error('Error Redis', err)); +redis.connect(); + +module.exports = redis; \ No newline at end of file diff --git a/Configuracion/corsOptions.js b/Configuracion/corsOptions.js new file mode 100644 index 00000000..b3e7ebd7 --- /dev/null +++ b/Configuracion/corsOptions.js @@ -0,0 +1,5 @@ +module.exports = { + origin: [process.env.LOCAL_URL, process.env.DEPLOYED_URL], + methods: ["GET", "POST", "PUT", "DELETE"], + credentials: true, +}; diff --git a/Configuracion/dotenv.js b/Configuracion/dotenv.js new file mode 100644 index 00000000..ce300906 --- /dev/null +++ b/Configuracion/dotenv.js @@ -0,0 +1,5 @@ +const dotenv = require("dotenv"); +const camino = require("path"); + +const archivoEnv = `.env.${process.env.NODE_ENV || "staging"}`; +dotenv.config({ path: camino.resolve(process.cwd(), archivoEnv) }); diff --git a/Configuracion/rutasSwagger.js b/Configuracion/rutasSwagger.js new file mode 100644 index 00000000..b7982bb0 --- /dev/null +++ b/Configuracion/rutasSwagger.js @@ -0,0 +1,58 @@ +module.exports = [ + './Autenticacion/Rutas/RutasIndividuales/autenticacionSesion.routes.js', + './Autenticacion/Rutas/RutasIndividuales/cerrarSesion.routes.js', + './Autenticacion/Rutas/RutasIndividuales/inicioSesion.routes.js', + + './Categorias/Rutas/RutasIndividuales/consultarListaCategorias.routes.js', + './Categorias/Rutas/RutasIndividuales/crearCategoria.routes.js', + './Categorias/Rutas/RutasIndividuales/eliminarCategoria.routes.js', + + './Clientes/Rutas/RutasIndividuales/consultarClientes.routes.js', + './Clientes/Rutas/RutasIndividuales/consultarSistema.routes.js', + './Clientes/Rutas/RutasIndividuales/eliminarCliente.routes.js', + './Clientes/Rutas/RutasIndividuales/leerCliente.routes.js', + './Clientes/Rutas/RutasIndividuales/actualizarClientes.routes.js', + './Clientes/Rutas/RutasIndividuales/crearCliente.routes.js', + + './Cuotas/Rutas/RutasIndividuales/consultarCuotas.routes.js', + './Cuotas/Rutas/RutasIndividuales/crearCuota.routes.js', + './Cuotas/Rutas/RutasIndividuales/eliminarSetCuotas.routes.js', + './Cuotas/Rutas/RutasIndividuales/obtenerOpcionesCuotas.routes.js', + + './Empleados/Rutas/RutasIndividuales/consultarLista.routes.js', + './Empleados/Rutas/RutasIndividuales/consultarListaGrupos.routes.js', + './Empleados/Rutas/RutasIndividuales/eliminarEmpleado.routes.js', + './Empleados/Rutas/RutasIndividuales/eliminarGrupoEmpleados.routes.js', + './Empleados/Rutas/RutasIndividuales/leerGrupoEmpleados.routes.js', + './Empleados/Rutas/RutasIndividuales/importarEmpleados.routes.js', + './Empleados/Rutas/RutasIndividuales/crearGrupoEmpleados.routes.js', + './Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js', + + './Eventos/Rutas/RutasIndividuales/consultarListaEventos.routes.js', + './Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js', + './Eventos/Rutas/RutasIndividuales/eliminarEvento.routes.js', + + './Pagos/Rutas/RutasIndividuales/consultarTipoPago.routes.js', + './Pagos/Rutas/RutasIndividuales/actualizarTipoPago.routes.js', + + './Pedidos/Rutas/RutasIndividuales/eliminarPedidos.routes.js', + './Pedidos/Rutas/RutasIndividuales/obtenerPedidos.routes.js', + + './Productos/Rutas/RutasIndividuales/consultarProductos.routes.js', + './Productos/Rutas/RutasIndividuales/crearProducto.routes.js', + './Productos/Rutas/RutasIndividuales/eliminarProducto.routes.js', + + './Roles/Rutas/RutasIndividuales/consultarLista.routes.js', + './Roles/Rutas/RutasIndividuales/crearRol.routes.js', + './Roles/Rutas/RutasIndividuales/eliminarRol.routes.js', + './Roles/Rutas/RutasIndividuales/obtenerOpcionesRol.routes.js', + + './SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js', + './SetsProductos/Rutas/RutasIndividuales/eliminarSetsProductos.routes.js', + './SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js', + + './Usuarios/Rutas/RutasIndividuales/consultarListaUsuarios.routes.js', + './Usuarios/Rutas/RutasIndividuales/crearUsuario.routes.js', + './Usuarios/Rutas/RutasIndividuales/eliminarUsuario.routes.js', + './Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js', +]; diff --git a/Configuracion/swagger.js b/Configuracion/swagger.js new file mode 100644 index 00000000..a0ac74ad --- /dev/null +++ b/Configuracion/swagger.js @@ -0,0 +1,21 @@ +const rutas = require('@altertex/config/rutasSwagger'); + +const opcionesSwagger = { + definition: { + openapi: '3.0.0', + info: { + title: 'API de TEXT&LINES', + version: '1.0.0', + description: 'Documentación de la API para TEXT&LINES', + }, + servers: [ + { + url: process.env.LOCAL_URL_BACKEND || 'http://localhost:5000', + }, + ], + }, + + apis: rutas, +}; + +module.exports = opcionesSwagger; diff --git a/Cuotas/Controladores/actualizarSetCuotas.controller.js b/Cuotas/Controladores/actualizarSetCuotas.controller.js new file mode 100644 index 00000000..7c5ae1da --- /dev/null +++ b/Cuotas/Controladores/actualizarSetCuotas.controller.js @@ -0,0 +1,24 @@ +const repositorio = require('@altertex/cuota/repos/actualizarSetCuotasRepositorio'); + +/** + * Controlador que gestiona la actualización de un set de cuotas. + * + * @param {object} req - Objeto de solicitud HTTP de Express. + * @param {object} res - Objeto de respuesta HTTP de Express. + * @returns {object} Respuesta JSON con el resultado de la operación. + */ +exports.actualizarSetCuotas = async (req, res) => { + try { + const { idCuotaSet, cambios } = req.body; + + if (!idCuotaSet || !cambios) { + return res.status(400).json({ mensaje: 'Datos incompletos' }); + } + + await repositorio.actualizarSetCuotas(idCuotaSet, cambios); + return res.status(200).json({ mensaje: 'Set de cuotas actualizado correctamente' }); + + } catch (error) { + return res.status(500).json({ mensaje: error.message }); + } +}; diff --git a/Cuotas/Controladores/consultarListasCuotas.controller.js b/Cuotas/Controladores/consultarListasCuotas.controller.js new file mode 100644 index 00000000..bb89bb37 --- /dev/null +++ b/Cuotas/Controladores/consultarListasCuotas.controller.js @@ -0,0 +1,49 @@ +const repositorio = require('@altertex/cuota/repos/cuotasRepositorio'); +const MENSAJES_CUOTAS = require('@altertex/util/const/mensajesCuotas'); + +/** + * Controlador para consultar la lista de sets de cuotas. + * + * RF32 - Consulta Lista de Sets de Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF32 + * + * @async + * @function consultarLista + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.user - Datos del usuario autenticado. + * @param {number} req.user.clienteSeleccionado - ID del cliente autenticado. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con: + * - 200 si la consulta fue exitosa. + * - 204 si no hay resultados. + * - 400 si falta el ID del cliente. + * - 500 si hay error en el servidor. + */ +exports.consultarLista = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + + if (!idCliente) { + return res + .status(MENSAJES_CUOTAS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_CUOTAS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const resultados = await repositorio.obtenerCuotas(idCliente); + + if (!resultados || resultados.length === 0) { + return res + .status(MENSAJES_CUOTAS.SIN_RESULTADOS.codigo) + .json({ mensaje: MENSAJES_CUOTAS.SIN_RESULTADOS.mensaje }); + } + + return res.status(MENSAJES_CUOTAS.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_CUOTAS.CONSULTA_EXITOSA.mensaje, + cuotas: resultados, + }); + } catch { + return res + .status(MENSAJES_CUOTAS.ERROR_CONSULTAR_CUOTAS.codigo) + .json({ mensaje: MENSAJES_CUOTAS.ERROR_CONSULTAR_CUOTAS.mensaje }); + } +}; diff --git a/Cuotas/Controladores/crearCuota.controller.js b/Cuotas/Controladores/crearCuota.controller.js new file mode 100644 index 00000000..22569040 --- /dev/null +++ b/Cuotas/Controladores/crearCuota.controller.js @@ -0,0 +1,69 @@ +const { validarCuotaSet } = require('@altertex/cuota/ctrl/validarCuotaSet'); +const repositorio = require('@altertex/cuota/repos/crearCuotaRepositorio'); +const MENSAJES = require('@altertex/util/const/mensajesCuotas'); + +/** + * RF31 - Crear Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF31 + * + * Controlador para crear un nuevo conjunto de cuotas (cuotaSet) desde el frontend. + * + * - Valida que el cuerpo de la solicitud tenga el formato correcto + * - Valida nombre y productos usando la función `validarCuotaSet` + * - Agrega automáticamente la fecha de creación y el ID del cliente autenticado + * - Llama al repositorio para persistir en la base de datos + * + * @function crearCuota + * @param {Express.Request} req - Objeto de solicitud HTTP + * @param {Express.Response} res - Objeto de respuesta HTTP + * + * @returns {Response} Respuesta HTTP con estado 201 si fue exitoso o 400 si falló + */ +exports.crearCuota = async (req, res) => { + const cuotaSetModelo = req.body; + + try { + if (!cuotaSetModelo || typeof cuotaSetModelo !== 'object') { + return res.status(400).json({ error: MENSAJES.FORMATO_INVALIDO }); + } + + if (!cuotaSetModelo.nombre || cuotaSetModelo.nombre.trim() === '') { + return res.status(400).json({ error: MENSAJES.NOMBRE_OBLIGATORIO }); + } + + let errorValidacion = null; + try { + errorValidacion = validarCuotaSet( + cuotaSetModelo.nombre, + cuotaSetModelo.productosYLimite, + res + ); + } catch (err) { + return res.status(400).json({ + error: 'Error creando cuota set', + detalle: err.message || err, + }); + } + + if (errorValidacion) { + return res.status(400).json({ + error: errorValidacion, + contexto: 'Error al validar los datos del set de cuotas. Verifica los campos enviados.', + }); + } + + const hoy = new Date(); + const fechaFormateada = hoy.toISOString().split('T')[0]; + + cuotaSetModelo.ultimaActualizacion = fechaFormateada; + cuotaSetModelo.idCliente = req.user.clienteSeleccionado; + + await repositorio.crearCuota(cuotaSetModelo); + + return res.status(201).json({ exito: MENSAJES.CREACION_EXITOSA }); + } catch (error) { + return res.status(400).json({ + error: MENSAJES.ERROR_CREACION, + detalle: error.message || error, + }); + } +}; diff --git a/Cuotas/Controladores/eliminarSetCuotas.controller.js b/Cuotas/Controladores/eliminarSetCuotas.controller.js new file mode 100644 index 00000000..0cbeff99 --- /dev/null +++ b/Cuotas/Controladores/eliminarSetCuotas.controller.js @@ -0,0 +1,49 @@ +const repositorio = require('@altertex/cuota/repos/eliminarSetCuotasRepositorio'); +const MENSAJES_SET_CUOTAS = require('@altertex/util/const/mensajesCuotas'); + +/** + * Controlador para eliminar uno o varios sets de cuotas. + * + * RF[35] - Super Administrador elimina Set de Cuotas + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF35 + * + * @async + * @function eliminarSetCuotas + * @param {object} req - Objeto de solicitud HTTP. + * @param {object} req.body - Cuerpo de la solicitud, debe incluir un arreglo `idsSetCuotas`. + * @param {object} res - Objeto de respuesta HTTP. + * + * @returns {Response} Devuelve un estado: + * - 200 si la eliminación fue exitosa. + * - 404 si no se encontraron IDs válidos. + * - 500 si ocurrió un error interno. + */ +exports.eliminarSetCuotas = async (req, res) => { + try { + const idsSetCuotas = req.body.idsSetCuotas; + + if (!Array.isArray(idsSetCuotas) || idsSetCuotas.length === 0) { + return res.status(MENSAJES_SET_CUOTAS.SET_CUOTA_NO_ENCONTRADO.codigo).json({ + mensaje: MENSAJES_SET_CUOTAS.SET_CUOTA_NO_ENCONTRADO.mensaje, + }); + } + + await Promise.all( + idsSetCuotas.map(async (idSetCuota) => { + const resultadoSetCuotas = await repositorio.eliminarSetCuotas(idSetCuota); + + if (resultadoSetCuotas.affectedRows === 0) { + throw new Error(`Set de cuotas con ID ${idSetCuota} no encontrado`); + } + }) + ); + + return res.status(MENSAJES_SET_CUOTAS.SET_CUOTA_ELIMINADO.codigo).json({ + mensaje: MENSAJES_SET_CUOTAS.SET_CUOTA_ELIMINADO.mensaje, + }); + } catch { + return res.status(MENSAJES_SET_CUOTAS.ERROR_ELIMINAR_SET_CUOTAS.codigo).json({ + mensaje: MENSAJES_SET_CUOTAS.ERROR_ELIMINAR_SET_CUOTAS.mensaje, + }); + } +}; diff --git a/Cuotas/Controladores/leerSetCuotas.controller.js b/Cuotas/Controladores/leerSetCuotas.controller.js new file mode 100644 index 00000000..9c33418d --- /dev/null +++ b/Cuotas/Controladores/leerSetCuotas.controller.js @@ -0,0 +1,44 @@ +const repositorio = require('@altertex/cuota/repos/leerSetCuotasRepositorio'); +const MENSAJES_CUOTAS = require('@altertex/util/const/mensajesCuotas'); + +/** + * Lee un conjunto de cuotas desde la base de datos utilizando su ID. + * + * Valida el parámetro `idSetCuota` y obtiene la información del set de cuotas a través del repositorio. + * Si el set de cuotas no es encontrado o el parámetro es inválido, retorna un error. + * + * @param {Express.Request} req - La solicitud HTTP que contiene el `idSetCuota` en el cuerpo. + * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente. + * @returns {Promise} Responde con el set de cuotas encontrado o un mensaje de error. + * + * @see [RF33] Leer set cuotas(https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF33) + */ +exports.leerSetCuotas = async (req, res) => { + const idSetCuota = parseInt(req.body.idSetCuota); + + if (isNaN(idSetCuota)) { + return res + .status(MENSAJES_CUOTAS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_CUOTAS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const setCuota = await repositorio.obtenerSetCuotaPorId(idSetCuota); + + if (!setCuota) { + return res + .status(MENSAJES_CUOTAS.SET_CUOTA_NO_ENCONTRADO.codigo) + .json({ mensaje: MENSAJES_CUOTAS.SET_CUOTA_NO_ENCONTRADO.mensaje }); + } + + return res.status(MENSAJES_CUOTAS.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_CUOTAS.CONSULTA_EXITOSA.mensaje, + setCuota, + }); + } catch (error) { + console.error('Error al consultar Set cuotas:', error); + return res + .status(MENSAJES_CUOTAS.ERROR_OBTENER_SET_CUOTA.codigo) + .json({ mensaje: MENSAJES_CUOTAS.ERROR_OBTENER_SET_CUOTA.mensaje }); + } +}; diff --git a/Cuotas/Controladores/obtenerOpcionesCuotas.controller.js b/Cuotas/Controladores/obtenerOpcionesCuotas.controller.js new file mode 100644 index 00000000..835fe72b --- /dev/null +++ b/Cuotas/Controladores/obtenerOpcionesCuotas.controller.js @@ -0,0 +1,28 @@ +const repositorio = require('@altertex/cuota/repos/obtenerOpcionesCuotasRepositorio'); +const MENSAJES = require('@altertex/util/const/mensajesCuotas'); + +/** + * Obtiene las opciones de cuota disponibles para un cliente. + * + * Este controlador se encarga de recibir la solicitud para obtener las opciones de cuota para un cliente, + * verificando primero que el `idCliente` esté presente en la solicitud. Luego, llama a un repositorio para + * obtener las opciones de cuota. Si ocurre un error o falta el `idCliente`, se responde con un mensaje de error. + * + * @param {object} req - El objeto de solicitud que contiene el `idCliente` en su cuerpo. + * @param {object} res - El objeto de respuesta para enviar el resultado o el error. + * @returns {Promise} No devuelve valor explícito. Envía una respuesta con el estado adecuado. + */ +exports.obtenerOpcionesCuotas = async (req, res) => { + try { + const idCliente = req.body.clienteSeleccionado; + if (!idCliente) { + return res.status(400).json({ mensaje: MENSAJES.FALTA_ID_CLIENTE }); + } + + const resultado = await repositorio.obtenerCuotaOpcion(idCliente); + + return res.status(201).json({ mensaje: MENSAJES.OPCIONES_OBTENIDAS, resultado }); + } catch (error) { + return res.status(400).json({ mensaje: MENSAJES.ERROR_OBTENIENDO_OPCIONES, error }); + } +}; diff --git a/Cuotas/Controladores/validarCuotaSet.js b/Cuotas/Controladores/validarCuotaSet.js new file mode 100644 index 00000000..8e82e4b6 --- /dev/null +++ b/Cuotas/Controladores/validarCuotaSet.js @@ -0,0 +1,70 @@ +/** + * + * RF31 - Crear Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF31 + * + * Valida los datos de un conjunto de cuotas (cuotaSet), asegurando que el nombre + * y la lista de productos con sus respectivos límites sean válidos. + * + * Esta función lanza errores HTTP utilizando `res.status().json()` si detecta + * algún problema en los datos recibidos. + * + * @function validarCuotaSet + * @param {string} nombre - Nombre del conjunto de cuotas. + * @param {Array} productosYLimite - Lista de productos con sus límites. + * @param {string} productosYLimite[].idProducto - ID del producto. + * @param {number} productosYLimite[].limite - Límite máximo asignado al producto. + * @param {number} productosYLimite[].limiteActual - Límite actual usado del producto. + * @returns {void} + * + * @throws Retorna una respuesta HTTP 400 si: + * - El nombre es inválido. + * - La lista de productos está vacía o mal formada. + * - Algún producto tiene campos inválidos. + * + * @note Esta función depende implícitamente de `res`, pero no se pasa como parámetro. + * Para que sea reutilizable, se recomienda lanzar errores o retornar un objeto de error en lugar de usar `res` directamente. + */ +const MENSAJES = require('@altertex/util/const/mensajesCuotas'); + +function validarCuotaSet(nombre, productosYLimite) { + if (!nombre || typeof nombre !== 'string' || nombre.trim() === '') { + return MENSAJES.NOMBRE_REQUERIDO; + } + + if (!Array.isArray(productosYLimite) || productosYLimite.length === 0) { + return MENSAJES.PRODUCTOS_REQUERIDOS; + } + + for (let iterador = 0; iterador < productosYLimite.length; iterador += 1) { + const { idProducto, limite, limiteActual } = productosYLimite[iterador]; + + if (!idProducto || typeof idProducto !== 'string' || idProducto.trim() === '') { + return MENSAJES.ID_PRODUCTO_INVALIDO(iterador); + } + + // Rechazar strings numéricos con ceros a la izquierda + if ( + (typeof limite === 'string' && /^0[0-9]+$/.test(limite)) + || (typeof limiteActual === 'string' && /^0[0-9]+$/.test(limiteActual)) + ) { + return 'No se permiten ceros a la izquierda en los valores de cuota.'; + } + + if (typeof limite !== 'number' || isNaN(limite) || !Number.isInteger(limite) || limite <= 0) { + return MENSAJES.LIMITE_INVALIDO(idProducto); + } + + if ( + typeof limiteActual !== 'number' + || isNaN(limiteActual) + || !Number.isInteger(limiteActual) + || limiteActual <= 0 + ) { + return MENSAJES.LIMITE_ACTUAL_INVALIDO(idProducto); + } + } + + return null; +} + +module.exports = { validarCuotaSet }; diff --git a/Cuotas/Datos/Repositorios/actualizarSetCuotasRepositorio.js b/Cuotas/Datos/Repositorios/actualizarSetCuotasRepositorio.js new file mode 100644 index 00000000..158b6a66 --- /dev/null +++ b/Cuotas/Datos/Repositorios/actualizarSetCuotasRepositorio.js @@ -0,0 +1,44 @@ +const CONSULTAS_CUOTAS = require('@altertex/util/const/consultasCuotas'); +const correrQuery = require('@altertex/util/ser/correrQuery'); + +/** + * Actualiza un set de cuotas en la base de datos. + * + * @async + * @param {number} idCuotaSet - ID del set de cuotas a actualizar. + * @param {object} cambios - Objeto con los cambios a aplicar. + * @param {string} cambios.nombre - Nombre actualizado del set de cuotas. + * @param {string} cambios.descripcion - Descripción del set de cuotas. + * @param {number} cambios.periodoRenovacion - Periodo de renovación en meses. + * @param {boolean} cambios.renovacionHabilitada - Si la renovación está habilitada. + * @param {Array} [cambios.productos=[]] - Lista de productos con límites. + * @param {number} cambios.productos[].idProducto - ID del producto asociado. + * @param {number} cambios.productos[].limite - Límite asignado al producto. + * @param {number} cambios.productos[].limiteActual - Límite actual del producto. + * @throws {Error} Si ocurre un error en la consulta a la base de datos. + */ +exports.actualizarSetCuotas = async (idCuotaSet, cambios) => { + const { nombre, descripcion, periodoRenovacion, renovacionHabilitada, productos = [] } = cambios; + + await correrQuery(CONSULTAS_CUOTAS.ACTUALIZAR_CUOTA_SET, [ + nombre, + descripcion, + periodoRenovacion, + renovacionHabilitada, + new Date(), + idCuotaSet + ]); + + await correrQuery(CONSULTAS_CUOTAS.ELIMINAR_PRODUCTOS_CUOTA_SET, [idCuotaSet]); + + for (const producto of productos) { + if (producto.idProducto && producto.idProducto > 0) { + await correrQuery(CONSULTAS_CUOTAS.INSERTAR_CUOTA_PRODUCTO_ACTUALIZAR, [ + idCuotaSet, + producto.idProducto, + producto.limite || 0, + producto.limiteActual || 0 + ]); + } + } +}; diff --git a/Cuotas/Datos/Repositorios/crearCuotaRepositorio.js b/Cuotas/Datos/Repositorios/crearCuotaRepositorio.js new file mode 100644 index 00000000..c54e811c --- /dev/null +++ b/Cuotas/Datos/Repositorios/crearCuotaRepositorio.js @@ -0,0 +1,130 @@ +const db = require('@altertex/util/bd/db'); +const QUERY = require('@altertex/util/const/consultasCuotas'); + +/** + * RF31 - Crear Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF31 + * + * Crea un nuevo conjunto de cuotas (cuotaSet) en la base de datos. + * + * Esta función realiza una transacción que: + * - Inserta un nuevo registro en la tabla de cuotas (cuotaSet) + * - Asocia los productos con sus respectivos límites + * - Si algún producto tiene un `idProducto` no numérico, se busca en la base de datos + * - Si ocurre algún error, la transacción se revierte automáticamente + * + * @async + * @function crearCuota + * @param {object} data - Objeto que contiene la información del cuotaSet. + * @param {number} data.idCliente - ID del cliente al que pertenece el cuotaSet. + * @param {string} data.nombre - Nombre del conjunto de cuotas. + * @param {string} data.descripcion - Descripción del conjunto de cuotas. + * @param {string} data.periodoRenovacion - Periodo de renovación (e.g., "mensual"). + * @param {boolean} data.renovacionHabilitada - Si la renovación automática está habilitada. + * @param {Array} data.productosYLimite - Arreglo de productos con sus límites. + * @param {string|number} data.productosYLimite[].idProducto - ID numérico o código del producto. + * @param {number} data.productosYLimite[].limite - Límite máximo asignado. + * @param {number} data.productosYLimite[].limiteActual - Límite actual utilizado. + * @param {string} data.ultimaActualizacion - Fecha en formato YYYY-MM-DD. + * + * @returns {Promise} ID del nuevo cuotaSet creado. + * + * @throws {Error} Si faltan parámetros requeridos o falla la transacción. + */ +exports.crearCuota = async (data) => { + // Validaciones iniciales + if ( + !data + || typeof data !== 'object' + || typeof data.idCliente !== 'number' + || typeof data.nombre !== 'string' + || typeof data.descripcion !== 'string' + || typeof data.periodoRenovacion !== 'number' + || typeof data.renovacionHabilitada !== 'boolean' + || !Array.isArray(data.productosYLimite) + || typeof data.ultimaActualizacion !== 'string' + ) { + throw new Error('Datos inválidos o incompletos para crear la cuota.'); + } + + // Validar estructura de cada producto + for (const item of data.productosYLimite) { + // Verifica si el valor original es string y tiene ceros a la izquierda + if ( + (typeof item.limite === 'string' && /^0[0-9]+$/.test(item.limite)) + || (typeof item.limiteActual === 'string' && /^0[0-9]+$/.test(item.limiteActual)) + ) { + throw new Error('No se permiten ceros a la izquierda en los valores de cuota.'); + } + if ( + !item + || (typeof item.idProducto !== 'string' && typeof item.idProducto !== 'number') + || typeof item.limite !== 'number' + || !Number.isInteger(item.limite) + || item.limite <= 0 + || typeof item.limiteActual !== 'number' + || !Number.isInteger(item.limiteActual) + || item.limiteActual <= 0 + ) { + throw new Error( + 'Cada producto debe tener un idProducto (string o number), limite (entero > 0) y limiteActual (entero > 0).' + ); + } + } + + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const { + nombre, + descripcion, + periodoRenovacion, + renovacionHabilitada, + productosYLimite, + ultimaActualizacion, + idCliente, + } = data; + + const [resultado] = await conexion.execute(QUERY.INSERTAR_CUOTA, [ + idCliente, + nombre, + descripcion, + periodoRenovacion, + renovacionHabilitada, + ultimaActualizacion, + ]); + + const cuotaSetId = resultado.insertId; + + for (const item of productosYLimite) { + let idProducto = item.idProducto; + + if (isNaN(idProducto)) { + const [rows] = await conexion.execute(QUERY.SELECCIONAR_PRODUCTO, [idProducto]); + + if (rows.length === 0) { + continue; + } + + idProducto = rows[0].idProducto; + } + + await conexion.execute(QUERY.INSERTAR_CUOTA_PRODUCTO, [ + cuotaSetId, + idProducto, + item.limite, + item.limiteActual, + ]); + } + + await conexion.commit(); + + return cuotaSetId; + } catch { + await conexion.rollback(); + throw new Error('Error creando cuota set'); + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Cuotas/Datos/Repositorios/cuotasRepositorio.js b/Cuotas/Datos/Repositorios/cuotasRepositorio.js new file mode 100644 index 00000000..0bca9b34 --- /dev/null +++ b/Cuotas/Datos/Repositorios/cuotasRepositorio.js @@ -0,0 +1,41 @@ +// Importación de la función utilitaria para ejecutar queries SQL hacia la base de datos. +const correrQuery = require('@altertex/util/ser/correrQuery'); + +// Importación del conjunto de consultas SQL relacionadas con la entidad "Cuotas". +const CONSULTAS_CUOTAS = require('@altertex/util/const/consultasCuotas'); + +/** + * Función encargada de obtener los sets de cuotas asociados a un cliente específico. + * RF32 - Consulta Lista de Sets de Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF32 + * @async + * @function obtenerCuotas + * @param {number} idCliente - Identificador único del cliente a consultar. + * @returns {Promise>} Retorna una lista de sets de cuotas si existen, + * o un array vacío si ocurre un error o no se encuentran resultados. + * + * @throws {Error} En caso de fallo en la ejecución de la consulta, se captura el error + * y se retorna una lista vacía. El error se registra en consola para facilitar depuración. + * + * @example + * const cuotas = await obtenerCuotas(42); + * console.log(cuotas); // [{ idCuota: 1, nombre: 'Mensualidad A', ... }, ...] + * + * @description + * Esta función forma parte del requerimiento funcional RF32 - "Consulta Lista de Sets de Cuotas". + * Realiza una consulta SQL utilizando `correrQuery`, pasando como parámetro el `idCliente`. + * Si no hay resultados o ocurre un error, retorna un array vacío como respuesta segura por defecto. + */ +exports.obtenerCuotas = async (idCliente) => { + try { + // Ejecuta la consulta SQL con el ID del cliente como parámetro. + const resultado = await correrQuery(CONSULTAS_CUOTAS.OBTENER_CUOTAS, [idCliente]); + + // Retorna los resultados o un arreglo vacío si no hay datos. + return resultado || []; + } catch { + // Registra el error en consola para diagnóstico. + + // Devuelve un array vacío en caso de error para evitar ruptura del flujo. + return []; + } +}; diff --git a/Cuotas/Datos/Repositorios/eliminarSetCuotasRepositorio.js b/Cuotas/Datos/Repositorios/eliminarSetCuotasRepositorio.js new file mode 100644 index 00000000..3879ce7d --- /dev/null +++ b/Cuotas/Datos/Repositorios/eliminarSetCuotasRepositorio.js @@ -0,0 +1,46 @@ +const db = require('@altertex/util/bd/db'); +const CONSULTAS_CUOTAS = require('@altertex/util/const/consultasCuotas'); + +/** + * Elimina un set de cuotas y sus relaciones con productos desde la base de datos. + * + * @async + * @function eliminarSetCuotas + * @param {number} idSetCuotas - ID del set de cuotas a eliminar. + * @returns {Promise} Objeto con mensaje de éxito y resultados de las operaciones SQL. + * @throws {Error} Si ocurre un error durante la transacción. + */ +exports.eliminarSetCuotas = async (idSetCuotas) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const [resultadoProductosSetCuotas] = await conexion.query( + CONSULTAS_CUOTAS.ELIMINAR_CUOTA_SET_PRODUCTO, + [idSetCuotas] + ); + + const [resultadoSetCuotas] = await conexion.query( + CONSULTAS_CUOTAS.ELIMINAR_CUOTA_SET, + [idSetCuotas] + ); + + if (resultadoSetCuotas.affectedRows === 0) { + throw new Error(`Set de cuotas con ID ${idSetCuotas} no encontrado`); + } + + await conexion.commit(); + + return { + mensaje: 'Set de cuotas eliminado correctamente', + resultadoProductosSetCuotas, + resultadoSetCuotas, + }; + } catch { + await conexion.rollback(); + throw new Error('Error eliminando set de cuotas'); + } finally { + conexion.release(); + } +}; \ No newline at end of file diff --git a/Cuotas/Datos/Repositorios/leerSetCuotasRepositorio.js b/Cuotas/Datos/Repositorios/leerSetCuotasRepositorio.js new file mode 100644 index 00000000..b939a7b6 --- /dev/null +++ b/Cuotas/Datos/Repositorios/leerSetCuotasRepositorio.js @@ -0,0 +1,55 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_CUOTAS = require('@altertex/util/const/consultasCuotas'); + +/** + * Obtiene un conjunto de cuotas desde la base de datos mediante su ID. + * + * Ejecuta una consulta SQL y retorna el primer conjunto de cuotas encontrado o `null` si no existe. + * + * @param {number} idSetCuota - ID del set de cuotas a buscar. + * @returns {Promise} El conjunto de cuotas encontrado o `null` si no existe. + * @throws {Error} Si ocurre un error al ejecutar la consulta. + */ +exports.obtenerSetCuotaPorId = async (idSetCuota) => { + + const query = CONSULTAS_CUOTAS.LEER_CUOTA_SET; + const queryCuotas = CONSULTAS_CUOTAS.LEER_CUOTA_SET_PRODUCTOS; + + const resultado = await correrQuery(query, [idSetCuota]); + + if (resultado.length === 0) return null; + + const productosCuota = await correrQuery(queryCuotas, [idSetCuota]); + + const productos = productosCuota.map((producto) => ({ + idProducto: producto.idProducto, + nombre: producto.nombreComun, + nombreComun: producto.nombreComun, + cuota_valor: producto.cuota_valor, + limite_actual: producto.limite_actual, + valor: producto.cuota_valor, + limite: producto.cuota_valor, + limiteActual: producto.limite_actual + })); + + const cuotas = productosCuota.map((producto) => ({ + valor: producto.cuota_valor, + })); + + const setCuota = { + idSetCuota: resultado[0].idCuotaSet, + idCuotaSet: resultado[0].idCuotaSet, + + nombre: resultado[0].nombre, + descripcion: resultado[0].descripcion, + + periodoRenovacion: resultado[0].periodoRenovacion, + renovacionHabilitada: resultado[0].renovacionHabilitada, + ultimaActualizacion: resultado[0].ultimaActualizacion, + productos, + cuotas, + }; + + + return setCuota; +}; \ No newline at end of file diff --git a/Cuotas/Datos/Repositorios/obtenerOpcionesCuotasRepositorio.js b/Cuotas/Datos/Repositorios/obtenerOpcionesCuotasRepositorio.js new file mode 100644 index 00000000..dbaf7703 --- /dev/null +++ b/Cuotas/Datos/Repositorios/obtenerOpcionesCuotasRepositorio.js @@ -0,0 +1,21 @@ +const QUERY = require('@altertex/util/const/consultasCuotas'); +const correrQuery = require('@altertex/util/ser/correrQuery'); + +/** + * Obtiene las opciones de cuota disponibles para un cliente específico. + * + * Realiza una consulta a la base de datos usando el identificador de cliente (`idCliente`) para obtener las opciones de cuota + * asociadas a ese cliente. Si ocurre un error durante la ejecución de la consulta, se captura y se lanza un nuevo error. + * + * @param {number} idCliente - El ID del cliente para el cual se obtienen las opciones de cuota. + * @returns {Promise} Una promesa que resuelve con las opciones de cuota obtenidas de la base de datos. + * @throws {Error} Si ocurre un error durante la ejecución de la consulta. + */ +exports.obtenerCuotaOpcion = async (idCliente) => { + try { + const resultado = await correrQuery(QUERY.OBTENER_OPCIONES, [idCliente]); + return resultado; + } catch { + throw new Error('Error obteniendo opciones'); + } +}; diff --git a/Cuotas/Rutas/RutasIndividuales/actualizarSetCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/actualizarSetCuotas.routes.js new file mode 100644 index 00000000..65860635 --- /dev/null +++ b/Cuotas/Rutas/RutasIndividuales/actualizarSetCuotas.routes.js @@ -0,0 +1,37 @@ +/** + * Ruta para actualizar un set de cuotas. + * + * Método: PUT + * Ruta: /api/cuotas/actualizar-set-cuotas (o la que defina `RUTAS.CUOTAS.ACTUALIZAR_SET_CUOTAS`) + * + * Middleware aplicados: + * - revisión de API Key + * - autorización por token JWT + * - verificación de permisos de usuario + * + * Permiso requerido: PERMISOS.ACTUALIZAR_SET_CUOTAS + * + * @module actualizarSetCuotas.routes + */ + + + +const express = require('express'); +const controlador = require('@altertex/cuota/ctrl/actualizarSetCuotas.controller'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const PERMISOS = require('@altertex/util/const/permisos'); + +const ruteador = express.Router(); + +ruteador.put( + RUTAS.CUOTAS.ACTUALIZAR_SET_CUOTAS, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.ACTUALIZAR_SET_CUOTAS), + controlador.actualizarSetCuotas +); + +module.exports = ruteador; diff --git a/Cuotas/Rutas/RutasIndividuales/consultarCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/consultarCuotas.routes.js new file mode 100644 index 00000000..11d420c4 --- /dev/null +++ b/Cuotas/Rutas/RutasIndividuales/consultarCuotas.routes.js @@ -0,0 +1,115 @@ +// Importación del framework Express para la creación de rutas HTTP. +const express = require('express'); + +// Creación de una instancia del enrutador de Express. +const ruteador = express.Router(); + +// Importación del controlador encargado de consultar la lista de sets de cuotas. +const controlador = require('@altertex/cuota/ctrl/consultarListasCuotas.controller'); + +// Importación de middlewares de seguridad para validar API Key, token JWT y permisos de usuario. +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +// Importación de constantes de permisos y rutas del sistema. +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF32 - Consulta Lista de Sets de Cuotas + * Documentación del requisito funcional: + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF32 + */ + +/** + * @swagger + * /api/cuotas/consultar-lista: + * post: + * summary: Consulta la lista de sets de cuotas del cliente autenticado. + * tags: + * - Cuotas + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Consulta exitosa de sets de cuotas. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Consulta de cuotas exitosa. + * token: + * type: string + * example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + * 400: + * description: Error en la solicitud. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Error en la solicitud. + * 401: + * description: Credenciales inválidas o token no autorizado. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Credenciales inválidas, no tiene el permiso necesario. + * 403: + * description: No tiene el permiso necesario para realizar esta acción. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: No tiene el permiso necesario. + * 404: + * description: No se encontraron sets de cuotas para el cliente. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: No se encontraron resultados. + * 500: + * description: Error interno al obtener los sets de cuotas. + * content: + * application/json: + * schema: + * type: object + * properties: + * message: + * type: string + * example: Error al obtener los sets de cuotas. + */ + +// Definición de la ruta POST para consultar la lista de sets de cuotas. +// Protegida por validación de API Key, autenticación de token y verificación de permisos. +ruteador.post( + RUTAS.CUOTAS.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_SETS_CUOTAS), + controlador.consultarLista, +); + +// Exporta el enrutador para que pueda ser utilizado por la aplicación principal. +module.exports = ruteador; \ No newline at end of file diff --git a/Cuotas/Rutas/RutasIndividuales/crearCuota.routes.js b/Cuotas/Rutas/RutasIndividuales/crearCuota.routes.js new file mode 100644 index 00000000..d3565108 --- /dev/null +++ b/Cuotas/Rutas/RutasIndividuales/crearCuota.routes.js @@ -0,0 +1,109 @@ +const express = require("express"); +const ruteador = express.Router(); +const controlador = require("@altertex/cuota/ctrl/crearCuota.controller"); + +const RUTAS = require("@altertex/util/const/rutas"); +const validarYSanitizar = require("@altertex/util/inter/validarYSanitizar"); +const revisarApiKey = require("@altertex/util/inter/revisarApiKey"); +const autorizarToken = require("@altertex/util/inter/autorizarToken"); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +/** + * + * RF31 - Crear Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF31 + * + * @swagger + * /api/cuotas/crear-cuota: + * post: + * summary: Crea un nuevo conjunto de cuotas (cuotaSet) + * tags: + * - Cuotas + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idCliente + * - nombre + * - descripcion + * - periodoRenovacion + * - renovacionHabilitada + * - productosYLimite + * - ultimaActualizacion + * properties: + * idCliente: + * type: integer + * example: 102 + * nombre: + * type: string + * example: "Cuota Abril" + * descripcion: + * type: string + * example: "Límites para el mes de abril" + * periodoRenovacion: + * type: string + * example: "mensual" + * renovacionHabilitada: + * type: boolean + * example: true + * ultimaActualizacion: + * type: string + * format: date + * example: "2025-04-19" + * productosYLimite: + * type: array + * items: + * type: object + * required: + * - idProducto + * - limite + * - limiteActual + * properties: + * idProducto: + * type: string + * example: "PROD001" + * limite: + * type: number + * example: 100 + * limiteActual: + * type: number + * example: 0 + * responses: + * 201: + * description: Cuota set creado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * exito: + * type: string + * example: Cuota set creado exitosamente + * resultado: + * type: object + * 400: + * description: Error de validación o al crear el cuota set + * content: + * application/json: + * schema: + * type: object + * properties: + * error: + * type: string + * example: Error creando cuota set + */ +ruteador.post( + RUTAS.CUOTAS.AGREGAR, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + controlador.crearCuota +); + +module.exports = ruteador; diff --git a/Cuotas/Rutas/RutasIndividuales/eliminarSetCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/eliminarSetCuotas.routes.js new file mode 100644 index 00000000..4cf89899 --- /dev/null +++ b/Cuotas/Rutas/RutasIndividuales/eliminarSetCuotas.routes.js @@ -0,0 +1,80 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cuota/ctrl/eliminarSetCuotas.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +/** + * RF35 - Elimina Set de Cuotas - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF35 + */ + +/** + * @swagger + * /api/cuotas/eliminar-set-cuotas: + * delete: + * summary: Elimina uno o varios sets de cuotas + * description: Este endpoint permite eliminar múltiples sets de cuotas dados sus IDs. + * tags: [Cuotas] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idsSetCuotas: + * type: array + * description: IDs de los sets de cuotas a eliminar + * items: + * type: integer + * example: [1, 2, 3] + * responses: + * 200: + * description: Sets de cuotas eliminados correctamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Sets de cuotas eliminados correctamente." + * 400: + * description: No se proporcionaron IDs válidos + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Set de cuota no encontrado." + * 500: + * description: Error en el servidor al intentar eliminar + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al eliminar set de cuotas." + */ + +ruteador.post( + RUTAS.CUOTAS.ELIMINAR_SET_CUOTAS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_SET_CUOTAS), + controlador.eliminarSetCuotas +); + +module.exports = ruteador; diff --git a/Cuotas/Rutas/RutasIndividuales/leerSetCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/leerSetCuotas.routes.js new file mode 100644 index 00000000..e5481465 --- /dev/null +++ b/Cuotas/Rutas/RutasIndividuales/leerSetCuotas.routes.js @@ -0,0 +1,67 @@ +//RF33 - LEER COUTA SET - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF33] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cuota/ctrl/leerSetCuotas.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/cuotas/leer-set-cuotas: + * post: + * summary: Leer set de cuotas + * description: Obtiene el set de cuotas según los parámetros enviados. + * tags: + * - Cuotas + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idSet: + * type: string + * description: ID del set de cuotas a consultar + * required: + * - idSet + * responses: + * '200': + * description: Set de cuotas obtenido correctamente + * content: + * application/json: + * schema: + * type: object + * properties: + * cuotas: + * type: array + * items: + * type: object + * # Define los campos de cada cuota aquí + * '401': + * description: No autorizado (API Key o Token inválido) + * '403': + * description: Permisos insuficientes + * '400': + * description: Error de validación o parámetros incorrectos + * '500': + * description: Error interno del servidor + */ + +ruteador.post( + RUTAS.CUOTAS.LEER_SET_CUOTAS, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.LEER_SET_CUOTAS), + controlador.leerSetCuotas +); + +module.exports = ruteador; diff --git a/Cuotas/Rutas/RutasIndividuales/obtenerOpcionesCuotas.routes.js b/Cuotas/Rutas/RutasIndividuales/obtenerOpcionesCuotas.routes.js new file mode 100644 index 00000000..ead60383 --- /dev/null +++ b/Cuotas/Rutas/RutasIndividuales/obtenerOpcionesCuotas.routes.js @@ -0,0 +1,19 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/cuota/ctrl/obtenerOpcionesCuotas.controller'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const RUTAS = require('@altertex/util/const/rutas'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); + +ruteador.post( + RUTAS.CUOTAS.OPCIONES, + autorizarToken, + limitePeticionesDiarias, + revisarApiKey(), + controlador.obtenerOpcionesCuotas, +); + +module.exports = ruteador; diff --git a/Cuotas/Rutas/indexCuotas.routes.js b/Cuotas/Rutas/indexCuotas.routes.js new file mode 100644 index 00000000..0c4056a8 --- /dev/null +++ b/Cuotas/Rutas/indexCuotas.routes.js @@ -0,0 +1,21 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutaCrearCuota = require('@altertex/cuota/rutasInd/crearCuota.routes'); +const rutaObtenerOpcionesCuota = require('@altertex/cuota/rutasInd/obtenerOpcionesCuotas.routes'); +const rutasConsultarListaCuotas = require('@altertex/cuota/rutasInd/consultarCuotas.routes'); +const rutaEliminarSetCuotas = require('@altertex/cuota/rutasInd/eliminarSetCuotas.routes'); +const rutaLeerCuota = require('@altertex/cuota/rutasInd/leerSetCuotas.routes'); +const rutaActualizarSetCuotas = require('@altertex/cuota/rutasInd/actualizarSetCuotas.routes'); + + +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.use(RUTAS.CUOTAS.BASE, rutaCrearCuota); +ruteador.use(RUTAS.CUOTAS.BASE, rutaObtenerOpcionesCuota); +ruteador.use(RUTAS.CUOTAS.BASE, rutasConsultarListaCuotas); +ruteador.use(RUTAS.CUOTAS.BASE, rutaEliminarSetCuotas); +ruteador.use(RUTAS.CUOTAS.BASE, rutaLeerCuota); +ruteador.use(RUTAS.CUOTAS.BASE, rutaActualizarSetCuotas); + + +module.exports = ruteador; diff --git a/Empleados/Controladores/actualizarEmpleado.controller.js b/Empleados/Controladores/actualizarEmpleado.controller.js new file mode 100644 index 00000000..ebd90016 --- /dev/null +++ b/Empleados/Controladores/actualizarEmpleado.controller.js @@ -0,0 +1,51 @@ +const MENSAJES = require('@altertex/util/const/mensajesEmpleados'); +const repositorio = require('@altertex/emp/repos/repositorioActualizarEmpleado'); +//RF[19] Actualizar empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19] + +/** + * Controlador para actualizar la información de un empleado. + * + * Este endpoint recibe un objeto con los cambios que se aplicarán + * sobre un empleado y usa su repositorio para hacer el cambio en la + * base de datos. + * + * @function actualizarEmpleado + * @async + * @param {Express.Request} req - Objeto de solicitud HTTP de Express. + * @param {object} req.body - Cuerpo de la solicitud. + * @param {object|Array} req.body.cambios - Información del empleado a actualizar. + * @param {Express.Response} res - Objecto de respuesta HTTP de Express. + * @returns {Promise} Retorna una respuesta JSON indicando éxito o un error. + */ +exports.actualizarEmpleado = async (req, res) => { + let datos; + + // Si no hay cambios + if (req.body.id || req.body.idEmpleado) { + datos = [req.body]; + } else if (req.body.cambios) { + // Si la información viene en el formato esperado (hay cambios) + datos = Array.isArray(req.body.cambios) ? req.body.cambios : [req.body.cambios]; + } else { + return res + .status(MENSAJES.ERROR_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.ERROR_ACTUALIZAR.mensaje }); + } + + if (!datos || datos.length === 0) { + return res + .status(MENSAJES.ERROR_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.ERROR_ACTUALIZAR.mensaje }); + } + + try { + await repositorio.actualizarEmpleado(datos); + return res + .status(MENSAJES.EXITO_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.EXITO_ACTUALIZAR.mensaje, datos }); + } catch { + return res + .status(MENSAJES.ERROR_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.ERROR_ACTUALIZAR.mensaje }); + } +}; diff --git a/Empleados/Controladores/actualizarGrupoEmpleado.controller.js b/Empleados/Controladores/actualizarGrupoEmpleado.controller.js new file mode 100644 index 00000000..3687ffcc --- /dev/null +++ b/Empleados/Controladores/actualizarGrupoEmpleado.controller.js @@ -0,0 +1,39 @@ +const MENSAJES = require('@altertex/util/const/mensajesGrupoEmpleados'); +const repositorio = require('@altertex/emp/repos/repositorioActualizarGrupo'); + +// RF[24] Actualiza grupo empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24] + +/** + * Controlador HTTP para actualizar un grupo de empleados. + * + * Valida que el cuerpo de la solicitud contenga los datos necesarios, + * y delega la lógica de actualización al repositorio correspondiente. + * + * Respuestas posibles: + * - 400 si faltan datos en el cuerpo de la solicitud. + * - 200 si el grupo se actualiza correctamente. + * - 500 si ocurre un error en el proceso de actualización. + * + * @async + * @function actualizarGrupoEmpleados + * @param {Express.Request} req - Objeto de solicitud HTTP de Express. + * @param {Express.Response} res - Objeto de respuesta HTTP de Express. + * @returns {Promise} La respuesta HTTP con el estado y mensaje correspondiente. + */ +exports.actualizarGrupoEmpleados = async (req, res) => { + const datosActualizacion = req.body; + if (!datosActualizacion || Object.keys(datosActualizacion).length === 0) { + return res + .status(MENSAJES.FORMATO_INVALIDO_DATOS.codigo) + .json({ mensaje: MENSAJES.FORMATO_INVALIDO_DATOS.mensaje }); + } + try { + await repositorio.actualizarGrupoEmpleados(datosActualizacion); + + return res + .status(MENSAJES.GRUPO_ACTUALIZADO.codigo) + .json({ mensaje: MENSAJES.GRUPO_ACTUALIZADO.mensaje }); + } catch (error) { + return res.status(MENSAJES.ERROR_ACTUALIZAR_GRUPOS.codigo).json({ mensaje: error.message }); + } +}; diff --git a/Empleados/Controladores/consultarLista.controller.js b/Empleados/Controladores/consultarLista.controller.js new file mode 100644 index 00000000..ed3c7972 --- /dev/null +++ b/Empleados/Controladores/consultarLista.controller.js @@ -0,0 +1,45 @@ +const repositorio = require('@altertex/emp/repos/repositorioEmpleados'); +const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesEmpleados'); + +/** + * Controlador para la consulta de la lista de empleados de un cliente. + * + * RF17 - Consulta Lista de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF17 + * + * @async + * @function consultarLista + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {object} req.user - Datos del usuario autenticado. + * @param {number} req.user.clienteSeleccionado - ID del cliente seleccionado para la consulta. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la consulta es exitosa, junto con los datos de los empleados. + * - 400 si los parámetros proporcionados son inválidos. + * - 500 si ocurre un error en el servidor. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.consultarLista = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + + if (!idCliente) { + return res + .status(MENSAJES_EMPLEADOS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_EMPLEADOS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const resultados = await repositorio.obtenerEmpleados(idCliente); + + return res.status(MENSAJES_EMPLEADOS.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.CONSULTA_EXITOSA.mensaje, + empleados: resultados, + }); + } catch { + return res.status(MENSAJES_EMPLEADOS.ERROR_CONSULTAR_EMPLEADOS.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.ERROR_CONSULTAR_EMPLEADOS.mensaje, + }); + } +}; diff --git a/Empleados/Controladores/consultarListaGrupos.controller.js b/Empleados/Controladores/consultarListaGrupos.controller.js new file mode 100644 index 00000000..55e5a8c1 --- /dev/null +++ b/Empleados/Controladores/consultarListaGrupos.controller.js @@ -0,0 +1,45 @@ +const repositorio = require('@altertex/emp/repos/repositorioGrupoDeEmpleados'); +const MENSAJES_GRUPO_EMPLEADOS = require('@altertex/util/const/mensajesGrupoEmpleados'); + +/** + * Controlador para la consulta de la lista de empleados de un cliente. + * + * RF22 - Consulta Lista de Grupo Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF22 + * + * @async + * @function consultarLista + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {object} req.user - Datos del usuario autenticado. + * @param {number} req.user.clienteSeleccionado - ID del cliente seleccionado para la consulta. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la consulta es exitosa, junto con los datos de los empleados. + * - 400 si los parámetros proporcionados son inválidos. + * - 500 si ocurre un error en el servidor. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.consultarLista = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + + if (!idCliente) { + return res + .status(MENSAJES_GRUPO_EMPLEADOS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_GRUPO_EMPLEADOS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const resultados = await repositorio.obtenerGrupoDeEmpleados(idCliente); + + return res.status(MENSAJES_GRUPO_EMPLEADOS.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.CONSULTA_EXITOSA.mensaje, + grupoEmpleados: resultados, + }); + } catch { + return res.status(MENSAJES_GRUPO_EMPLEADOS.ERROR_CONSULTAR_GRUPOS.codigo).json({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.ERROR_CONSULTAR_GRUPOS.mensaje, + }); + } +}; diff --git a/Empleados/Controladores/crearEmpleado.controller.js b/Empleados/Controladores/crearEmpleado.controller.js new file mode 100644 index 00000000..4ecb4a14 --- /dev/null +++ b/Empleados/Controladores/crearEmpleado.controller.js @@ -0,0 +1,172 @@ +const bcrypt = require('bcryptjs'); +const repositorio = require('@altertex/emp/repos/repositorioCrearEmpleado'); + +/** + * Controlador para crear un nuevo empleado. + * Este endpoint recibe un objeto con la información del nuevo empleado + * y usa su repositorio para insertar el nuevo registro en la base de datos. + * + * @function crearEmpleado + * @async + * @param {Array} req - Objeto de solicitud HTTP de Express. + * @param {Array} req.body - Cuerpo de la solicitud con los datos del nuevo empleado. + * @param {string} req.body[].nombreCompleto - Nombre completo del usuario. + * @param {string} req.body[].correoElectronico - Correo electrónico único del usuario. + * @param {string} req.body[].contrasenia - Contraseña en texto plano. + * @param {string} req.body[].numeroTelefono - Número de teléfono (10 dígitos). + * @param {string} req.body[].direccion - Dirección del usuario. + * @param {string} req.body[].fechaNacimiento - Fecha de nacimiento en formato YYYY-MM-DD. + * @param {string} req.body[].genero - Género del usuario. + * @param {boolean} req.body[].estatus - Estatus activo/inactivo del usuario. + * @param {number} req.body[].idRol - ID del rol asignado al usuario. + * @param {number|Array} req.body[].idCliente - ID(s) de cliente asociados. + * @param {string} req.body[].numeroEmergencia - Teléfono de emergencia del empleado. + * @param {string} req.body[].areaTrabajo - Área donde trabaja el empleado. + * @param {string} req.body[].posicion - Puesto del empleado. + * @param {number} req.body[].cantidadPuntos - Puntos iniciales del empleado. + * @param {string} req.body[].antiguedad - Fecha de ingreso (YYYY-MM-DD). + * @param {response} res - Objeto de respuesta HTTP. + * @returns {Promise} + * + * - 200 si el empleado se creó correctamente. + * - 400 si el cuerpo está vacío o no es un arreglo. + * - 500 si ocurre un error al crear el empleado. + */ +exports.crearEmpleado = async (req, res) => { + const { + nombreCompleto, + correoElectronico, + contrasenia, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus, + idRol, + idCliente, + numeroEmergencia, + areaTrabajo, + posicion, + cantidadPuntos, + antiguedad, + } = req.body; + + // Validaciones críticas + if (!idCliente) { + return res.status(400).json({ mensaje: 'Cliente no seleccionado' }); + } + if ( + !nombreCompleto + || !correoElectronico + || !contrasenia + || !numeroTelefono + || !direccion + || !fechaNacimiento + || !genero + || estatus === undefined + || idRol === undefined + || !numeroEmergencia + || !areaTrabajo + || !posicion + || cantidadPuntos === undefined + || !antiguedad + ) { + return res.status(400).json({ mensaje: 'Faltan campos requeridos' }); + } + + // Validaciones de formato y longitud + if (nombreCompleto.length > 75) { + return res.status(400).json({ mensaje: 'El nombre es demasiado largo' }); + } + if (!/^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$/.test(nombreCompleto)) { + return res.status(400).json({ mensaje: 'El nombre solo puede contener letras y espacios' }); + } + if (correoElectronico.length > 75) { + return res.status(400).json({ mensaje: 'El correo es demasiado largo' }); + } + const correoValido = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!correoValido.test(correoElectronico)) { + return res.status(400).json({ mensaje: 'El correo electrónico no es válido' }); + } + if (contrasenia.length > 75) { + return res.status(400).json({ mensaje: 'La contraseña es demasiado larga' }); + } + const tieneCaracterEspecial = /[!@#$%^&*(),.?":{}|<>]/; + const tieneMayuscula = /[A-Z]/; + if ( + contrasenia.length < 8 + || !tieneCaracterEspecial.test(contrasenia) + || !tieneMayuscula.test(contrasenia) + ) { + return res + .status(400) + .json({ + mensaje: + 'La contraseña es débil. Debe tener al menos 8 caracteres, una mayúscula y un caracter especial.', + }); + } + if (direccion.length > 150) { + return res.status(400).json({ mensaje: 'La dirección es demasiado larga' }); + } + if (posicion.length > 75) { + return res.status(400).json({ mensaje: 'La posición es demasiado larga' }); + } + if (areaTrabajo.length > 75) { + return res.status(400).json({ mensaje: 'El área de trabajo es demasiado larga' }); + } + if (genero.length > 20) { + return res.status(400).json({ mensaje: 'El género es demasiado largo' }); + } + if (isNaN(numeroEmergencia)) { + return res.status(400).json({ mensaje: 'El número de emergencia no es válido' }); + } + if (!/^\d+$/.test(String(cantidadPuntos)) || Number(cantidadPuntos) < 0) { + return res + .status(400) + .json({ mensaje: 'Los puntos deben ser un número entero mayor o igual a 0' }); + } + const telefonoValido = /^\d{10}$/; + if (!telefonoValido.test(numeroTelefono)) { + return res + .status(400) + .json({ mensaje: 'El número de teléfono debe tener 10 dígitos numéricos' }); + } + const fechaRegex = /^\d{4}-\d{2}-\d{2}$/; + if (!fechaRegex.test(fechaNacimiento) || isNaN(Date.parse(fechaNacimiento))) { + return res + .status(400) + .json({ mensaje: 'La fecha de nacimiento no tiene un formato válido (YYYY-MM-DD)' }); + } + if (!fechaRegex.test(antiguedad) || isNaN(Date.parse(antiguedad))) { + return res + .status(400) + .json({ mensaje: 'La antigüedad no tiene un formato válido (YYYY-MM-DD)' }); + } + + try { + const contraseniaEncriptada = await bcrypt.hash(contrasenia, 10); + const resultado = await repositorio.crearEmpleado({ + nombreCompleto, + correoElectronico, + contrasenia: contraseniaEncriptada, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus, + idRol, + idCliente, + numeroEmergencia, + areaTrabajo, + posicion, + cantidadPuntos, + antiguedad, + }); + return res.status(201).json({ + mensaje: 'Empleado creado exitosamente', + datos: resultado, + }); + } catch (error) { + return res.status(400).json({ mensaje: error.message }); + } +}; diff --git a/Empleados/Controladores/crearGrupoEmpleados.controller.js b/Empleados/Controladores/crearGrupoEmpleados.controller.js new file mode 100644 index 00000000..13d2cb16 --- /dev/null +++ b/Empleados/Controladores/crearGrupoEmpleados.controller.js @@ -0,0 +1,68 @@ +//RF21 - Crear Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF21 +/** + * @file crearGrupoEmpleados.controller.js + * @description + * Controlador encargado de crear un nuevo grupo de empleados y asignarles una lista de empleados. + */ + +// Importación del repositorio con la lógica de negocio. +const repositorio = require('@altertex/emp/repos/repositorioCrearGrupo'); +const MENSAJES = require('@altertex/util/const/mensajesEmpleados'); + +/** + * Controlador para crear un grupo de empleados. + * + * @async + * @function crearGrupoEmpleados + * @param {object} req - Request HTTP + * @param {object} res - Response HTTP + * @returns {Promise} + */ +exports.crearGrupoEmpleados = async (req, res) => { + const { + nombreGrupo, + descripcion, + idCliente = parseInt(req.user.clienteSeleccionado), + listaEmpleados, + } = req.body; + + // Validación de campos requeridos + if ( + !nombreGrupo + || !descripcion + || !idCliente + || !Array.isArray(listaEmpleados) + || listaEmpleados.length === 0 + ) { + return res.status(400).json({ + mensaje: MENSAJES.DATOS_INCOMPLETOS.mensaje, + }); + } + + try { + // Validación de nombre duplicado + const yaExiste = await repositorio.existeGrupoConNombre(nombreGrupo.trim(), idCliente); + if (yaExiste) { + return res.status(400).json({ + mensaje: MENSAJES.GRUPO_NOMBRE_REPETIDO.mensaje, + }); + } + + // Crear grupo y asignar empleados + const resultado = await repositorio.crearGrupoYAsignarEmpleados( + nombreGrupo, + descripcion, + idCliente, + listaEmpleados, + ); + + return res.status(201).json({ + mensaje: MENSAJES.GRUPO_CREADO.mensaje, + idGrupo: resultado.idGrupo, + }); + } catch { + return res.status(500).json({ + mensaje: MENSAJES.ERROR_CREAR_GRUPO.mensaje, + }); + } +}; \ No newline at end of file diff --git a/Empleados/Controladores/eliminarEmpleado.controller.js b/Empleados/Controladores/eliminarEmpleado.controller.js new file mode 100644 index 00000000..7b03f7e6 --- /dev/null +++ b/Empleados/Controladores/eliminarEmpleado.controller.js @@ -0,0 +1,73 @@ +const repositorio = require('@altertex/emp/repos/repositorioEliminarEmpleado'); +const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesEmpleados'); + +/** + * Controlador para eliminar uno o varios empleados. + * + * RF[20] - Elimina empleado: https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF20 + * + * Este controlador valida que se reciba un arreglo de IDs de empleados. Luego intenta eliminar + * cada uno de ellos mediante el repositorio correspondiente. Si alguno no existe o ya fue eliminado, + * se acumulan sus IDs. Si ocurre un error al eliminar, se registra el mensaje de error y se devuelve + * una respuesta HTTP adecuada. + * + * @async + * @function eliminarEmpleado + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {number[]} req.body.idsEmpleado - Array de IDs numéricos de los empleados a eliminar. + * @param {object} res - Objeto de respuesta de Express. + * @returns {Promise} Respuesta HTTP con: + * - `200 OK` si al menos un empleado fue eliminado exitosamente. + * - `404 Not Found` si todos los empleados estaban ausentes o ya eliminados. + * - `500 Internal Server Error` si ocurrió un error durante el proceso. + */ +exports.eliminarEmpleado = async (req, res) => { + const idsEmpleado = req.body.idsEmpleado; + + // Validación de entrada + if (!Array.isArray(idsEmpleado) || idsEmpleado.length === 0) { + return res.status(MENSAJES_EMPLEADOS.EMPLEADO_NO_ENCONTRADO.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.EMPLEADO_NO_ENCONTRADO.mensaje, + }); + } + + const errores = []; + const noEncontrados = []; + + // Intentar eliminar cada empleado individualmente + await Promise.all( + idsEmpleado.map(async (id) => { + try { + const resultado = await repositorio.eliminarEmpleado(id); + if (resultado.affectedRows === 0) { + noEncontrados.push(id); + } + } catch (err) { + errores.push({ id, error: err.message || 'Error desconocido' }); + } + }) + ); + + // Si hubo errores técnicos + if (errores.length > 0) { + return res.status(MENSAJES_EMPLEADOS.ERROR_ELIMINAR_EMPLEADO.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.ERROR_ELIMINAR_EMPLEADO.mensaje, + detalles: errores, + }); + } + + // Si ninguno de los empleados fue encontrado + if (noEncontrados.length === idsEmpleado.length) { + return res.status(MENSAJES_EMPLEADOS.EMPLEADO_NO_ENCONTRADO.codigo).json({ + mensaje: 'Ninguno de los empleados fue encontrado o ya habían sido eliminados.', + ids: noEncontrados, + }); + } + + // Eliminación exitosa (parcial o total) + return res.status(MENSAJES_EMPLEADOS.EMPLEADO_ELIMINADO.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.EMPLEADO_ELIMINADO.mensaje, + noEncontrados: noEncontrados.length > 0 ? noEncontrados : undefined, + }); +}; diff --git a/Empleados/Controladores/eliminarGrupoEmpleados.controller.js b/Empleados/Controladores/eliminarGrupoEmpleados.controller.js new file mode 100644 index 00000000..46345af0 --- /dev/null +++ b/Empleados/Controladores/eliminarGrupoEmpleados.controller.js @@ -0,0 +1,48 @@ +const repositorio = require('@altertex/emp/repos/repositorioEliminarGrupoEmpleados'); +const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesGrupoEmpleados'); + +/** + * RF25 - Eliminar Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF25 + * Controlador para eliminar uno o varios grupos de empleados. + * + * Este endpoint permite eliminar grupos de empleados especificados mediante sus IDs. + * El proceso elimina las relaciones entre empleados y grupos, y luego elimina los grupos en sí, + * utilizando una transacción para garantizar la integridad de la base de datos. + * + * @async + * @function eliminarGrupoEmpleados + * @param {object} req - Objeto de solicitud Express. + * @param {object} req.body - Cuerpo de la solicitud. + * @param {number[]} req.body.idsGrupo - Array de IDs de grupos de empleados a eliminar. + * @param {object} res - Objeto de respuesta Express. + * @returns {Promise} Respuesta HTTP con el resultado de la operación: + * - 200: Eliminación exitosa. + * - 400 o 500: Error al eliminar grupo(s) de empleados. + * + * @throws {Error} Error capturado si ocurre una falla durante el proceso de eliminación. + * + */ +exports.eliminarGrupoEmpleados = async (req, res) => { + try { + const idsGrupo = req.body.idsGrupo; + + if (!Array.isArray(idsGrupo) || idsGrupo.length === 0) { + return res.status(MENSAJES_EMPLEADOS.ELIMINAR_GRUPO_ERROR.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.ELIMINAR_GRUPO_ERROR.mensaje, + }); + } + await Promise.all( + idsGrupo.map(async (idGrupo) => { + await repositorio.eliminarGrupoConTransaccion(idGrupo); + }) + ); + + return res.status(MENSAJES_EMPLEADOS.ELIMINAR_GRUPO_EXITOSO.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.ELIMINAR_GRUPO_EXITOSO.mensaje, + }); + } catch { + return res.status(MENSAJES_EMPLEADOS.ELIMINAR_GRUPO_ERROR.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.ELIMINAR_GRUPO_ERROR.mensaje, + }); + } +}; diff --git a/Empleados/Controladores/exportarEmpleados.controller.js b/Empleados/Controladores/exportarEmpleados.controller.js new file mode 100644 index 00000000..f3d3e4c3 --- /dev/null +++ b/Empleados/Controladores/exportarEmpleados.controller.js @@ -0,0 +1,73 @@ +const repositorio = require('@altertex/emp/repos/repositorioExportarEmpleado'); +const { Parser } = require('json2csv'); +const { format } = require('date-fns'); +const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesEmpleados'); + +/** + * Controlador para exportar empleados seleccionados de un cliente y retornar CSV como string en JSON. + * + * @async + * @function exportarEmpleados + * @param {Request} req + * @param {Response} res + * @returns {Response} JSON con mensaje + contenido CSV en texto plano + * @see [RF59 - Exportar Empleados](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59) + */ +exports.exportarEmpleados = async (req, res) => { + try { + const idCliente = parseInt(req.user.clienteSeleccionado); + const idsEmpleado = req.body.idsEmpleado; + + if (!Array.isArray(idsEmpleado) || idsEmpleado.length === 0) { + return res.status(MENSAJES_EMPLEADOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: 'Debes seleccionar al menos un empleado para exportar.' + }); + } + + const idsSeleccionados = idsEmpleado.map(id => parseInt(id)); + const empleados = await repositorio.obtenerEmpleadosExportacion(idCliente, idsSeleccionados); + + if (!empleados || empleados.length === 0) { + return res.status(MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.mensaje, + csv: '' + }); + } + + empleados.forEach(emp => { + emp.fechaNacimiento = format(new Date(emp.fechaNacimiento), 'dd/MM/yyyy'); + emp.antiguedad = format(new Date(emp.antiguedad), 'dd/MM/yyyy'); + }); + + const campos = [ + { label: 'ID', value: 'idEmpleado' }, + { label: 'Nombre completo', value: 'nombreCompleto' }, + { label: 'Correo electrónico', value: 'correoElectronico' }, + { label: 'Número de teléfono', value: 'numeroTelefono' }, + { label: 'Dirección', value: 'direccion' }, + { label: 'Fecha de nacimiento', value: 'fechaNacimiento' }, + { label: 'Género', value: 'genero' }, + { label: 'Estatus', value: 'estatus' }, + { label: 'Número de emergencia', value: 'numeroEmergencia' }, + { label: 'Área de trabajo', value: 'areaTrabajo' }, + { label: 'Posición', value: 'posicion' }, + { label: 'Cantidad de puntos', value: 'cantidadPuntos' }, + { label: 'Antigüedad', value: 'antiguedad' } + ]; + + const parser = new Parser({ fields: campos }); + const csv = parser.parse(empleados); + const csvConBOM = `\uFEFF${csv}`; + + return res.status(MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.mensaje, + csv: csvConBOM + }); + } catch (error) { + console.error('Error al exportar empleados:', error); + return res.status(MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.codigo).json({ + mensaje: MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.mensaje, + csv: '' + }); + } +}; \ No newline at end of file diff --git a/Empleados/Controladores/importarEmpleados.controller.js b/Empleados/Controladores/importarEmpleados.controller.js new file mode 100644 index 00000000..fde30429 --- /dev/null +++ b/Empleados/Controladores/importarEmpleados.controller.js @@ -0,0 +1,216 @@ +const bcrypt = require('bcryptjs'); +const repositorio = require('@altertex/emp/repos/repositorioImportarEmpleado'); +const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios'); + +/** + * Controlador para importar múltiples empleados con su usuario asociado. + * + * Esta función valida y procesa un arreglo de objetos con la información completa + * de usuario y empleado. Aplica validaciones de formato, unicidad de correo, + * fortaleza de contraseña, y formato telefónico antes de delegar al repositorio. + * + * Cada objeto debe incluir los datos necesarios tanto para crear el usuario como + * para insertar al empleado en la base de datos. + * + * Si alguna fila falla, se continúa con las demás y se devuelve un resumen de errores. + * + * @async + * @function importarEmpleados + * @param {Array} req - Objeto de solicitud HTTP. Espera `req.body` como un arreglo de objetos empleados. + * @param {Array} req.body - Arreglo de objetos con datos de usuario y empleado. + * @param {string} req.body[].nombreCompleto - Nombre completo del usuario. + * @param {string} req.body[].correoElectronico - Correo electrónico único del usuario. + * @param {string} req.body[].contrasena - Contraseña en texto plano. + * @param {string} req.body[].numeroTelefono - Número de teléfono (10 dígitos). + * @param {string} req.body[].direccion - Dirección del usuario. + * @param {string} req.body[].fechaNacimiento - Fecha de nacimiento en formato YYYY-MM-DD. + * @param {string} req.body[].genero - Género del usuario. + * @param {boolean} req.body[].estatus - Estatus activo/inactivo del usuario. + * @param {number} req.body[].idRol - ID del rol asignado al usuario. + * @param {number|Array} req.body[].idCliente - ID(s) de cliente asociados. + * @param {string} req.body[].numeroEmergencia - Teléfono de emergencia del empleado. + * @param {string} req.body[].areaTrabajo - Área donde trabaja el empleado. + * @param {string} req.body[].posicion - Puesto del empleado. + * @param {number} req.body[].cantidadPuntos - Puntos iniciales del empleado. + * @param {string} req.body[].antiguedad - Fecha de ingreso (YYYY-MM-DD). + * @param {response} res - Objeto de respuesta HTTP. + * @returns {Promise} + * - 200 si todos los empleados se importaron correctamente. + * - 207 si hubo errores parciales en una o más filas (respuesta incluye `errores`). + * - 400 si el cuerpo está vacío o no es un arreglo. + * + */ +exports.importarEmpleados = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + const empleados = req.body; + + if (!Array.isArray(empleados) || empleados.length === 0) { + return res.status(400).json({ mensaje: 'No se recibieron empleados.' }); + } + + const errores = []; + const listaParaImportar = []; + + for (const [index, datos] of empleados.entries()) { + const fila = `Fila ${index + 1}`; + const { + nombreCompleto, + correoElectronico, + contrasena, + numeroTelefono + } = datos; + + if ( + !numeroTelefono + || !datos.direccion + || !datos.fechaNacimiento + || !datos.genero + || !datos.numeroEmergencia + || !datos.areaTrabajo + || !datos.posicion + || !datos.antiguedad + ) { + errores.push({ fila, error: 'Faltan campos requeridos' }); + continue; + } + + if (!nombreCompleto){ + errores.push({ fila, error: 'El nombre es requerido' }); + continue; + } if (nombreCompleto.length > 75) { + errores.push({ fila, error: 'El nombre es demasiado largo' }); + continue; + } if (!/^[A-Za-zÁÉÍÓÚáéíóúÑñ\s]+$/.test(nombreCompleto)) { + errores.push({ fila, error: 'El nombre solo puede contener letras y espacios' }); + continue; + } + + if (!correoElectronico) { + errores.push({ fila, error: 'El correo es requerido' }); + continue; + } if (correoElectronico && correoElectronico.length > 75) { + errores.push({ fila, error: 'El correo es demasiado largo' }); + continue; + } + + if (!contrasena) { + errores.push({ fila, error: 'La contraseña es requerida' }); + continue; + } if (contrasena.length > 75) { + errores.push({ fila, error: 'La contraseña es demasiado larga' }); + continue; + } + + if (typeof datos.idCliente !== 'undefined' && datos.idCliente !== '' && datos.idCliente !== null) { + errores.push({ fila, error: 'El cliente no debe ser incluido en el archivo' }); + continue; + } + + if (datos.direccion.length > 150) { + errores.push({ fila, error: 'La dirección es demasiado larga' }); + continue; + } + + if (datos.estatus == null) { + errores.push({ fila, error: 'Estatus inválido: debe ser 0 o 1' }); + continue; + } + + if (datos.posicion.length > 75) { + errores.push({ fila, error: 'La posición es demasiado larga' }); + continue; + } + + if(datos.areaTrabajo.length > 75) { + errores.push({ fila, error: 'El área de trabajo es demasiado larga' }); + continue; + } + + if (datos.genero.length > 20) { + errores.push({ fila, error: 'El género es demasiado largo' }); + continue; + } + + if (isNaN(datos.numeroEmergencia)) { + errores.push({ fila, error: 'El número de emergencia no es valido' }); + continue; + } + + if ( + !/^\d+$/.test(String(datos.cantidadPuntos)) + || Number(datos.cantidadPuntos) < 0) { + errores.push({ fila, error: 'Los puntos deben ser un número entero mayor o igual a 0' }); + continue; + } + + // Correo válido + const correoValido = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!correoValido.test(correoElectronico)) { + errores.push({ fila, error: MENSAJES_USUARIOS.CORREO_INVALIDO.mensaje }); + continue; + } + + // Contraseña fuerte + const tieneCaracterEspecial = /[!@#$%^&*(),.?":{}|<>]/; + const tieneMayuscula = /[A-Z]/; + if ( + contrasena.length < 8 + || !tieneCaracterEspecial.test(contrasena) + || !tieneMayuscula.test(contrasena) + ) { + errores.push({ fila, error: MENSAJES_USUARIOS.CONTRASENA_DEBIL.mensaje }); + continue; + } + + // Teléfono válido + const telefonoValido = /^\d{10}$/; + if (!telefonoValido.test(numeroTelefono)) { + errores.push({ fila, error: MENSAJES_USUARIOS.TELEFONO_INVALIDO.mensaje }); + continue; + } + + const fechaRegex = /^\d{4}-\d{2}-\d{2}$/; + if (!fechaRegex.test(datos.fechaNacimiento) || isNaN(Date.parse(datos.fechaNacimiento))) { + errores.push({ fila, error: 'La fecha de nacimiento no tiene un formato válido (DD-MM-YYYY)' }); + continue; + } + if (!fechaRegex.test(datos.antiguedad) || isNaN(Date.parse(datos.antiguedad))) { + errores.push({ fila, error: 'La antigüedad no tiene un formato válido (DD-MM-YYYY)' }); + continue; + } + + try { + const hash = await bcrypt.hash(contrasena, 10); + listaParaImportar.push({ + ...datos, + contrasena: hash + }); + } catch (err) { + errores.push({ fila, error: `Error al procesar contraseña: ${err.message}` }); + } + } + + if (errores.length > 0) { + return res.status(207).json({ + mensaje: 'Importación parcial con errores.', + errores + }); + } + + for (const empleado of listaParaImportar) { + empleado.idCliente = idCliente; + } + + try { + await repositorio.importarEmpleadosMasivo(listaParaImportar); + } catch (error) { + errores.push({ + fila: "", + error: error.message + }); + } + + return res.status(200).json({ + mensaje: 'Todos los empleados importados correctamente.' + }); +}; diff --git a/Empleados/Controladores/leerGrupoEmpleados.controller.js b/Empleados/Controladores/leerGrupoEmpleados.controller.js new file mode 100644 index 00000000..b9f85ee9 --- /dev/null +++ b/Empleados/Controladores/leerGrupoEmpleados.controller.js @@ -0,0 +1,42 @@ +const repositorio = require('@altertex/emp/repos/repositorioLeerGrupoDeEmpleados'); +const MENSAJES_GRUPO_EMPLEADOS = require('@altertex/util/const/mensajesGrupoEmpleados'); + +/** + * Lee los detalles de un grupo de empleados desde la base de datos utilizando su ID. + * + * Valida el parámetro `idGrupo` y obtiene la información del grupo de empleados a través del repositorio. + * Si el grupo de empleados no es encontrado o el parámetro es inválido, retorna un error. + * + * @param {Express.Request} req - La solicitud HTTP que contiene el `idGrupo` en el cuerpo. + * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente. + * @returns {Promise} Responde con el grupo de empleados encontrado o un mensaje de error. + * + * @see RF[23] Lee grupo de empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF23 + */ +exports.leerGrupoEmpleados = async (req, res) => { + const idGrupo = parseInt(req.body.idGrupo); + + if (isNaN(idGrupo)) { + return res + .status(MENSAJES_GRUPO_EMPLEADOS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_GRUPO_EMPLEADOS.PARAMETROS_INVALIDOS.mensaje }); + } + try { + const grupoEmpleados = await repositorio.obtenerGrupoEmpleadosPorId(idGrupo); + + if (!grupoEmpleados) { + return res + .status(MENSAJES_GRUPO_EMPLEADOS.GRUPO_NO_ENCONTRADO.codigo) + .json({ mensaje: MENSAJES_GRUPO_EMPLEADOS.GRUPO_NO_ENCONTRADO.mensaje }); + } + + return res.status(MENSAJES_GRUPO_EMPLEADOS.GRUPO_OBTENIDO.codigo).json({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.GRUPO_OBTENIDO.mensaje, + grupoEmpleados, + }); + } catch { + return res + .status(MENSAJES_GRUPO_EMPLEADOS.ERROR_OBTENER_GRUPO.codigo) + .json({ mensaje: MENSAJES_GRUPO_EMPLEADOS.ERROR_OBTENER_GRUPO.mensaje }); + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioActualizarEmpleado.js b/Empleados/Datos/Repositorios/repositorioActualizarEmpleado.js new file mode 100644 index 00000000..eca4d541 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioActualizarEmpleado.js @@ -0,0 +1,47 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES = require('@altertex/util/const/mensajesEmpleados'); +const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados'); + +//RF[19] Actualizar empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19] + +/** + *Repositorio para actualizar los datos de un empleado en la BD. + * + * Recorre un arreglo de objetos que contienen la información de cada empleado para + * actualizar su información. + * + * Utiliza un array de objetos con la información del empleado, y hace la + * consulta correspondiente a la base de datos. + * + * @function actualizarEmpleado + * @async + * @param {Array<{ idEmpleado: number, idUsuario: number, numeroEmergencia: + * number, areaTrabajo: string, posicion: string, + * cantidadPuntos: number, antiguedad: Date}>} datos - Lista de + * información del empleado a actualizar. + * @throws {Error} Si el arreglo está vacío o si ocurre un error en la base de datos. + * @returns {Promise} Promesa que se resuelve cuando todas las actualizaciones han sido ejecutadas. + */ +exports.actualizarEmpleado = async (datos) => { + if (!Array.isArray(datos) || datos.length === 0) { + throw new Error('Sin datos para actualizar.'); + } + try { + await Promise.all( + datos.map( + ({ idEmpleado, numeroEmergencia, areaTrabajo, posicion, cantidadPuntos, antiguedad }) => { + return correrQuery(CONSULTAS_EMPLEADOS.ACTUALIZAR, [ + numeroEmergencia, + areaTrabajo, + posicion, + cantidadPuntos, + antiguedad, + idEmpleado, + ]); + } + ) + ); + } catch { + throw new Error(MENSAJES.ERROR_ACTUALIZAR.mensaje); + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioActualizarGrupo.js b/Empleados/Datos/Repositorios/repositorioActualizarGrupo.js new file mode 100644 index 00000000..4f803667 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioActualizarGrupo.js @@ -0,0 +1,111 @@ +const CONSULTAS = require('@altertex/util/const/consultasGrupoEmpleados'); +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES = require('@altertex/util/const/mensajesGrupoEmpleados'); +// RF[24] Actualiza grupo empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24] + +/** + * Actualiza un grupo de empleados en la base de datos, incluyendo su nombre, + * descripción, empleados asociados y sets de productos relacionados. + * + * Esta función realiza: + * - La actualización del nombre y descripción del grupo. + * - La verificación y reemplazo de los empleados asociados al grupo. + * - La verificación y reemplazo de los sets de productos asociados al grupo. + * + * En caso de que los empleados o sets proporcionados no pertenezcan al mismo cliente, + * se lanza un error. + * + * Nota: Si empleados o setsDeProductos están vacíos, se eliminan todas las asociaciones existentes. + * + * @async + * @function actualizarGrupoEmpleados + * @param {object} datosActualizacion - Datos necesarios para actualizar el grupo. + * @param {number} datosActualizacion.idGrupoEmpleado - ID del grupo de empleados a actualizar. + * @param {string} datosActualizacion.nombre - Nuevo nombre del grupo. + * @param {string} datosActualizacion.descripcion - Nueva descripción del grupo. + * @param {number[]} datosActualizacion.empleados - Lista de IDs de empleados a asociar al grupo. Array vacío elimina todas las asociaciones. + * @param {number[]} datosActualizacion.setsDeProductos - Lista de IDs de sets de productos a asociar al grupo. Array vacío elimina todas las asociaciones. + * @throws {Error} Si ocurre algún error durante la actualización o verificación de empleados/sets. + */ +exports.actualizarGrupoEmpleados = async (datosActualizacion) => { + const { idGrupoEmpleado, nombre, descripcion, empleados, setsDeProductos } = datosActualizacion; + + try { + // Actualizar nombre y descripción del grupo + await correrQuery(CONSULTAS.ACTUALIZAR_GRUPO_EMPLEADOS_NOMBRE_DESCRIPCION, [ + nombre, + descripcion, + idGrupoEmpleado, + nombre, + descripcion, + ]); + + // Manejar empleados (incluyendo arrays vacíos) + if (empleados !== undefined && Array.isArray(empleados)) { + if (empleados.length > 0) { + // Si hay empleados, verificar que pertenezcan al mismo cliente + const empleadosSTR = empleados.join(', '); + const valores = empleados + .map((empleadoId) => `(${empleadoId}, ${idGrupoEmpleado})`) + .join(', '); + + const resultadoVerificacion = await correrQuery( + CONSULTAS.VERIFICAR_EMPLEADOS_CLIENTE.replace('__EMPLEADOS__', empleadosSTR), + [idGrupoEmpleado], + ); + + if (resultadoVerificacion[0].validos !== empleados.length) { + throw new Error(MENSAJES.ERROR_VERIFICACION_CLIENTE_EMPLEADO.mensaje); + } + + // Eliminar empleados que no están en la nueva lista + await correrQuery( + CONSULTAS.ELIMINAR_EMPLEADOS_DE_GRUPO_BASE.replace('__ID__', idGrupoEmpleado).replace( + '__EMPLEADOS__', + empleadosSTR, + ), + ); + + // Agregar los nuevos empleados + await correrQuery(CONSULTAS.AGREGAR_EMPLEADOS_NUEVOS_BASE.replace('__VALORES__', valores)); + } else { + // Si el array está vacío, eliminar todas las asociaciones de empleados + await correrQuery(CONSULTAS.ELIMINAR_TODOS_EMPLEADOS_DE_GRUPO, [idGrupoEmpleado]); + } + } + + // Manejar sets de productos (incluyendo arrays vacíos) + if (setsDeProductos !== undefined && Array.isArray(setsDeProductos)) { + if (setsDeProductos.length > 0) { + // Si hay sets, verificar que pertenezcan al mismo cliente + const setsSTR = setsDeProductos.join(', '); + const valores = setsDeProductos.map((setId) => `(${setId}, ${idGrupoEmpleado})`).join(', '); + + const resultadoVerificacion = await correrQuery( + CONSULTAS.VERIFICAR_SETS_CLIENTE.replace('__SETS__', setsSTR), + [idGrupoEmpleado], + ); + + if (resultadoVerificacion[0].validos !== setsDeProductos.length) { + throw new Error(MENSAJES.ERROR_VERIFICACION_CLIENTE_SET.mensaje); + } + + // Eliminar sets que no están en la nueva lista + await correrQuery( + CONSULTAS.ELIMINAR_SETS_DE_GRUPO_BASE.replace('__ID__', idGrupoEmpleado).replace( + '__SETS__', + setsSTR, + ), + ); + + // Agregar los nuevos sets + await correrQuery(CONSULTAS.AGREGAR_SETS_NUEVOS_BASE.replace('__VALORES__', valores)); + } else { + // Si el array está vacío, eliminar todas las asociaciones de sets + await correrQuery(CONSULTAS.ELIMINAR_TODOS_SETS_DE_GRUPO, [idGrupoEmpleado]); + } + } + } catch (error) { + throw new Error(error); + } +}; \ No newline at end of file diff --git a/Empleados/Datos/Repositorios/repositorioCrearEmpleado.js b/Empleados/Datos/Repositorios/repositorioCrearEmpleado.js new file mode 100644 index 00000000..ed0ed8f2 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioCrearEmpleado.js @@ -0,0 +1,156 @@ +const db = require('@altertex/util/bd/db'); +const ROL_PREDETERMINADO = 3; // ID del rol por defecto para empleados + +const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados'); +const CONSULTAS_IMPORTAR_EMPLEADOS = require('@altertex/util/const/consultasImportarEmpleados'); + +//RF[16] Crear empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF16] + +/** + * Crea un nuevo empleado y su usuario asociado en la base de datos. + * + * @async + * @function crearEmpleado + * @param {object} empleado - Objeto con los datos del nuevo empleado. + * @param {string} empleado.nombreCompleto - Nombre completo del usuario. + * @param {string} empleado.correoElectronico - Correo electrónico único del usuario. + * @param {string} empleado.contrasena - Contraseña en texto plano (ya hasheada). + * @param {string} empleado.numeroTelefono - Teléfono del usuario (exactamente 10 dígitos). + * @param {string} empleado.direccion - Dirección del usuario. + * @param {string} empleado.fechaNacimiento - Fecha de nacimiento (YYYY-MM-DD). + * @param {string} empleado.genero - Género del usuario. + * @param {boolean} empleado.estatus - Estatus del usuario (true = activo, false = inactivo). + * @param {number} empleado.idRol - ID del rol asignado al usuario. + * @param {number} empleado.idCliente - ID del cliente asociado al usuario. + * @param {string} empleado.numeroEmergencia - Teléfono de emergencia del empleado. + * @param {string} empleado.areaTrabajo - Área de trabajo del empleado. + * @param {string} empleado.posicion - Puesto o cargo del empleado. + * @param {number} empleado.cantidadPuntos - Puntos acumulados del empleado. + * @param {string} empleado.antiguedad - Fecha de antigüedad/ingreso (YYYY-MM-DD). + * @throws {Error} Si el parámetro "empleado" no es un objeto válido o está vacío. + * @throws {Error} Si ocurre cualquier fallo durante la inserción en la transacción. + * + * @returns {Promise} Resuelve con un objeto que contiene el ID del nuevo empleado y su usuario. + */ +exports.crearEmpleado = async (empleado) => { + if (!empleado || typeof empleado !== 'object') { + throw new Error('No se recibió un empleado válido para importar.'); + } + + const conn = await db.getConnection(); + + try { + await conn.beginTransaction(); + + // Validar correo duplicado + const [correoExistente] = await conn.query( + CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_CORREOS_DUPLICADOS, + [[empleado.correoElectronico]] + ); + if (correoExistente.length > 0) { + throw new Error(`Correo ya registrado: ${empleado.correoElectronico}`); + } + + // Validar fecha de nacimiento + if (!/^\d{4}-\d{2}-\d{2}$/.test(empleado.fechaNacimiento)) { + throw new Error('Fecha de nacimiento inválida. Debe ser en formato YYYY-MM-DD.'); + } + const fechaNacimiento = new Date(empleado.fechaNacimiento); + if (isNaN(fechaNacimiento.getTime())) { + throw new Error('Fecha de nacimiento inválida.'); + } + const hoy = new Date(); + if (fechaNacimiento > hoy) { + throw new Error('La fecha de nacimiento no puede ser futura.'); + } + if (fechaNacimiento.getFullYear() < 1900) { + throw new Error('La fecha de nacimiento no puede ser anterior a 1900.'); + } + + // Validar que sea mayor o igual a 18 años + const edad = hoy.getFullYear() - fechaNacimiento.getFullYear(); + const mes = hoy.getMonth() - fechaNacimiento.getMonth(); + const dia = hoy.getDate() - fechaNacimiento.getDate(); + let edadFinal = edad; + if (mes < 0 || (mes === 0 && dia < 0)) { + edadFinal -= 1; + } + if (edadFinal < 18) { + throw new Error('El empleado debe tener al menos 18 años.'); + } + + // Validar teléfono duplicado + const [telefonoExistente] = await conn.query( + CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_TELEFONO_DUPLICADO, + [[empleado.numeroTelefono]] + ); + if (telefonoExistente.length > 0) { + throw new Error(`Teléfono ya registrado: ${empleado.numeroTelefono}`); + } + + //Validar antigüedad + if (!/^\d{4}-\d{2}-\d{2}$/.test(empleado.antiguedad)) { + throw new Error('Antigüedad inválida. Debe ser en formato YYYY-MM-DD.'); + } + const fechaAntiguedad = new Date(empleado.antiguedad); + if (isNaN(fechaAntiguedad.getTime())) { + throw new Error('Antigüedad inválida.'); + } + if (fechaAntiguedad > hoy) { + throw new Error('La antigüedad no puede ser futura.'); + } + if (fechaAntiguedad.getFullYear() < 1900) { + throw new Error('La antigüedad no puede ser anterior a 1900.'); + } + // Validar que la antigüedad sea menor o igual a la fecha de nacimiento + if (fechaAntiguedad < fechaNacimiento) { + throw new Error('La antigüedad no puede ser anterior a la fecha de nacimiento.'); + } + // Validar que la antigüedad sea menor o igual a la fecha actual + if (fechaAntiguedad > hoy) { + throw new Error('La antigüedad no puede ser posterior a la fecha actual.'); + } + + // Insertar usuario + const [resultadoUsuario] = await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_USUARIO, [ + empleado.nombreCompleto, + empleado.correoElectronico, + empleado.contrasenia, + empleado.numeroTelefono, + empleado.direccion, + empleado.fechaNacimiento, + empleado.genero, + empleado.estatus, + ]); + const idUsuario = resultadoUsuario.insertId; + + // Insertar rol + await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_ROL, [idUsuario, ROL_PREDETERMINADO]); + + // Insertar asociación usuario-cliente + const listaClientes = Array.isArray(empleado.idCliente) + ? empleado.idCliente + : [empleado.idCliente]; + for (const idCli of listaClientes) { + await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_USUARIO_CLIENTE, [idUsuario, idCli]); + } + + // Insertar empleado + await conn.query(CONSULTAS_EMPLEADOS.INSERTAR_EMPLEADO, [ + idUsuario, + empleado.idCliente, + empleado.numeroEmergencia, + empleado.areaTrabajo, + empleado.posicion, + parseFloat(empleado.cantidadPuntos), + empleado.antiguedad, + ]); + + await conn.commit(); + } catch (err) { + await conn.rollback(); + throw new Error(`${err.message}`); + } finally { + if (conn) conn.release(); + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioCrearGrupo.js b/Empleados/Datos/Repositorios/repositorioCrearGrupo.js new file mode 100644 index 00000000..e2d3e9a9 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioCrearGrupo.js @@ -0,0 +1,73 @@ +// RF21 - Crear Grupo de Empleados +const db = require('@altertex/util/bd/db'); +const CONSULTAS = require('@altertex/util/const/consultasGrupoEmpleados'); + +/** + * Crea un nuevo grupo de empleados y asigna una lista de empleados al grupo. + * + * @async + * @function crearGrupoYAsignarEmpleados + * @param {string} nombreGrupo - Nombre del nuevo grupo. + * @param {string} descripcion - Descripción del grupo. + * @param {number} idCliente - ID del cliente al que pertenece el grupo. + * @param {number[]} listaEmpleados - Lista de IDs de empleados que se asignarán al grupo. + * @returns {Promise<{idGrupo: number}>} El ID del grupo creado. + * @throws {Error} Si ocurre un error durante la transacción. + */ +exports.crearGrupoYAsignarEmpleados = async (nombreGrupo, descripcion, idCliente, listaEmpleados) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + // Inserta el grupo en la base de datos + const [resultadoGrupo] = await conexion.query( + CONSULTAS.CREAR_GRUPO, + [idCliente, nombreGrupo, descripcion] + ); + + const idGrupo = resultadoGrupo.insertId; + + // Asigna los empleados al grupo + for (const idEmpleado of listaEmpleados) { + await conexion.query( + CONSULTAS.ASIGNAR_EMPLEADO_A_GRUPO, + [idEmpleado, idGrupo] + ); + } + + await conexion.commit(); + return { idGrupo }; + } catch (error) { + await conexion.rollback(); + throw error; + } finally { + if (conexion) conexion.release(); + } +}; + +/** + * Verifica si ya existe un grupo con el mismo nombre para un cliente dado. + * + * @async + * @function existeGrupoConNombre + * @param {string} nombreGrupo - Nombre del grupo a validar. + * @param {number} idCliente - ID del cliente. + * @returns {Promise} `true` si el grupo ya existe, `false` si no. + * @throws {Error} Si ocurre un error durante la consulta. + */ +exports.existeGrupoConNombre = async (nombreGrupo, idCliente) => { + const conexion = await db.getConnection(); + + try { + const [resultados] = await conexion.query( + CONSULTAS.VALIDAR_NOMBRE_REPETIDO, + [idCliente, nombreGrupo.trim()] + ); + return resultados.length > 0; + } catch { + throw new Error("Ya existe un grupo con ese nombre"); + } finally { + if (conexion) conexion.release(); + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioEliminarEmpleado.js b/Empleados/Datos/Repositorios/repositorioEliminarEmpleado.js new file mode 100644 index 00000000..204860eb --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioEliminarEmpleado.js @@ -0,0 +1,33 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados'); + +/** + * Elimina un empleado y su usuario vinculado. + * + * @param {number} idEmpleado - ID del empleado a eliminar. + * @returns {Promise<{affectedRows: number}>} Resultado de la eliminación del empleado. + */ +exports.eliminarEmpleado = async (idEmpleado) => { + try { + // 1. Obtener el idUsuario asociado al empleado + const resultadoUsuario = await correrQuery( + CONSULTAS_EMPLEADOS.OBTENER_ID_USUARIO_POR_EMPLEADO, + [idEmpleado] + ); + + const idUsuario = resultadoUsuario[0]?.idUsuario; + if (!idUsuario) { + throw new Error(`No se encontró un usuario asociado al empleado con ID ${idEmpleado}`); + } + + // 2. Eliminar el usuario + await correrQuery('DELETE FROM usuario WHERE idUsuario = ?', [idUsuario]); + + // 3. Eliminar el empleado + const resultado = await correrQuery(CONSULTAS_EMPLEADOS.ELIMINAR_EMPLEADO, [idEmpleado]); + + return resultado; + } catch { + throw new Error('Ocurrio un error al eliminar un empleado'); + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioEliminarGrupoEmpleados.js b/Empleados/Datos/Repositorios/repositorioEliminarGrupoEmpleados.js new file mode 100644 index 00000000..3800661e --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioEliminarGrupoEmpleados.js @@ -0,0 +1,49 @@ +//RF25 - Eliminar Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF25 + +const db = require('@altertex/util/bd/db'); +const CONSULTAS_GRUPOS = require('@altertex/util/const/consultasGrupoEmpleados'); + +/** + * Elimina un grupo de empleados y sus relaciones con empleados dentro de una transacción. + * + * Esta función realiza tres operaciones: + * 1. Elimina las relaciones entre empleados y el grupo en `empleado_grupo`. + * 2. Elimina las relaciones en `set_producto_grupo_empleado`. + * 3. Elimina el grupo de la tabla `grupo_empleado`. + * + * Si alguna de las operaciones falla, se revierte toda la transacción para mantener la integridad de los datos. + * + * @async + * @function eliminarGrupoConTransaccion + * @param {number} idGrupo - ID del grupo de empleados a eliminar. + * @returns {Promise} - Resultado de la operación MySQL, incluyendo `affectedRows`. + * @throws {Error} - Si ocurre un error durante la transacción o eliminación. + */ +exports.eliminarGrupoConTransaccion = async (idGrupo) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + // 1. Eliminar de empleado_grupo + await conexion.query(CONSULTAS_GRUPOS.ELIMINAR_EMPLEADO_GRUPO, [idGrupo]); + + // 2. Eliminar de set_producto_grupo_empleado + await conexion.query(CONSULTAS_GRUPOS.ELIMINAR_SET_PRODUCTO_GRUPO, [idGrupo]); + + // 3. Eliminar de grupo_empleado + const [resultado] = await conexion.query(CONSULTAS_GRUPOS.ELIMINAR_GRUPO, [idGrupo]); + + if (resultado.affectedRows === 0) { + throw new Error(`Grupo con ID ${idGrupo} no encontrado`); + } + + await conexion.commit(); + return resultado; + } catch (error) { + await conexion.rollback(); + throw error; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Empleados/Datos/Repositorios/repositorioEmpleados.js b/Empleados/Datos/Repositorios/repositorioEmpleados.js new file mode 100644 index 00000000..6270fb2b --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioEmpleados.js @@ -0,0 +1,28 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados'); + +/** + * Función para obtener la lista de empleados de un cliente específico. + * + * RF17 - Consulta Lista Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF17 + * + * @async + * @function obtenerEmpleados + * @param {number} idCliente - ID del cliente cuyos empleados se desean consultar. + * + * @returns {Promise} Lista de empleados del cliente. + * - Si no se encuentran empleados, se retorna un array vacío. + * + * @throws {Error} Si ocurre un error al ejecutar la consulta o si no se encuentran resultados. + */ +exports.obtenerEmpleados = async (idCliente) => { + const query = CONSULTAS_EMPLEADOS.OBTENER_LISTA; + + try { + const empleados = await correrQuery(query, [idCliente]); + + return empleados; + } catch { + return []; + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioExportarEmpleado.js b/Empleados/Datos/Repositorios/repositorioExportarEmpleado.js new file mode 100644 index 00000000..f8d7f19a --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioExportarEmpleado.js @@ -0,0 +1,17 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_EMPLEADOS = require('@altertex/util/const/consultasEmpleados'); + +/** + * Consulta la lista de empleados seleccionados de un cliente para exportar en CSV. + * + * @param {number} idCliente + * @param {number[]} idsEmpleado + * @returns {Promise>} + * + * @see [RF59 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59) + */ +exports.obtenerEmpleadosExportacion = (idCliente, idsEmpleado) => { + const placeholders = idsEmpleado.map(() => '?').join(', '); + const query = CONSULTAS_EMPLEADOS.OBTENER_DATOS_EXPORTACION.replace('__IDS__', placeholders); + return correrQuery(query, [idCliente, ...idsEmpleado]); +}; \ No newline at end of file diff --git a/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js b/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js new file mode 100644 index 00000000..184fc061 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioGrupoDeEmpleados.js @@ -0,0 +1,61 @@ +/** + * @file grupoEmpleados.repositorio.js + * @description + * Contiene funciones para consultar y validar grupos de empleados asociados a un cliente. + * Incluye lógica para obtener todos los grupos de un cliente y verificar duplicados por nombre. + * + * RF22 - Consulta Lista de Grupo Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF22 + */ + +// Importación de utilidades para consultas a base de datos. +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_GRUPO_EMPLEADOS = require('@altertex/util/const/consultasGrupoEmpleados'); +const conexion = require('@altertex/util/bd/db'); + +/** + * Obtiene la lista de grupos de empleados asociados a un cliente específico. + * + * @async + * @function obtenerGrupoDeEmpleados + * @param {number} idCliente - ID del cliente cuyo grupo de empleados se desea consultar. + * @returns {Promise>} Retorna un arreglo con los grupos encontrados, o vacío si no hay resultados. + * + * @description + * Ejecuta una consulta SQL que retorna los grupos de empleados de un cliente. + * Si ocurre un error durante la ejecución, se captura y se retorna un arreglo vacío. + */ +exports.obtenerGrupoDeEmpleados = async (idCliente) => { + const query = CONSULTAS_GRUPO_EMPLEADOS.OBTENER_LISTA; + + try { + const gruposDeEmpleados = await correrQuery(query, [idCliente]); + return gruposDeEmpleados; + } catch { + return []; + } +}; + +/** + * Verifica si ya existe un grupo de empleados con el mismo nombre para un cliente determinado. + * + * @function existeGrupoConNombre + * @param {string} nombreGrupo - Nombre del grupo a verificar. + * @param {number} idCliente - ID del cliente propietario del grupo. + * @returns {Promise} Retorna true si el grupo ya existe, false si no. + * + * @description + * Ejecuta una consulta SQL para validar si el nombre del grupo ya está registrado para ese cliente, + * útil para evitar duplicados al momento de crear nuevos grupos. + */ +exports.existeGrupoConNombre = (nombreGrupo, idCliente) => { + return new Promise((resolve, reject) => { + conexion.query( + CONSULTAS_GRUPO_EMPLEADOS.VALIDAR_NOMBRE_REPETIDO, + [nombreGrupo.trim(), idCliente], + (err, resultados) => { + if (err) return reject(err); + resolve(resultados.length > 0); + }, + ); + }); +}; \ No newline at end of file diff --git a/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js b/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js new file mode 100644 index 00000000..9a0741c1 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioImportarEmpleado.js @@ -0,0 +1,141 @@ +const db = require('@altertex/util/bd/db'); +const DEFAULT_ROLE_ID = 3; // ID del rol por defecto para empleados +const CONSULTAS_IMPORTAR_EMPLEADOS = require('@altertex/util/const/consultasImportarEmpleados'); +/** + * Importa en bloque múltiples empleados, creando sus usuarios, asignando rol y vinculación con clientes. + * + * @async + * @function importarEmpleadosMasivo + * @param {Array} empleados - Lista de objetos con los datos de usuario y empleado. + * @param {string} empleados[].nombreCompleto - Nombre completo del usuario. + * @param {string} empleados[].correoElectronico - Correo electrónico único del usuario. + * @param {string} empleados[].contrasena - Contraseña en texto plano (ya hasheada previo a la llamada). + * @param {string} empleados[].numeroTelefono - Teléfono del usuario (exactamente 10 dígitos). + * @param {string} empleados[].direccion - Dirección del usuario. + * @param {string} empleados[].fechaNacimiento - Fecha de nacimiento (YYYY-MM-DD). + * @param {string} empleados[].genero - Género del usuario. + * @param {boolean} empleados[].estatus - Estatus del usuario (true = activo, false = inactivo). + * @param {number|Array} empleados[].idCliente - Uno o varios IDs de cliente asociados. + * @param {string} empleados[].numeroEmergencia - Teléfono de emergencia. + * @param {string} empleados[].areaTrabajo - Área de trabajo del empleado. + * @param {string} empleados[].posicion - Puesto o cargo del empleado. + * @param {number} empleados[].cantidadPuntos - Puntos acumulados del empleado. + * @param {string} empleados[].antiguedad - Fecha de antigüedad/ingreso (YYYY-MM-DD). + * @throws {Error} Si el parámetro "empleados" no es un array válido o está vacío. + * @throws {Error} Si se detectan correos o teléfonos duplicados en la base de datos. + * @throws {Error} Si ocurre cualquier fallo durante la inserción en la transacción. + * + * @returns {Promise} Resuelve sin valor si la importación fue exitosa. + */ +exports.importarEmpleadosMasivo = async (empleados) => { + if (!Array.isArray(empleados) || empleados.length === 0) { + throw new Error('No se recibió ningún empleado para importar.'); + } + + // Obtenemos la conexión del pool + const conn = await db.getConnection(); + + try { + // Iniciamos la transacción + await conn.beginTransaction(); + + // 1) Validar correos duplicados en bloque + const correos = empleados.map((elemento) => elemento.correoElectronico); + const [correosExistentes] = await conn.query( + CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_CORREOS_DUPLICADOS, + [correos] + ); + if (correosExistentes.length > 0) { + const lista = correosExistentes.map((fila) => fila.correoElectronico).join(', '); + throw new Error(`Correos ya registrados: ${lista}`); + } + + // 2) Validar teléfonos duplicados en bloque + const telefonos = empleados.map((elemento) => elemento.numeroTelefono); + const [telefonosExistentes] = await conn.query( + CONSULTAS_IMPORTAR_EMPLEADOS.VALIDAR_TELEFONO_DUPLICADO, + [telefonos] + ); + if (telefonosExistentes.length > 0) { + const lista = telefonosExistentes.map((fila) => fila.numeroTelefono).join(', '); + throw new Error(`Teléfonos ya registrados: ${lista}`); + } + + // 3) Bulk‐insert de usuarios + const usuariosValues = empleados.map((elemento) => [ + elemento.nombreCompleto, + elemento.correoElectronico, + elemento.contrasena, + elemento.numeroTelefono, + elemento.direccion, + elemento.fechaNacimiento, + elemento.genero, + elemento.estatus, + ]); + await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_USUARIO_EN_VOLUMEN, [usuariosValues]); + + // 4) Recuperar los IDs generados + const [rowsUsuarios] = await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.OBTENER_ID_GENERADOS, [ + correos, + ]); + const idMap = rowsUsuarios.reduce((map, row) => { + map[row.correoElectronico] = row.idUsuario; + return map; + }, {}); + + // 5) Bulk‐insert de roles + const rolValues = empleados.map((elemento) => [ + idMap[elemento.correoElectronico], + DEFAULT_ROLE_ID, + ]); + await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_ROLES_EN_VOLUMEN, [rolValues]); + + // 6) Bulk‐insert de asociaciones usuario‐cliente + const clienteValues = []; + empleados.forEach((elemento) => { + const idU = idMap[elemento.correoElectronico]; + const listaClientes = Array.isArray(elemento.idCliente) + ? elemento.idCliente + : [elemento.idCliente]; + listaClientes.forEach((idCli) => clienteValues.push([idU, idCli])); + }); + await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_USUARIO_CLIENTE_EN_VOLUMEN, [ + clienteValues, + ]); + + // 7) Bulk‐insert de empleados + const empValues = empleados.map((elemento) => [ + idMap[elemento.correoElectronico], + elemento.idCliente, + elemento.numeroEmergencia, + elemento.areaTrabajo, + elemento.posicion, + parseFloat(elemento.cantidadPuntos), + elemento.antiguedad, + ]); + await conn.query(CONSULTAS_IMPORTAR_EMPLEADOS.INSERTAR_EMPLEADOS_EN_VOLUMEN, [empValues]); + + await conn.commit(); + } catch (err) { + await conn.rollback(); + const mensajeOriginal = err.message || ''; + + // Detectar error por entrada duplicada + const entradaDuplicada = mensajeOriginal.match(/Duplicate entry '(.+)' for key '(.+)'/); + + if (entradaDuplicada) { + const valorDuplicado = entradaDuplicada[1]; + const campo = entradaDuplicada[2]; + + let campoTraducido = campo; + if (campo.includes('correoElectronico')) campoTraducido = 'correo electrónico'; + else if (campo.includes('telefono')) campoTraducido = 'número de teléfono'; + + throw new Error(`La entrada ${campoTraducido} "${valorDuplicado}" esta duplicada`); + } + + throw new Error(`Error en importación masiva: ${mensajeOriginal}`); + } finally { + if (conn) conn.release(); + } +}; diff --git a/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js b/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js new file mode 100644 index 00000000..54831892 --- /dev/null +++ b/Empleados/Datos/Repositorios/repositorioLeerGrupoDeEmpleados.js @@ -0,0 +1,58 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_GRUPOS_EMPLEADOS = require('@altertex/util/const/consultasGrupoEmpleados'); + +/** + * Obtiene un grupo de empleados desde la base de datos mediante su ID. + * + * Ejecuta una consulta SQL y retorna el primer grupo de empleados encontrado o `null` si no existe. + * + * @param {number|string} idGrupo - ID del grupo a buscar. + * @returns {Promise} El grupo de empleados encontrado o `null` si no existe. + * @throws {Error} Si ocurre un error al ejecutar la consulta. + * + * @see RF[23] Lee grupo de empleados -https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF23 + */ +exports.obtenerGrupoEmpleadosPorId = async (idGrupo) => { + const query = CONSULTAS_GRUPOS_EMPLEADOS.LEER_GRUPO; + try { + const resultado = await correrQuery(query, [idGrupo]); + + if (resultado.length === 0) return null; + + // ✅ Manejo seguro de setProductosActualizar cuando es null o contiene objetos con valores null + const setProductosOriginal = resultado[0].setProductosActualizar || []; + + // Filtrar objetos que tienen id null (cuando no hay datos reales) + const setProductosValidos = setProductosOriginal.filter((obj) => obj.id !== null); + + const setProductosFiltrados = Object.values( + setProductosValidos.reduce((acc, obj) => { + acc[obj.id] = obj; // sobrescribe si ya existe ese id + return acc; + }, {}) + ); + + const grupoEmpleados = { + idGrupo: resultado[0].idGrupo, + nombre: resultado[0].nombre, + descripcion: resultado[0].descripcion, + setsProductos: resultado[0].setsProductos ? resultado[0].setsProductos.split(', ') : [], + idsSetProductos: resultado[0].idsSetProductos + ? resultado[0].idsSetProductos.split(', ').map(Number) + : [], + empleados: resultado[0].infoEmpleados ? resultado[0].infoEmpleados.split(' || ') : [], + idsEmpleados: resultado[0].idsEmpleados + ? resultado[0].idsEmpleados.split(',').map(Number) + : [], + empleadosActualizar: (resultado[0].empleadosActualizar || []).filter( + (obj) => obj.id !== null + ), + setProductosActualizar: setProductosFiltrados, // Ya no necesita validación adicional + }; + + return grupoEmpleados; + } catch (error) { + console.error('Error al obtener el grupo de empleados con id:', error); + throw error; + } +}; diff --git a/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js b/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js new file mode 100644 index 00000000..5b1496aa --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/actualizarEmpleado.routes.js @@ -0,0 +1,119 @@ +const express = require('express'); +const ruteador = express.Router(); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const controlador = require('@altertex/emp/ctrl/actualizarEmpleado.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +//RF[19] Actualizar Empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19] + +/** + * @swagger + * /api/empleados/actualizar: + * put: + * summary: Actualiza la información de uno o varios empleados. + * tags: + * - Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: array + * description: Array de objetos con información de empleados a actualizar + * items: + * type: object + * required: + * - idEmpleado + * - numeroEmergencia + * - areaTrabajo + * - posicion + * - cantidadPuntos + * - antiguedad + * properties: + * id: + * type: integer + * example: 50 + * idEmpleado: + * type: integer + * example: 50 + * idUsuario: + * type: integer + * example: 30 + * nombreCompleto: + * type: string + * example: "Angel Romero" + * correoElectronico: + * type: string + * example: "aromero@google.com" + * numeroEmergencia: + * type: string + * example: "9876543214" + * areaTrabajo: + * type: string + * example: "Ventas" + * posicion: + * type: string + * example: "Auxiliar" + * cantidadPuntos: + * type: integer + * example: 2 + * antiguedad: + * type: string + * example: "2000-02-10" + * responses: + * 200: + * description: Información del empleado actualizada correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Actualización exitosa." + * datos: + * type: array + * items: + * type: object + * 400: + * description: Error en los datos enviados o en la actualización. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al actualizar" + * x-codeSamples: + * - lang: JavaScript + * label: cURL + * source: | + * # Ejemplo enviando un array de empleados + * curl -X PUT "https://tu-api.com/api/empleados/actualizar" \ + * -H "x-api-key: TU_API_KEY" \ + * -H "Authorization: Bearer TU_TOKEN" \ + * -H "Content-Type: application/json" \ + * -d '[{"id":50,"idUsuario":30,"nombreCompleto":"Angel Romero","correoElectronico":"aromero@google.com","numeroEmergencia":"9876543214","areaTrabajo":"Ventas","posicion":"Auxiliar","cantidadPuntos":2,"antiguedad":"2000-02-10","idEmpleado":50}]' + */ + +ruteador.put( + RUTAS.EMPLEADOS.ACTUALIZAR, + revisarApiKey(), + validarYSanitizar, + autorizarToken, + limitePeticionesDiarias, + revisarPermisos(PERMISOS.ACTUALIZAR_EMPLEADO), + controlador.actualizarEmpleado +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/actualizarGrupoEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/actualizarGrupoEmpleados.routes.js new file mode 100644 index 00000000..d4c530bd --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/actualizarGrupoEmpleados.routes.js @@ -0,0 +1,118 @@ +const express = require('express'); +const ruteador = express.Router(); +const RUTAS = require('@altertex/util/const/rutas'); +const PERMISOS = require('@altertex/util/const/permisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const controlador = require('@altertex/emp/ctrl/actualizarGrupoEmpleado.controller'); +// RF[24] Actualiza grupo empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24] + +/** + * @swagger + * /api/empleados/actualizar-grupo: + * put: + * summary: Actualiza un grupo de empleados + * description: Actualiza el nombre, la descripción, los empleados y los sets de productos asociados a un grupo de empleados existente. + * tags: + * - Grupos de Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idGrupoEmpleado + * - nombre + * - descripcion + * - empleados + * - setsDeProductos + * properties: + * idGrupoEmpleado: + * type: integer + * example: 1 + * nombre: + * type: string + * example: "Grupo Administrativo" + * descripcion: + * type: string + * example: "Grupo para empleados administrativos" + * empleados: + * type: array + * items: + * type: integer + * example: [101, 102, 103] + * setsDeProductos: + * type: array + * items: + * type: integer + * example: [201, 202] + * responses: + * 200: + * description: Se actualizó correctamente el grupo de empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Se actualizo correctamente el grupo de empleados. + * 400: + * description: Error de validación o datos inválidos. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * examples: + * Formato inválido: + * value: + * mensaje: No se obtuvieron los datos correctamente. + * Verificación de empleados fallida: + * value: + * mensaje: Algunos empleados no pertenecen al mismo cliente que el grupo. + * Verificación de sets fallida: + * value: + * mensaje: Algunos sets de productos no pertenecen al cliente de este grupo. + * Actualización fallida: + * value: + * mensaje: Error actualizando el grupo de empleados. + * 403: + * description: No tiene permisos para realizar esta acción. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No tiene permiso para consultar grupos de empleados de este cliente. + * 500: + * description: Error interno del servidor. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Ocurrió un error al actualizar el grupo de empleados. + */ +ruteador.put( + RUTAS.EMPLEADOS.ACTUALIZAR_GRUPO_EMPLEADO, + revisarApiKey(), + autorizarToken, + verificarPermisos(PERMISOS.ACTUALIZAR_GRUPO_EMPLEADOS), + validarYSanitizar, + controlador.actualizarGrupoEmpleados +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/consultarLista.routes.js b/Empleados/Rutas/RutasIndividuales/consultarLista.routes.js new file mode 100644 index 00000000..b8afc64d --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/consultarLista.routes.js @@ -0,0 +1,116 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/consultarLista.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF17 - Consulta Lista Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF17 + */ + +/** + * @swagger + * /api/empleados/consultar-lista: + * post: + * summary: Consulta la lista de empleados de un cliente + * description: | + * Este endpoint permite obtener la lista completa de empleados registrados para un cliente específico. + * La consulta está protegida por API Key, token de sesión y verificación de permisos. + * tags: [Empleados] + * security: + * - ApiKeyAuth: [] + * responses: + * 200: + * description: Lista de empleados obtenida exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Lista de empleados obtenida exitosamente." + * empleados: + * type: array + * items: + * type: object + * properties: + * nombreCompleto: + * type: string + * example: "Ana Martínez" + * correoElectronico: + * type: string + * example: "ana.martinez@example.com" + * idEmpleado: + * type: integer + * example: 3 + * idUsuario: + * type: integer + * example: 3 + * idCliente: + * type: integer + * example: 101 + * numeroEmergencia: + * type: string + * example: "5587654321" + * areaTrabajo: + * type: string + * example: "Ventas" + * posicion: + * type: string + * example: "Asesor Comercial" + * cantidadPuntos: + * type: string + * example: "28.00" + * antiguedad: + * type: string + * format: date-time + * example: "2020-06-15T05:00:00.000Z" + * 400: + * description: Los parámetros enviados no son válidos. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Parámetros inválidos." + * 404: + * description: No se encontraron empleados registrados para el cliente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No se encontraron resultados." + * 500: + * description: Error interno al intentar consultar la lista de empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al consultar empleados." + */ + +ruteador.post( + RUTAS.EMPLEADOS.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_EMPLEADOS), + controlador.consultarLista +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/consultarListaGrupos.routes.js b/Empleados/Rutas/RutasIndividuales/consultarListaGrupos.routes.js new file mode 100644 index 00000000..3e2f7859 --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/consultarListaGrupos.routes.js @@ -0,0 +1,117 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/consultarListaGrupos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF22 - Consulta Lista de Grupo Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF22 + */ + +/** + * @swagger + * /api/empleados/consultar-grupo: + * post: + * summary: Consulta la lista de grupos de empleados de un cliente + * description: | + * Este endpoint permite consultar los grupos de empleados asociados a un cliente, especificando un límite + * y un offset para la paginación de resultados. + * tags: [Empleados] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * limit: + * type: integer + * description: Número máximo de resultados a devolver. + * example: 10 + * offset: + * type: integer + * description: Número de resultados a omitir (para paginación). + * example: 0 + * responses: + * 200: + * description: Consulta exitosa. Se devuelve la lista de grupos de empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Lista de grupos de empleados obtenida exitosamente." + * grupoEmpleados: + * type: array + * items: + * type: object + * properties: + * idGrupo: + * type: integer + * example: 3 + * geNombre: + * type: string + * example: "Calidad Toyota" + * descripcion: + * type: string + * example: "Encargados de la calidad y los controles en el proceso de fabricación." + * idSetProducto: + * type: integer + * example: 3 + * spNombre: + * type: string + * example: "Set Calidad Toyota" + * totalEmpleados: + * type: integer + * example: 1 + * 400: + * description: Los parámetros 'limit' o 'offset' son inválidos o faltan. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Parámetros inválidos." + * 404: + * description: No se encontraron resultados para la consulta. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No se encontraron resultados." + * 500: + * description: Error en el servidor al intentar obtener la lista de empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al consultar empleados." + */ + +ruteador.post( + RUTAS.EMPLEADOS.CONSULTAR_GRUPO, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_GRUPOS_EMPLEADOS), + controlador.consultarLista +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/crearEmpleado.routes.js b/Empleados/Rutas/RutasIndividuales/crearEmpleado.routes.js new file mode 100644 index 00000000..f883d48f --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/crearEmpleado.routes.js @@ -0,0 +1,204 @@ +//RF16 - Crear Empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF16] + +const express = require('express'); +const ruteador = express.Router(); + +const controlador = require('@altertex/emp/ctrl/crearEmpleado.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +/** + * @swagger + * /api/empleados/crear: + * post: + * summary: Crea un nuevo empleado. + * tags: + * - Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * oneOf: + * - type: object + * description: Información del nuevo empleado a crear directamente en el body + * required: + * - idUsuario + * - idCliente + * - nombreCompleto + * - correoElectronico + * - numeroEmergencia + * - areaTrabajo + * - posicion + * - cantidadPuntos + * - antiguedad + * properties: + * idUsuario: + * type: integer + * example: 30 + * idCliente: + * type: integer + * example: 10 + * nombreCompleto: + * type: string + * example: "Carlos Pérez" + * correoElectronico: + * type: string + * example: "cperez@google.com" + * numeroEmergencia: + * type: string + * example: "9876543214" + * areaTrabajo: + * type: string + * example: "Producción" + * posicion: + * type: string + * example: "Operador" + * cantidadPuntos: + * type: integer + * example: 0 + * antiguedad: + * type: string + * example: "2024-01-01" + * - type: object + * properties: + * empleados: + * oneOf: + * - type: object + * description: Array único con información del empleado + * required: + * - idUsuario + * - idCliente + * - nombreCompleto + * - correoElectronico + * - numeroEmergencia + * - areaTrabajo + * - posicion + * - cantidadPuntos + * - antiguedad + * properties: + * idUsuario: + * type: integer + * example: 30 + * idCliente: + * type: integer + * example: 10 + * nombreCompleto: + * type: string + * example: "Carlos Pérez" + * correoElectronico: + * type: string + * example: "cperez@google.com" + * numeroEmergencia: + * type: string + * example: "9876543214" + * areaTrabajo: + * type: string + * example: "Producción" + * posicion: + * type: string + * example: "Operador" + * cantidadPuntos: + * type: integer + * example: 0 + * antiguedad: + * type: string + * example: "2024-01-01" + * - type: array + * description: Array de objetos con información de empleados + * items: + * type: object + * required: + * - idUsuario + * - idCliente + * - nombreCompleto + * - correoElectronico + * - numeroEmergencia + * - areaTrabajo + * - posicion + * - cantidadPuntos + * - antiguedad + * properties: + * idUsuario: + * type: integer + * example: 30 + * idCliente: + * type: integer + * example: 10 + * nombreCompleto: + * type: string + * example: "Carlos Pérez" + * correoElectronico: + * type: string + * example: "cperez@google.com" + * numeroEmergencia: + * type: string + * example: "9876543214" + * areaTrabajo: + * type: string + * example: "Producción" + * posicion: + * type: string + * example: "Operador" + * cantidadPuntos: + * type: integer + * example: 0 + * antiguedad: + * type: string + * example: "2024-01-01" + * responses: + * 201: + * description: Empleado creado correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Empleado creado exitosamente." + * datos: + * type: array + * items: + * type: object + * 400: + * description: Error en los datos enviados o en la creación. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al crear empleado" + * x-codeSamples: + * - lang: JavaScript + * label: cURL + * source: | + * # Ejemplo enviando datos directamente + * curl -X POST "https://tu-api.com/api/empleados/crear" \ + * -H "x-api-key: TU_API_KEY" \ + * -H "Authorization: Bearer TU_TOKEN" \ + * -H "Content-Type: application/json" \ + * -d '{"idUsuario":30,"idCliente":10,"nombreCompleto":"Carlos Pérez","correoElectronico":"cperez@google.com","numeroEmergencia":"9876543214","areaTrabajo":"Producción","posicion":"Operador","cantidadPuntos":0,"antiguedad":"2024-01-01"}' + */ + +ruteador.post( + RUTAS.EMPLEADOS.CREAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + validarYSanitizar, + verificarPermisos(PERMISOS.CREAR_EMPLEADO), + controlador.crearEmpleado +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/crearGrupoEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/crearGrupoEmpleados.routes.js new file mode 100644 index 00000000..24ee338d --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/crearGrupoEmpleados.routes.js @@ -0,0 +1,102 @@ +// RF21 - Crear Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF21 + +/** + * @file crearGrupoEmpleados.routes.js + * @description + * Define la ruta para crear un grupo de empleados. + * Aplica middlewares de validación, autenticación y autorización antes de delegar al controlador. + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/crearGrupoEmpleados.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/empleados/crear-grupo: + * post: + * summary: Crear un grupo de empleados. + * description: Crea un nuevo grupo de empleados y asigna empleados a ese grupo. + * tags: + * - Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * nombreGrupo: + * type: string + * example: "Grupo de Ventas" + * descripcion: + * type: string + * example: "Grupo encargado de las ventas del mes de enero" + * idCliente: + * type: integer + * example: 123 + * listaEmpleados: + * type: array + * items: + * type: integer + * example: [1, 2, 3] + * responses: + * 201: + * description: Grupo de empleados creado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Grupo de empleados creado exitosamente." + * idGrupo: + * type: integer + * example: 456 + * 400: + * description: Datos incompletos o inválidos. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Faltan datos requeridos: nombre del grupo o lista de empleados." + * 500: + * description: Error interno al crear el grupo de empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Ocurrió un error al crear el grupo de empleados." + */ + +// Ruta para crear grupo de empleados +ruteador.post( + RUTAS.EMPLEADOS.CREAR_GRUPO, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CREAR_GRUPO_EMPLEADOS), + controlador.crearGrupoEmpleados +); + +// Exportación del ruteador +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/eliminarEmpleado.routes.js b/Empleados/Rutas/RutasIndividuales/eliminarEmpleado.routes.js new file mode 100644 index 00000000..2f38cf65 --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/eliminarEmpleado.routes.js @@ -0,0 +1,80 @@ +// RF[20] Elimina empleado - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF20 + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/eliminarEmpleado.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/empleados/eliminar: + * delete: + * summary: Eliminar uno o varios empleados. + * description: Elimina empleados según su ID. Requiere autenticación y permisos. + * tags: + * - Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idsEmpleado: + * type: array + * items: + * type: integer + * example: [3, 5, 10] + * responses: + * 200: + * description: Empleados eliminados exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Empleados eliminados correctamente. + * 404: + * description: Empleados no encontrados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No se encontraron los empleados especificados. + * 500: + * description: Error interno al eliminar empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al eliminar empleados. + */ + +ruteador.delete( + RUTAS.EMPLEADOS.ELIMINAR_EMPLEADO, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_EMPLEADO), + controlador.eliminarEmpleado +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Empleados/Rutas/RutasIndividuales/eliminarGrupoEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/eliminarGrupoEmpleados.routes.js new file mode 100644 index 00000000..61bab347 --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/eliminarGrupoEmpleados.routes.js @@ -0,0 +1,86 @@ +//RF25 - Eliminar Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF25 + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/eliminarGrupoEmpleados.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/empleados/eliminar-grupo: + * post: + * tags: + * - Empleados + * summary: Eliminar uno o varios grupos de empleados + * description: | + * Elimina uno o más grupos de empleados, junto con sus relaciones asociadas. + * Requiere autenticación por API Key y Token JWT, además de permisos específicos. + * **Requisito funcional:** [RF25 - Eliminar Grupo de Empleados](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF25) + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idsGrupo + * properties: + * idsGrupo: + * type: array + * items: + * type: integer + * description: Lista de IDs de grupos a eliminar + * example: + * idsGrupo: [1, 2, 3] + * responses: + * 200: + * description: Grupos eliminados exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Grupo de empleados eliminado exitosamente. + * 400: + * description: Solicitud inválida (por ejemplo, sin IDs o formato incorrecto) + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al eliminar grupo de empleados. + * 500: + * description: Error inesperado del servidor + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error interno al procesar la eliminación. + */ + +ruteador.post( + RUTAS.EMPLEADOS.ELIMINAR_GRUPO, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_GRUPO_EMPLEADOS), + controlador.eliminarGrupoEmpleados +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/exportarEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/exportarEmpleados.routes.js new file mode 100644 index 00000000..a51ccfe8 --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/exportarEmpleados.routes.js @@ -0,0 +1,77 @@ +//RF59 - Exportar Empleados- https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59 + +/** + * @swagger + * /api/empleados/exportar: + * post: + * summary: Exporta la lista completa de empleados en formato CSV. + * tags: + * - Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * parameters: + * - in: header + * name: x-api-key + * required: true + * schema: + * type: string + * description: Clave de API + * - in: header + * name: Authorization + * required: true + * schema: + * type: string + * description: Token JWT con formato "Bearer " + * responses: + * 200: + * description: Archivo CSV generado correctamente. + * content: + * text/csv: + * schema: + * type: string + * format: binary + * 204: + * description: No hay empleados para exportar. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No hay empleados para exportar. + * 400: + * description: Error interno del servidor al exportar empleados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al exportar la lista de empleados. + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/exportarEmpleados.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +ruteador.post( + RUTAS.EMPLEADOS.EXPORTAR_EMPLEADOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + validarYSanitizar, + verificarPermisos(PERMISOS.EXPORTAR_EMPLEADOS), + controlador.exportarEmpleados +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Empleados/Rutas/RutasIndividuales/importarEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/importarEmpleados.routes.js new file mode 100644 index 00000000..9fd467f5 --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/importarEmpleados.routes.js @@ -0,0 +1,170 @@ +//RF57 - Importar Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF57 + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/importarEmpleados.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +/** + * @swagger + * /api/empleados/importar-empleados: + * post: + * summary: Importa múltiples empleados desde un JSON derivado de CSV. + * tags: + * - Empleados + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * parameters: + * - in: header + * name: x-api-key + * required: true + * schema: + * type: string + * description: Clave de API + * - in: header + * name: Authorization + * required: true + * schema: + * type: string + * description: Token JWT "Bearer " + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * required: + * - nombreCompleto + * - correoElectronico + * - contrasena + * - numeroTelefono + * - direccion + * - fechaNacimiento + * - genero + * - estatus + * - idCliente + * - numeroEmergencia + * - areaTrabajo + * - posicion + * - cantidadPuntos + * - antiguedad + * properties: + * nombreCompleto: + * type: string + * example: Juan Pérez + * correoElectronico: + * type: string + * format: email + * example: juan.perez@correo.com + * contrasena: + * type: string + * example: Password123! + * numeroTelefono: + * type: string + * example: 5512345678 + * direccion: + * type: string + * example: Av. Siempre Viva 742 + * fechaNacimiento: + * type: string + * format: date + * example: 1990-05-12 + * genero: + * type: string + * example: M + * estatus: + * type: boolean + * example: true + * idCliente: + * oneOf: + * - type: integer + * - type: array + * items: + * type: integer + * example: 101 + * numeroEmergencia: + * type: string + * example: 5512340000 + * areaTrabajo: + * type: string + * example: Logística + * posicion: + * type: string + * example: Analista de datos + * cantidadPuntos: + * type: number + * example: 120.5 + * antiguedad: + * type: string + * format: date + * example: 2020-01-15 + * responses: + * 200: + * description: Todos los empleados importados correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Todos los empleados importados correctamente. + * 207: + * description: Importación parcial con errores en algunas filas. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Importación parcial con errores. + * errores: + * type: array + * items: + * type: object + * properties: + * fila: + * oneOf: + * - type: integer + * - type: string + * example: 3 + * error: + * type: string + * example: Correo inválido. + * 400: + * description: Petición inválida, cuerpo vacío o no es un array. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No se recibieron empleados. + * 499: + * description: Cliente abortó la petición. + * 500: + * description: Error interno del servidor. + */ + +ruteador.post( + RUTAS.EMPLEADOS.IMPORTAR_EMPLEADOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + validarYSanitizar, + verificarPermisos(PERMISOS.IMPORTAR_EMPLEADOS), + controlador.importarEmpleados +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/RutasIndividuales/leerGrupoEmpleados.routes.js b/Empleados/Rutas/RutasIndividuales/leerGrupoEmpleados.routes.js new file mode 100644 index 00000000..48da58bc --- /dev/null +++ b/Empleados/Rutas/RutasIndividuales/leerGrupoEmpleados.routes.js @@ -0,0 +1,117 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/emp/ctrl/leerGrupoEmpleados.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF[23] Lee grupo de empleados -https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF23 + */ + +/** + * @swagger + * /api/empleados/leer-grupo: + * post: + * summary: Leer grupo de empleados. + * description: | + * Obtiene información sobre un grupo de empleados basado en los parámetros proporcionados. Requiere autenticación y permisos específicos. + * tags: [Empleados] + * security: + * - ApiKeyAuth: [] + * responses: + * 200: + * description: Información del grupo de empleados obtenida exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Grupo de empleados obtenido correctamente. + * data: + * type: object + * properties: + * idGrupo: + * type: integer + * example: 1 + * nombre: + * type: string + * example: "Grupo Ventas" + * descripcion: + * type: string + * example: "Grupo encargado de las ventas regionales" + * setsProductos: + * type: array + * items: + * type: object + * properties: + * idSetProducto: + * type: integer + * example: 101 + * nombreSet: + * type: string + * example: "Set de Productos A" + * empleados: + * type: array + * items: + * type: object + * properties: + * idEmpleado: + * type: integer + * example: 201 + * nombre: + * type: string + * example: "Juan Pérez" + * departamento: + * type: string + * example: "Ventas" + * 400: + * description: Solicitud inválida. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Parámetros inválidos. + * 401: + * description: No autorizado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No tienes permisos para realizar esta acción. + * 500: + * description: Error interno del servidor. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al obtener el grupo de empleados. + */ +ruteador.post( + RUTAS.EMPLEADOS.LEER_GRUPO, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.LEER_GRUPO_EMPLEADOS), + controlador.leerGrupoEmpleados +); + +module.exports = ruteador; diff --git a/Empleados/Rutas/indexEmpleados.routes.js b/Empleados/Rutas/indexEmpleados.routes.js new file mode 100644 index 00000000..7f39b6f5 --- /dev/null +++ b/Empleados/Rutas/indexEmpleados.routes.js @@ -0,0 +1,39 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutasConsultarListaGrupos = require('@altertex/emp/rutasInd/consultarListaGrupos.routes'); +const rutasCrearEmpleado = require('@altertex/emp/rutasInd/crearEmpleado.routes'); +const rutasConsultarLista = require('@altertex/emp/rutasInd/consultarLista.routes'); +const rutasEliminarGrupo = require('@altertex/emp/rutasInd/eliminarGrupoEmpleados.routes'); +const rutasEliminarEmpleado = require('@altertex/emp/rutasInd/eliminarEmpleado.routes'); +const rutasImportarEmpleados = require('@altertex/emp/rutasInd/importarEmpleados.routes'); +const rutasExportarEmpleados = require('@altertex/emp/rutasInd/exportarEmpleados.routes'); +const rutasLeerGrupoEmpleados = require('@altertex/emp/rutasInd/leerGrupoEmpleados.routes'); +const rutasCrearGrupo = require('@altertex/emp/rutasInd/crearGrupoEmpleados.routes'); +const rutasActualizarEmpleado = require('@altertex/emp/rutasInd/actualizarEmpleado.routes'); +const rutasActualizarGrupo = require('@altertex/emp/rutasInd/actualizarGrupoEmpleados.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +//RF22 - Consulta Lista de Grupo Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF22 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasConsultarListaGrupos); +//RF17 - Consulta Lista Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF17 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasConsultarLista); +//RF25 - Eliminar Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF25 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasEliminarGrupo); +//RF20 - Eliminar Empleado - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF20 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasEliminarEmpleado); +//RF57 - Importar Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF57 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasImportarEmpleados); +//RF23 Lee grupo de empleados -https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF23 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasLeerGrupoEmpleados); +//RF21 - Crear Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF21 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasCrearGrupo); +//RF19 - Actualizar Empleado - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasActualizarEmpleado); +//RF24 - Actualizar Grupo de Empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF24 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasActualizarGrupo); +//RF59 - Exportar Empleados- https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasExportarEmpleados); +//RF16 - Crear Empleado - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF16 +ruteador.use(RUTAS.EMPLEADOS.BASE, rutasCrearEmpleado); +module.exports = ruteador; diff --git a/Eventos/Controladores/consultarEvento.controller.js b/Eventos/Controladores/consultarEvento.controller.js new file mode 100644 index 00000000..b7503273 --- /dev/null +++ b/Eventos/Controladores/consultarEvento.controller.js @@ -0,0 +1,44 @@ +const repositorio = require('@altertex/eve/repos/repositorioConsultarEvento'); +const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos'); + +/** + * Lee los detalles de un evento desde la base de datos utilizando su ID. + * + * Valida el parámetro `idEvento` y obtiene la información del evento a través del repositorio. + * Si el evento no es encontrado o el parámetro es inválido, retorna un error. + * + * @param {Express.Request} req - La solicitud HTTP que contiene el `idEvento` en el cuerpo. + * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente. + * @returns {Promise} Responde con el evento encontrado o un mensaje de error. + * + * @see [RF38 Leer evento](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF38) + * + */ +exports.consultarEvento = async (req, res) => { + const idEvento = parseInt(req.body.idEvento); + + if (isNaN(idEvento)) { + return res + .status(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const evento = await repositorio.obtenerEventoPorId(idEvento); + + if (!evento) { + return res + .status(MENSAJES_EVENTOS.EVENTO_NO_ENCONTRADO.codigo) + .json({ mensaje: MENSAJES_EVENTOS.EVENTO_NO_ENCONTRADO.mensaje }); + } + + return res.status(MENSAJES_EVENTOS.EVENTO_OBTENIDO.codigo).json({ + mensaje: MENSAJES_EVENTOS.EVENTO_OBTENIDO.mensaje, + evento, + }); + } catch { + return res + .status(MENSAJES_EVENTOS.ERROR_OBTENER_EVENTO.codigo) + .json({ mensaje: MENSAJES_EVENTOS.ERROR_OBTENER_EVENTO.mensaje }); + } +}; diff --git a/Eventos/Controladores/consultarListaEventos.controller.js b/Eventos/Controladores/consultarListaEventos.controller.js new file mode 100644 index 00000000..47f4d247 --- /dev/null +++ b/Eventos/Controladores/consultarListaEventos.controller.js @@ -0,0 +1,40 @@ +//RF37 Consulta Lista de Eventos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF37] + +const repositorio = require('@altertex/eve/repos/repositorioConsultarListaEventos'); +const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos'); + +/** + * Obtiene la lista de eventos asociados al cliente del usuario autenticado + * @function consultarListaEventos + * @param {object} req - Objeto de solicitud Express + * @param {object} res - Objeto de respuesta Express + * @returns {object} Respuesta JSON con la lista de eventos o mensaje de error + */ +exports.consultarListaEventos = async (req, res) => { + try { + const idCliente = parseInt(req.user.clienteSeleccionado); + + if (isNaN(idCliente)) { + return res.status(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: 'ID del cliente no válido o no seleccionado', + }); + } + + const resultados = await repositorio.consultarListaEventos(idCliente); + + if (!resultados || resultados.length === 0) { + return res.status(MENSAJES_EVENTOS.EVENTOS_NO_ENCONTRADOS.codigo).json({ + mensaje: MENSAJES_EVENTOS.EVENTOS_NO_ENCONTRADOS.mensaje, + }); + } + + return res.status(MENSAJES_EVENTOS.LISTA_EVENTOS_OBTENIDA.codigo).json({ + mensaje: MENSAJES_EVENTOS.LISTA_EVENTOS_OBTENIDA.mensaje, + listaEventos: resultados, + }); + } catch { + return res + .status(MENSAJES_EVENTOS.ERROR_OBTENER_EVENTOS.codigo) + .json({ mensaje: MENSAJES_EVENTOS.ERROR_OBTENER_EVENTOS.mensaje }); + } +}; diff --git a/Eventos/Controladores/crearEvento.controller.js b/Eventos/Controladores/crearEvento.controller.js new file mode 100644 index 00000000..fa769be6 --- /dev/null +++ b/Eventos/Controladores/crearEvento.controller.js @@ -0,0 +1,74 @@ +// RF36 - Crear Evento - [https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF36] + +const repositorio = require('@altertex/eve/repos/repositorioCrearEvento'); +const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos'); + +/** + * Controlador para crear un nuevo evento. + * + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud que contiene los datos del evento. + * @param {string} req.body.idCliente - ID del cliente asociado al evento. + * @param {string} req.body.nombre - Nombre del evento. + * @param {string} req.body.descripcion - Descripción del evento. + * @param {number} req.body.puntos - Puntos otorgados por el evento. + * @param {number} req.body.multiplicador - Multiplicador de puntos del evento. + * @param {string} req.body.periodoRenovacion - Periodo de renovación del evento. + * @param {boolean} req.body.renovacion - Indica si el evento se renueva automáticamente. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {object} - Respuesta JSON con el resultado de la operación. + */ +exports.crearEvento = async (req, res) => { + try { + const { idCliente, nombre, descripcion, puntos, multiplicador, periodoRenovacion, renovacion } = req.body; + + // Validaciones básicas de campos requeridos (descripcion y periodoRenovacion son opcionales) + if (!idCliente || !nombre || !puntos || !multiplicador) { + + return res.status(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo).json({ + codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo, + mensaje: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.mensaje, + }); + } + + // Validar los datos de entrada + const idClienteNum = parseInt(idCliente, 10); + const puntosNum = parseFloat(puntos); + const multiplicadorNum = parseFloat(multiplicador); + + // Validaciones de formato + if (isNaN(idClienteNum) || idClienteNum <= 0) { + return res.status(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo).json({ + codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo, + mensaje: 'El ID del cliente debe ser un número válido mayor a 0.', + }); + } + + const nuevoEvento = { + idCliente: idClienteNum, + nombre, + descripcion: descripcion && descripcion.trim() !== '' ? descripcion : null, + puntos: puntosNum, + multiplicador: multiplicadorNum, + periodoRenovacion: periodoRenovacion && periodoRenovacion.trim() !== '' ? periodoRenovacion : null, + renovacion: renovacion ? 1 : 0, + }; + + const resultado = await repositorio.crearEvento(nuevoEvento); + + // Verificar si el evento fue creado exitosamente + return res.status(MENSAJES_EVENTOS.EVENTO_CREADO.codigo).json({ + codigo: MENSAJES_EVENTOS.EVENTO_CREADO.codigo, + mensaje: MENSAJES_EVENTOS.EVENTO_CREADO.mensaje, + evento: resultado?.evento || resultado, + }); + + } catch (error) { + // Usar el mensaje personalizado del error en la respuesta + return res.status(MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo).json({ + codigo: MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo, + mensaje: error.message || MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.mensaje, + }); + } +}; diff --git a/Eventos/Controladores/eliminarEvento.controller.js b/Eventos/Controladores/eliminarEvento.controller.js new file mode 100644 index 00000000..9f869ca0 --- /dev/null +++ b/Eventos/Controladores/eliminarEvento.controller.js @@ -0,0 +1,52 @@ +//RF40 Eliminar Evento - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF40] +const repositorio = require('@altertex/eve/repos/repositorioEliminarEvento'); +const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos'); + +/** + * Elimina un evento específico para un cliente + * @function eliminarEvento + * @param {object} req - Objeto de solicitud Express + * @param {object} res - Objeto de respuesta Express + * @returns {object} Respuesta JSON con confirmación o mensaje de error + */ +exports.eliminarEvento = async (req, res) => { + try { + const idsEvento = req.body.idsEvento; + + if (!Array.isArray(idsEvento) || idsEvento.length === 0) { + return res.status(MENSAJES_EVENTOS.EVENTO_NO_ENCONTRADO.codigo).json({ + mensaje: MENSAJES_EVENTOS.EVENTO_NO_ENCONTRADO.mensaje, + }); + } + + let eliminados = 0; + const noEncontrados = []; + + await Promise.all( + idsEvento.map(async (idEvento) => { + const resultadoEvento = await repositorio.eliminarEvento(idEvento); + if (resultadoEvento.affectedRows === 0) { + noEncontrados.push(idEvento); + } else { + eliminados += 1; + } + }) + ); + + if (eliminados === 0) { + return res.status(MENSAJES_EVENTOS.EVENTO_NO_ENCONTRADO.codigo).json({ + mensaje: MENSAJES_EVENTOS.EVENTO_NO_ENCONTRADO.mensaje, + noEncontrados, + }); + } + + return res.status(MENSAJES_EVENTOS.EVENTO_ELIMINADO.codigo).json({ + mensaje: MENSAJES_EVENTOS.EVENTO_ELIMINADO.mensaje, + noEncontrados: noEncontrados.length ? noEncontrados : undefined, + }); + } catch { + return res.status(MENSAJES_EVENTOS.ERROR_ELIMINAR_EVENTO.codigo).json({ + mensaje: MENSAJES_EVENTOS.ERROR_ELIMINAR_EVENTO.mensaje, + }); + } +}; diff --git a/Eventos/Datos/Repositorios/repositorioConsultarEvento.js b/Eventos/Datos/Repositorios/repositorioConsultarEvento.js new file mode 100644 index 00000000..a72c8c4f --- /dev/null +++ b/Eventos/Datos/Repositorios/repositorioConsultarEvento.js @@ -0,0 +1,33 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_EVENTOS = require('@altertex/util/const/consultasEventos'); + +/** + * Obtiene un evento desde la base de datos mediante su ID. + * + * Ejecuta una consulta SQL y retorna el primer evento encontrado o `null` si no existe. + * + * @param {number|string} idEvento - ID del evento a buscar. + * @returns {Promise} El evento encontrado o `null` si no existe. + * @throws {Error} Si ocurre un error al ejecutar la consulta. + * + * @see [RF38 Leer evento](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF38) + */ +exports.obtenerEventoPorId = async (idEvento) => { + const query = CONSULTAS_EVENTOS.LEER_EVENTO; + + const resultado = await correrQuery(query, [idEvento]); + + if (resultado.length === 0) return null; + + const evento = { + idEvento: resultado[0].idEvento, + nombre: resultado[0].nombre, + descripcion: resultado[0].descripcion, + puntos: resultado[0].puntos, + multiplicador: resultado[0].multiplicador, + periodoRenovacion: resultado[0].periodoRenovacion, + renovacion: resultado[0].renovacion, + }; + + return evento; +}; diff --git a/Eventos/Datos/Repositorios/repositorioConsultarListaEventos.js b/Eventos/Datos/Repositorios/repositorioConsultarListaEventos.js new file mode 100644 index 00000000..65dd961f --- /dev/null +++ b/Eventos/Datos/Repositorios/repositorioConsultarListaEventos.js @@ -0,0 +1,20 @@ +//RF37 Consulta Lista de Eventos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF37] +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_EVENTOS = require('@altertex/util/const/consultasEventos'); + +/** + * Obtiene la lista de eventos de un cliente específico de la base de datos + * @function consultarListaEventos + * @param {number} clienteSeleccionado - ID del cliente seleccionado + * @returns {Array} - Lista de eventos encontrados + */ +exports.consultarListaEventos = async (clienteSeleccionado) => { + const query = CONSULTAS_EVENTOS.OBTENER_LISTA_EVENTOS; + + try { + const listaEventos = await correrQuery(query, [clienteSeleccionado]); + return listaEventos; + } catch { + throw new Error('Error obteniendo la lista de eventos.'); + } +}; diff --git a/Eventos/Datos/Repositorios/repositorioCrearEvento.js b/Eventos/Datos/Repositorios/repositorioCrearEvento.js new file mode 100644 index 00000000..55d09b47 --- /dev/null +++ b/Eventos/Datos/Repositorios/repositorioCrearEvento.js @@ -0,0 +1,64 @@ +// RF36 - Crear Evento - [https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF36] + +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_EVENTOS = require('@altertex/util/const/consultasEventos'); +const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos'); + +/** + * Crea un nuevo evento en la base de datos + * @function crearEvento + * @param {object} evento - Objeto que contiene los datos del evento a crear + * @param {number} evento.idCliente - ID del cliente asociado al evento + * @param {string} evento.nombre - Nombre del evento + * @param {string} evento.descripcion - Descripción del evento + * @param {number} evento.puntos - Puntos asociados al evento + * @param {number} evento.multiplicador - Multiplicador del evento + * @param {number} evento.periodoRenovacion - Periodo de renovación del evento + * @param {number} evento.renovacion - Renovación del evento + * @returns {object} - Resultado de la operación de creación + * @throws {Error} - Error personalizado según el problema encontrado + */ +exports.crearEvento = async ({ + idCliente, + nombre, + descripcion, + puntos, + multiplicador, + periodoRenovacion, + renovacion, +}) => { + try { + const query = CONSULTAS_EVENTOS.CREAR_EVENTO; + + const resultado = await correrQuery(query, [ + idCliente, + nombre, + descripcion, + puntos, + multiplicador, + periodoRenovacion, + renovacion, + ]); + + // Si no hay resultado + if (!resultado) { + throw new Error(MENSAJES_EVENTOS.ERROR_INESPERADO.mensaje); + } + + return resultado; + } catch (error) { + // Si es un error que ya hemos generado, lo lanzamos tal cual + const mensajesError = Object.values(MENSAJES_EVENTOS).map(msg => msg.mensaje); + if (mensajesError.includes(error.message)) { + throw error; + } + + // Interpretamos posibles errores SQL - solo nos interesa si el cliente no existe + if (error.message.includes('foreign key constraint') || error.message.includes('FOREIGN KEY')) { + throw new Error(MENSAJES_EVENTOS.ERROR_CLIENTE_NO_EXISTE.mensaje); + } + + // Si es otro tipo de error, lanzamos un error genérico + throw new Error(`${MENSAJES_EVENTOS.ERROR_INESPERADO.mensaje}: ${error.message}`); + } +}; diff --git a/Eventos/Datos/Repositorios/repositorioEliminarEvento.js b/Eventos/Datos/Repositorios/repositorioEliminarEvento.js new file mode 100644 index 00000000..fa63140d --- /dev/null +++ b/Eventos/Datos/Repositorios/repositorioEliminarEvento.js @@ -0,0 +1,40 @@ +//RF40 Eliminar Evento - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF40] +const db = require('@altertex/util/bd/db'); +const CONSULTAS_EVENTOS = require('@altertex/util/const/consultasEventos'); + +/** + * Elimina un evento específico de la base de datos + * @function eliminarEvento + * @param {number} idEvento - ID del evento a eliminar + * @returns {object} - Resultado de la operación de eliminación + */ +exports.eliminarEvento = async (idEvento) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const [resultadosEmpleadosEventos] = await conexion.query( + CONSULTAS_EVENTOS.ELIMINAR_EMPLEADO_EVENTO, + [idEvento] + ); + + const [resultadosEventos] = await conexion.query( + CONSULTAS_EVENTOS.ELIMINAR_EVENTO, + [idEvento] + ); + + await conexion.commit(); + + return { + affectedRows: resultadosEventos.affectedRows, + resultadosEventos, + resultadosEmpleadosEventos, + }; + } catch { + await conexion.rollback(); + throw new Error('Error eliminando evento'); + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js b/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js new file mode 100644 index 00000000..94653d09 --- /dev/null +++ b/Eventos/Rutas/RutasIndividuales/consultarEvento.routes.js @@ -0,0 +1,103 @@ +// RF38 - Leer Evento - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF38] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/eve/ctrl/consultarEvento.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/eventos/consultar-evento: + * post: + * summary: Muestra la información de un evento específico. + * description: Obtiene los detalles completos de un evento usando su ID. + * tags: [Eventos] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - idEvento + * properties: + * idEvento: + * type: integer + * example: 123 + * description: Identificador único del evento + * responses: + * 200: + * description: Evento encontrado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Información del evento obtenida exitosamente." + * evento: + * type: object + * properties: + * idEvento: + * type: integer + * example: 123 + * nombre: + * type: string + * example: "Entrega Puntual" + * descripcion: + * type: string + * example: "Se otorgan puntos por cumplir con los tiempos de entrega en producción." + * puntos: + * type: integer + * example: 10 + * multiplicador: + * type: number + * example: 1.5 + * periodoRenovacion: + * type: string + * example: "Mensual" + * renovacion: + * type: boolean + * example: true + * 404: + * description: Evento no encontrado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Evento no encontrado." + * 500: + * description: Error interno del servidor al leer el evento. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Ocurrió un error al leer el evento." + */ +ruteador.post( + RUTAS.EVENTOS.CONSULTAR_EVENTO, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.LEER_EVENTO), + controlador.consultarEvento +); + +module.exports = ruteador; diff --git a/Eventos/Rutas/RutasIndividuales/consultarListaEventos.routes.js b/Eventos/Rutas/RutasIndividuales/consultarListaEventos.routes.js new file mode 100644 index 00000000..40f767c2 --- /dev/null +++ b/Eventos/Rutas/RutasIndividuales/consultarListaEventos.routes.js @@ -0,0 +1,71 @@ +//RF37 Consulta Lista de Eventos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF37] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/eve/ctrl/consultarListaEventos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/eventos/consultar-lista-eventos: + * post: + * summary: Obtener lista de eventos + * tags: [Eventos] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Lista de eventos obtenida exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Lista de eventos obtenida exitosamente + * lista_eventos: + * type: array + * items: + * type: object + * properties: + * idEvento: + * type: integer + * nombre: + * type: string + * descripcion: + * type: string + * puntos: + * type: number + * periodoRenovacion: + * type: string + * renovacion: + * type: boolean + * 204: + * description: No se encontraron eventos + * 400: + * description: Parámetros inválidos + * 401: + * description: No autorizado + * 403: + * description: Acceso denegado + * 500: + * description: Error al obtener eventos + */ +ruteador.post( + RUTAS.EVENTOS.CONSULTAR_LISTA_EVENTOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_EVENTO), + controlador.consultarListaEventos +); + +module.exports = ruteador; diff --git a/Eventos/Rutas/RutasIndividuales/crearEvento.routes.js b/Eventos/Rutas/RutasIndividuales/crearEvento.routes.js new file mode 100644 index 00000000..97feadf0 --- /dev/null +++ b/Eventos/Rutas/RutasIndividuales/crearEvento.routes.js @@ -0,0 +1,85 @@ +// RF36 - Crear Evento - [https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF36] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/eve/ctrl/crearEvento.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/eventos/crear: + * post: + * summary: Crea un nuevo evento. + * description: Este endpoint permite crear un nuevo evento en el sistema. + * tags: [Eventos] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idCliente: + * type: string + * description: ID del cliente asociado al evento. + * example: 101 + * nombre: + * type: string + * description: Nombre del evento. + * example: Evento de prueba + * descripcion: + * type: string + * description: Descripción del evento. + * example: Este es un evento de prueba. + * puntos: + * type: number + * format: double + * description: Puntos otorgados por el evento. + * example: 10.5 + * multiplicador: + * type: number + * format: double + * description: Multiplicador de puntos del evento. + * example: 1.5 + * periodoRenovacion: + * type: string + * description: Periodo de renovación del evento. + * example: mensual + * renovacion: + * type: boolean + * description: Indica si el evento se renueva automáticamente. + * example: true + * responses: + * 200: + * description: Evento creado exitosamente. + * 400: + * description: Solicitud incorrecta. + * 401: + * description: No autorizado. + * 403: + * description: Prohibido. + * 404: + * description: No encontrado. + * 500: + * description: Error interno del servidor. + */ +ruteador.post( + RUTAS.EVENTOS.CREAR, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CREAR_EVENTO), + controlador.crearEvento +); + +module.exports = ruteador; diff --git a/Eventos/Rutas/RutasIndividuales/eliminarEvento.routes.js b/Eventos/Rutas/RutasIndividuales/eliminarEvento.routes.js new file mode 100644 index 00000000..b536f7e4 --- /dev/null +++ b/Eventos/Rutas/RutasIndividuales/eliminarEvento.routes.js @@ -0,0 +1,64 @@ +//RF40 Eliminar Evento - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF40] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/eve/ctrl/eliminarEvento.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/eventos/eliminar: + * delete: + * summary: Eliminar un evento + * tags: [Eventos] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idEvento: + * type: integer + * description: ID del evento a eliminar + * responses: + * 200: + * description: Evento eliminado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Evento eliminado exitosamente + * 400: + * description: Parámetros inválidos + * 401: + * description: No autorizado + * 403: + * description: Acceso denegado + * 404: + * description: Evento no encontrado + * 500: + * description: Error al eliminar el evento + */ +ruteador.post( + RUTAS.EVENTOS.ELIMINAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_EVENTO), + controlador.eliminarEvento +); + +module.exports = ruteador; diff --git a/Eventos/Rutas/indexEventos.routes.js b/Eventos/Rutas/indexEventos.routes.js new file mode 100644 index 00000000..e3aef2a4 --- /dev/null +++ b/Eventos/Rutas/indexEventos.routes.js @@ -0,0 +1,20 @@ +const express = require('express'); +const ruteador = express.Router(); +const crearEvento = require('@altertex/eve/rutasInd/crearEvento.routes'); +const rutasConsultarListaEventos = require('@altertex/eve/rutasInd/consultarListaEventos.routes'); +const rutasConsultarEvento = require('@altertex/eve/rutasInd/consultarEvento.routes'); +const rutasEliminarEvento = require('@altertex/eve/rutasInd/eliminarEvento.routes'); +/** + * @module Eventos/Rutas + * @description Gestor de rutas para el módulo de eventos + */ + +const RUTAS = require('@altertex/util/const/rutas'); + +// Configuración de las rutas específicas para eventos +ruteador.use(RUTAS.EVENTOS.BASE, crearEvento); +ruteador.use(RUTAS.EVENTOS.BASE, rutasConsultarListaEventos); +ruteador.use(RUTAS.EVENTOS.BASE, rutasEliminarEvento); +ruteador.use(RUTAS.EVENTOS.BASE, rutasConsultarEvento); + +module.exports = ruteador; diff --git a/Pagos/Controladores/actualizarTipoPago.controller.js b/Pagos/Controladores/actualizarTipoPago.controller.js new file mode 100644 index 00000000..411c1718 --- /dev/null +++ b/Pagos/Controladores/actualizarTipoPago.controller.js @@ -0,0 +1,39 @@ +const MENSAJES = require('@altertex/util/const/mensajesPagos'); +const repositorio = require('@altertex/pago/repos/repositorioActualizarTipoPago'); + +//RF[54] Actualizar lista de pago - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF54] + +/** + * Controlador para actualizar los tipos de pago disponibles. + * + * Este endpoint recibe un objeto con los cambios a realizar sobre los métodos de pago + * y utiliza el repositorio correspondiente para aplicar las actualizaciones en la base de datos. + * + * @function actualizarTipoPago + * @async + * @param {Express.Request} req - Objeto de solicitud HTTP de Express. + * @param {object} req.body - Cuerpo de la solicitud. + * @param {Array} req.body.cambios - Lista de métodos de pago a actualizar, incluyendo su nuevo estado. + * @param {Express.Response} res - Objeto de respuesta HTTP de Express. + * @returns {Promise} Retorna una respuesta JSON indicando éxito o error en la operación. + */ +exports.actualizarTipoPago = async (req, res) => { + const datos = req.body.cambios; + + if (!datos) { + return res + .status(MENSAJES.ERROR_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.ERROR_ACTUALIZAR.mensaje }); + } + + try { + await repositorio.actualizarTipoPago(datos); + return res + .status(MENSAJES.EXITO_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.EXITO_ACTUALIZAR.mensaje, datos }); + } catch { + return res + .status(MENSAJES.ERROR_ACTUALIZAR.codigo) + .json({ mensaje: MENSAJES.ERROR_ACTUALIZAR.mensaje }); + } +}; diff --git a/Pagos/Controladores/consultarTipoPago.controller.js b/Pagos/Controladores/consultarTipoPago.controller.js new file mode 100644 index 00000000..d5a0d1a6 --- /dev/null +++ b/Pagos/Controladores/consultarTipoPago.controller.js @@ -0,0 +1,36 @@ +const MENSAJES = require('@altertex/util/const/mensajesPagos'); +const repositorio = require('@altertex/pago/repos/repositorioConsultarTipoPago'); + +//RF[52] Consulta Lista de Pago - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF52] + +/** + * Controlador para consultar los tipos de pago disponibles para el cliente autenticado. + * + * Este endpoint obtiene el ID del cliente desde el usuario autenticado (`req.user.clienteSeleccionado`) + * y devuelve la lista de métodos de pago habilitados para dicho cliente. + * + * @function consultarTipoPago + * @async + * @param {Express.Request} req - Objeto de solicitud HTTP. Se espera que contenga `user.clienteSeleccionado`. + * @param {Express.Response} res - Objeto de respuesta HTTP. + * @returns {Promise} Retorna una respuesta JSON con la lista de métodos de pago o un mensaje de error. + */ +exports.consultarTipoPago = async (req, res) => { + const cliente = req.user.clienteSeleccionado; + if (!cliente) { + return res + .status(MENSAJES.ERROR_CLIENTE_SELECCIONADO.codigo) + .json({ mensaje: MENSAJES.ERROR_CLIENTE_SELECCIONADO.mensaje }); + } + try { + const listaTipoPagos = await repositorio.consutlarTipoPago(cliente); + + return res + .status(MENSAJES.CONSULTA_EXITOSA.codigo) + .json({ mensaje: MENSAJES.CONSULTA_EXITOSA.mensaje, listaTipoPagos }); + } catch { + return res + .status(MENSAJES.ERROR_CONSULTA.codigo) + .json({ mensaje: MENSAJES.ERROR_CONSULTA.mensaje }); + } +}; diff --git a/Pagos/Datos/Repositorios/repositorioActualizarTipoPago.js b/Pagos/Datos/Repositorios/repositorioActualizarTipoPago.js new file mode 100644 index 00000000..5417a136 --- /dev/null +++ b/Pagos/Datos/Repositorios/repositorioActualizarTipoPago.js @@ -0,0 +1,33 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES = require('@altertex/util/const/mensajesPagos'); +const CONSULTAS = require('@altertex/util/const/consultasPagos'); + +//RF[54] Actualizar Lista de Pago - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF54] + +/** + * Repositorio para actualizar el estado de los tipos de pago en la base de datos. + * + * Recorre un arreglo de objetos que contienen la información de cada método de pago + * (ID, nombre del método y si está habilitado o no), y ejecuta la consulta SQL correspondiente + * para actualizar su estado. + * + * @function actualizarTipoPago + * @async + * @param {Array<{ id: number, metodo: string, habilitado: boolean }>} datos - Lista de métodos de pago a actualizar. + * @throws {Error} Si el arreglo está vacío o si ocurre un error en la base de datos. + * @returns {Promise} Promesa que se resuelve cuando todas las actualizaciones han sido ejecutadas. + */ +exports.actualizarTipoPago = async (datos) => { + if (!Array.isArray(datos) || datos.length === 0) { + throw new Error('No hay datos para actualizar.'); + } + try { + await Promise.all( + datos.map(({ id, habilitado }) => { + return correrQuery(CONSULTAS.ACTUALIZAR, [habilitado, id]); + }) + ); + } catch { + throw new Error(MENSAJES.ERROR_ACTUALIZAR.mensaje); + } +}; diff --git a/Pagos/Datos/Repositorios/repositorioConsultarTipoPago.js b/Pagos/Datos/Repositorios/repositorioConsultarTipoPago.js new file mode 100644 index 00000000..cd9e9839 --- /dev/null +++ b/Pagos/Datos/Repositorios/repositorioConsultarTipoPago.js @@ -0,0 +1,28 @@ +const MENSAJES = require('@altertex/util/const/mensajesPagos'); +const CONSULTAS = require('@altertex/util/const/consultasPagos'); +const correrQuery = require('@altertex/util/ser/correrQuery'); + +//RF[52] Consulta Lista de Pago - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF52] + +/** + * Repositorio para consultar la lista de métodos de pago habilitados para un cliente. + * + * Ejecuta una consulta SQL que devuelve los tipos de pago disponibles para el cliente especificado. + * + * @function consutlarTipoPago + * @async + * @param {number|string} cliente - Identificador del cliente cuyos métodos de pago se desean consultar. + * @throws {Error} Si no se proporciona un cliente o si ocurre un error durante la consulta. + * @returns {Promise>} Promesa que resuelve con un arreglo de objetos que representan los métodos de pago. + */ +exports.consutlarTipoPago = async (cliente) => { + if (!cliente) { + throw new Error(MENSAJES.ERROR_CONSULTA.mensaje); + } + try { + const listaTipoPagos = await correrQuery(CONSULTAS.CONSULTAR_LISTA, [cliente]); + return listaTipoPagos; + } catch { + throw new Error(MENSAJES.ERROR_CONSULTA.mensaje); + } +}; diff --git a/Pagos/Rutas/RutasIndividuales/actualizarTipoPago.routes.js b/Pagos/Rutas/RutasIndividuales/actualizarTipoPago.routes.js new file mode 100644 index 00000000..b3db590d --- /dev/null +++ b/Pagos/Rutas/RutasIndividuales/actualizarTipoPago.routes.js @@ -0,0 +1,104 @@ +const express = require('express'); +const ruteador = express.Router(); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const controlador = require('@altertex/pago/ctrl/actualizarTipoPago.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +//RF[54] Actualizar Lista de Pago - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF54] + +/** + * @swagger + * /api/pagos/actualizar: + * put: + * summary: Actualiza el estado de métodos de pago habilitados. + * tags: + * - Pagos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * cambios: + * type: array + * description: Lista de tipos de pago a actualizar. + * items: + * type: object + * required: + * - id + * - metodo + * - habilitado + * properties: + * id: + * type: integer + * example: 1 + * metodo: + * type: string + * example: "tarjeta_credito" + * habilitado: + * type: boolean + * example: true + * responses: + * 200: + * description: Tipos de pago actualizados correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Método(s) de pago actualizados correctamente." + * datos: + * type: array + * items: + * type: object + * properties: + * id: + * type: integer + * metodo: + * type: string + * habilitado: + * type: boolean + * 400: + * description: Error en los datos enviados o en la actualización. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al actualizar los métodos de pago." + * x-codeSamples: + * - lang: JavaScript + * label: cURL + * source: | + * curl -X PUT "https://tu-api.com/api/pagos/tipo" \ + * -H "x-api-key: TU_API_KEY" \ + * -H "Authorization: Bearer TU_TOKEN" \ + * -H "Content-Type: application/json" \ + * -d '{"cambios":[{"id":1,"metodo":"tarjeta_credito","habilitado":true}]}' + */ +ruteador.put( + RUTAS.PAGOS.ACTUALIZAR, + revisarApiKey(), + validarYSanitizar, + autorizarToken, + limitePeticionesDiarias, + revisarPermisos(PERMISOS.ACTUALIZAR_TIPO_PAGO), + controlador.actualizarTipoPago +); + +module.exports = ruteador; diff --git a/Pagos/Rutas/RutasIndividuales/consultarTipoPago.routes.js b/Pagos/Rutas/RutasIndividuales/consultarTipoPago.routes.js new file mode 100644 index 00000000..409757d8 --- /dev/null +++ b/Pagos/Rutas/RutasIndividuales/consultarTipoPago.routes.js @@ -0,0 +1,80 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pago/ctrl/consultarTipoPago.controller'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +//RF[52] Consulta Lista de Pago - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF52] + +/** + * @swagger + * /api/pagos/consultar-lista: + * get: + * summary: Consulta los tipos de pago disponibles para el cliente autenticado. + * tags: + * - Pagos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Consulta exitosa de métodos de pago. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Consulta exitosa." + * listaTipoPagos: + * type: array + * items: + * type: object + * properties: + * id: + * type: integer + * example: 1 + * metodo: + * type: string + * example: "tarjeta_credito" + * habilitado: + * type: boolean + * example: true + * 400: + * description: El cliente no ha sido seleccionado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No se ha seleccionado un cliente válido." + * 500: + * description: Error interno al consultar los métodos de pago. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al consultar los métodos de pago." + */ +ruteador.get( + RUTAS.PAGOS.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + revisarPermisos(PERMISOS.CONSULTAR_TIPOS_PAGO), + controlador.consultarTipoPago +); + +module.exports = ruteador; diff --git a/Pagos/Rutas/indexPagos.routes.js b/Pagos/Rutas/indexPagos.routes.js new file mode 100644 index 00000000..7282b5a4 --- /dev/null +++ b/Pagos/Rutas/indexPagos.routes.js @@ -0,0 +1,11 @@ +const express = require('express'); +const ruteador = express.Router(); +const RUTAS = require('@altertex/util/const/rutas'); + +const rutaConsultarPago = require('@altertex/pago/rutas/rutasInd/consultarTipoPago.routes'); +const rutasActualizarPago = require('@altertex/pago/rutas/rutasInd/actualizarTipoPago.routes'); + +ruteador.use(RUTAS.PAGOS.BASE, rutaConsultarPago); +ruteador.use(RUTAS.PAGOS.BASE, rutasActualizarPago); + +module.exports = ruteador; diff --git a/Pedidos/Controladores/actualizarPedido.controller.js b/Pedidos/Controladores/actualizarPedido.controller.js new file mode 100644 index 00000000..299ddbd4 --- /dev/null +++ b/Pedidos/Controladores/actualizarPedido.controller.js @@ -0,0 +1,47 @@ +const MENSAJES = require('@altertex/util/const/mensajesPedidos'); +const repositorio = require('@altertex/pedidos/repos/repositorioActualizarPedido'); + +/** + * RF[62] - Actualizar Pedido [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF62] + * Controlador para actualizar la información de uno o varios pedidos. + * + * Respuestas posibles: + * - 400 si faltan datos en el cuerpo de la solicitud. + * - 200 si el pedido se actualiza correctamente. + * + * @async + * @function actualizarGrupoEmpleados + * @param {Express.Request} req - Objeto de solicitud HTTP de Express. + * @param {Express.Response} res - Objeto de respuesta HTTP de Express. + * @returns {Promise} La respuesta HTTP con el estado y mensaje correspondiente. + */ +exports.actualizarPedido = async (req, res) => { + let datos; + if (req.body.idPedido) { + datos = [req.body]; + } else if (req.body.cambios) { + datos = Array.isArray(req.body.cambios) ? req.body.cambios : [req.body.cambios]; + } else { + return res.status(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.codigo).json({ + mensaje: MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje, + }); + } + + if (!datos || datos.length === 0) { + return res.status(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.codigo).json({ + mensaje: MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje, + }); + } + + try { + await repositorio.actualizarPedido(datos); + return res.status(MENSAJES.PEDIDO_ACTUALIZADO.codigo).json({ + mensaje: MENSAJES.PEDIDO_ACTUALIZADO.mensaje, + datos, + }); + } catch { + return res.status(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.codigo).json({ + mensaje: MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje, + }); + } +}; diff --git a/Pedidos/Controladores/eliminarPedidos.controller.js b/Pedidos/Controladores/eliminarPedidos.controller.js new file mode 100644 index 00000000..bad39007 --- /dev/null +++ b/Pedidos/Controladores/eliminarPedidos.controller.js @@ -0,0 +1,52 @@ +const repositorio = require('@altertex/pedidos/repos/repositorioEliminarPedidos'); +const MENSAJES_PEDIDOS = require('@altertex/util/const/mensajesPedidos'); + +/** + * Controlador para eliminar pedidos. + * RF[63] - Elimina pedido - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF63 + * + * @async + * @function eliminarPedido + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {number[]} req.body.idsPedido - Array de IDs numéricos de los pedidos a eliminar. + * @param {object} res - Objeto de respuesta de Express. + * @returns {Promise} Respuesta HTTP con estado: + * - 200 si los pedidos fueron eliminados correctamente. + * - 404 si no se encontraron los pedidos. + * - 500 si ocurre un error en el servidor. + * @throws {Error} Si ocurre un error durante la eliminación. + */ +exports.eliminarPedido = async (req, res) => { + try { + const idsPedidos = req.body.idsPedido; + + // Validar que se envíen IDs válidos + if (!Array.isArray(idsPedidos) || idsPedidos.length === 0) { + return res.status(MENSAJES_PEDIDOS.PEDIDO_NO_ENCONTRADO.codigo).json({ + mensaje: MENSAJES_PEDIDOS.PEDIDO_NO_ENCONTRADO.mensaje, + }); + } + + // Usar transacciones para eliminar los pedidos + await Promise.all( + idsPedidos.map(async (idPedido) => { + const resultadoPedido = await repositorio.eliminarPedido(idPedido); + + if (resultadoPedido.resultadoPedido.affectedRows === 0) { + throw new Error(`Pedido con ID ${idPedido} no encontrado`); + } + }) + ); + + // Respuesta exitosa + return res.status(MENSAJES_PEDIDOS.PEDIDO_ELIMINADO.codigo).json({ + mensaje: MENSAJES_PEDIDOS.PEDIDO_ELIMINADO.mensaje, + }); + } catch { + // Respuesta de error + return res.status(MENSAJES_PEDIDOS.ERROR_ELIMINAR_PEDIDO.codigo).json({ + mensaje: MENSAJES_PEDIDOS.ERROR_ELIMINAR_PEDIDO.mensaje, + }); + } +}; diff --git a/Pedidos/Controladores/obtenerPedidos.controller.js b/Pedidos/Controladores/obtenerPedidos.controller.js new file mode 100644 index 00000000..41016834 --- /dev/null +++ b/Pedidos/Controladores/obtenerPedidos.controller.js @@ -0,0 +1,46 @@ +const MENSAJES = require('@altertex/util/const/mensajesPedidos'); +const repositorio = require('@altertex/pedidos/repos/repositorioObtenerPedidos'); + +/** + * RF60 - Consulta Lista de Pedidos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF60 + * Controlador para obtener la lista de pedidos de un cliente autenticado. + * + * @async + * @function obtenerLista + * @param {Express.Request} req - Objeto de solicitud de Express. Debe contener `user.clienteSeleccionado`. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @returns {Promise} Respuesta HTTP con la lista de pedidos o un mensaje de error. + * + * @example + * // Éxito + * res.status(200).json({ + * mensaje: 'Consulta exitosa', + * pedidos: [...] + * }); + * + * @example + * // Error: cliente no seleccionado + * res.status(400).json({ + * error: 'No se pudo consultar los pedidos.' + * }); + */ +exports.obtenerLista = async (req, res) => { + const idCliente = req.user?.clienteSeleccionado; + + if (!idCliente) { + return res + .status(MENSAJES.ERROR_CONSULTAR_PEDIDOS.codigo) + .json({ error: MENSAJES.ERROR_CONSULTAR_PEDIDOS.mensaje }); + } + + try { + const resultado = await repositorio.obteneLista(idCliente); + return res + .status(MENSAJES.CONSULTA_EXITOSA.codigo) + .json({ mensaje: MENSAJES.CONSULTA_EXITOSA.mensaje, pedidos: resultado }); + } catch { + return res + .status(MENSAJES.ERROR_CONSULTAR_PEDIDOS.codigo) + .json({ error: MENSAJES.ERROR_CONSULTAR_PEDIDOS.mensaje }); + } +}; diff --git a/Pedidos/Datos/Repositorios/repositorioActualizarPedido.js b/Pedidos/Datos/Repositorios/repositorioActualizarPedido.js new file mode 100644 index 00000000..002d898a --- /dev/null +++ b/Pedidos/Datos/Repositorios/repositorioActualizarPedido.js @@ -0,0 +1,50 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES = require('@altertex/util/const/mensajesPedidos'); +const CONSULTAS = require('@altertex/util/const/consultasPedidos'); + +//RF[62] - Actualizar Pedido - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF62] + +/** + * Actualiza uno o varios pedidos en la base de datos con nueva información + * de estado, precio total, ID de envío y ID de pago. + * + * Esta función realiza: + * - La validación de que se proporcionen datos para actualizar. + * - La actualización simultánea de múltiples pedidos usando Promise.all. + * - El manejo de errores durante el proceso de actualización. + * + * La función procesa cada pedido de forma paralela, actualizando sus campos + * mediante una consulta SQL preparada con los parámetros proporcionados. + * + * @async + * @function actualizarPedido + * @param {object[]} datos - Array de objetos con los datos de los pedidos a actualizar. + * @param {number} datos[].idPedido - ID único del pedido a actualizar. + * @param {string|number} datos[].estado - Nuevo estado del pedido. + * @param {number} datos[].precioTotal - Nuevo precio total del pedido. + * @param {number} datos[].idEnvio - ID del envío asociado al pedido. + * @param {number} datos[].idPago - ID del pago asociado al pedido. + * @throws {Error} 'Sin datos para actualizar.' - Si el array está vacío o no es válido. + * @throws {Error} Mensaje de error específico desde MENSAJES.ERROR_ACTUALIZAR_PEDIDO - Si ocurre algún error durante la actualización. + */ +exports.actualizarPedido = async (datos) => { + if (!Array.isArray(datos) || datos.length === 0) { + throw new Error('Sin datos para actualizar.'); + } + + try { + await Promise.all( + datos.map(({ idPedido, estado, precioTotal, idEnvio, idPago }) => { + return correrQuery(CONSULTAS.ACTUALIZAR_PEDIDO, [ + estado, + precioTotal, + idPago, + idEnvio, + idPedido, + ]); + }) + ); + } catch { + throw new Error(MENSAJES.ERROR_ACTUALIZAR_PEDIDO.mensaje); + } +}; diff --git a/Pedidos/Datos/Repositorios/repositorioEliminarPedidos.js b/Pedidos/Datos/Repositorios/repositorioEliminarPedidos.js new file mode 100644 index 00000000..f6c14ae7 --- /dev/null +++ b/Pedidos/Datos/Repositorios/repositorioEliminarPedidos.js @@ -0,0 +1,56 @@ +const db = require('@altertex/util/bd/db'); +const CONSULTAS_PEDIDOS = require('@altertex/util/const/consultasPedidos'); + +/** + * Elimina las opciones asociadas a un pedido. + * //RF[63] Elimina pedido - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF63] + * @async + * @function eliminarPedido + * @param {number} idPedido - ID del pedido a eliminar. + * @returns {Promise} Objeto de resultado de la operación MySQL (por ejemplo, `affectedRows`). + * @throws {Error} Si ocurre un error durante la ejecución de la transacción. + */ +exports.eliminarPedido = async (idPedido) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + // Eliminar las opciones asociadas al pedido + const [resultadoOpciones] = await conexion.query( + CONSULTAS_PEDIDOS.ELIMINAR_PEDIDO_OPCION, + [idPedido] + ); + + // Eliminar la relación entre empleados y el pedido + const [resultadoEmpleados] = await conexion.query( + CONSULTAS_PEDIDOS.ELIMINAR_EMPLEADO_PEDIDO, + [idPedido] + ); + + // Eliminar el pedido + const [resultadoPedido] = await conexion.query( + CONSULTAS_PEDIDOS.ELIMINAR_PEDIDO, + [idPedido] + ); + + if (resultadoPedido.affectedRows === 0) { + throw new Error(`Pedido con ID ${idPedido} no encontrado`); + } + + // Confirmar la transacción + await conexion.commit(); + + return { + mensaje: 'Pedido eliminado correctamente', + resultadoOpciones, + resultadoEmpleados, + resultadoPedido, + }; + } catch { + await conexion.rollback(); + throw new Error('Error eliminando pedido'); + } finally { + conexion.release(); + } +}; \ No newline at end of file diff --git a/Pedidos/Datos/Repositorios/repositorioObtenerPedidos.js b/Pedidos/Datos/Repositorios/repositorioObtenerPedidos.js new file mode 100644 index 00000000..d36d5554 --- /dev/null +++ b/Pedidos/Datos/Repositorios/repositorioObtenerPedidos.js @@ -0,0 +1,25 @@ +const CONSULTAS = require('@altertex/util/const/consultasPedidos'); +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES = require('@altertex/util/const/mensajesPedidos'); + +/** + * RF60 - Consulta Lista de Pedidos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF60 + * Consulta la lista de pedidos asociados a un cliente. + * + * @async + * @function obteneLista + * @param {number} idCliente - El ID del cliente para el cual se desean obtener los pedidos. + * @returns {Promise} Una promesa que se resuelve con un arreglo de objetos que representan los pedidos. + * @throws {Error} Lanza un error si el ID del cliente es inválido o si ocurre un error al ejecutar la consulta. + */ +exports.obteneLista = async (idCliente) => { + try { + if (!idCliente || typeof idCliente !== 'number') { + throw new Error(MENSAJES.ERROR_CONSULTAR_PEDIDOS.mensaje); + } + + return correrQuery(CONSULTAS.OBTENER_LISTA, [idCliente]); + } catch { + throw new Error(MENSAJES.ERROR_CONSULTAR_PEDIDOS.mensaje); + } +}; diff --git a/Pedidos/Rutas/RutasIndividuales/actualizarPedidos.routes.js b/Pedidos/Rutas/RutasIndividuales/actualizarPedidos.routes.js new file mode 100644 index 00000000..90ba4c2a --- /dev/null +++ b/Pedidos/Rutas/RutasIndividuales/actualizarPedidos.routes.js @@ -0,0 +1,24 @@ +const express = require('express'); +const ruteador = express.Router(); + +const controlador = require('@altertex/pedidos/ctrl/actualizarPedido.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticiones = require('@altertex/util/inter/limitePeticiones'); +const RUTAS = require('@altertex/util/const/rutas'); +const PERMISOS = require('@altertex/util/const/permisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); + +// RF[62] - Actualizar Pedido +ruteador.put( + RUTAS.PEDIDOS.ACTUALIZAR_PEDIDO, + revisarApiKey(), + autorizarToken, + limitePeticiones, + verificarPermisos(PERMISOS.ACTUALIZAR_PEDIDO), + validarYSanitizar, + controlador.actualizarPedido +); + +module.exports = ruteador; diff --git a/Pedidos/Rutas/RutasIndividuales/eliminarPedidos.routes.js b/Pedidos/Rutas/RutasIndividuales/eliminarPedidos.routes.js new file mode 100644 index 00000000..6805499c --- /dev/null +++ b/Pedidos/Rutas/RutasIndividuales/eliminarPedidos.routes.js @@ -0,0 +1,79 @@ +//RF[63] Elimina pedido - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF63] + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pedidos/ctrl/eliminarPedidos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/pedidos/eliminar: + * post: + * summary: Eliminar pedidos. + * description: Elimina uno o varios pedidos de la base de datos. Requiere autenticación y permisos específicos. + * tags: + * - Pedidos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idsPedido: + * type: array + * items: + * type: integer + * example: [101, 102, 103] + * responses: + * 200: + * description: Pedidos eliminados exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Pedidos eliminados correctamente. + * 404: + * description: Pedidos no encontrados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No se encontraron los pedidos especificados. + * 500: + * description: Error interno al eliminar los pedidos. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al eliminar los pedidos. + */ + +ruteador.post( + RUTAS.PEDIDOS.ELIMINAR_PEDIDO, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_PEDIDO), + controlador.eliminarPedido +); + +module.exports = ruteador; diff --git a/Pedidos/Rutas/RutasIndividuales/obtenerPedidos.routes.js b/Pedidos/Rutas/RutasIndividuales/obtenerPedidos.routes.js new file mode 100644 index 00000000..4d0a42b8 --- /dev/null +++ b/Pedidos/Rutas/RutasIndividuales/obtenerPedidos.routes.js @@ -0,0 +1,78 @@ +const express = require('express'); +const ruteador = express.Router(); +const RUTAS = require('@altertex/util/const/rutas'); +const PERMISOS = require('@altertex/util/const/permisos'); +const controlador = require('@altertex/pedidos/ctrl/obtenerPedidos.controller'); + +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +/** + * RF60 - Consulta Lista de Pedidos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF60 + * @swagger + * /api/pedidos/consultar-lista: + * get: + * summary: Consulta la lista de pedidos del cliente autenticado + * tags: + * - Pedidos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Lista de pedidos obtenida exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Lista de pedidos obtenida exitosamente. + * pedidos: + * type: array + * items: + * type: object + * properties: + * idPedido: + * type: integer + * example: 1 + * nombreEmpleado: + * type: string + * example: Sofía Navarro + * fechaOrden: + * type: string + * format: date + * example: 2025-03-20 + * estatusPedido: + * type: string + * example: En Proceso + * precioTotal: + * type: string + * example: "450.00" + * estatusPago: + * type: string + * example: Pendiente + * estatusEnvio: + * type: string + * example: En Proceso + * 204: + * description: No se encontraron pedidos para el cliente. + * 403: + * description: No tiene permiso para consultar pedidos. + * 500: + * description: Error al consultar la lista de pedidos. + */ +ruteador.get( + RUTAS.PEDIDOS.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_PEDIDOS), + controlador.obtenerLista +); + +module.exports = ruteador; diff --git a/Pedidos/Rutas/indexPedidos.routes.js b/Pedidos/Rutas/indexPedidos.routes.js new file mode 100644 index 00000000..16647e25 --- /dev/null +++ b/Pedidos/Rutas/indexPedidos.routes.js @@ -0,0 +1,12 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutasObtenerPedidos = require('@altertex/pedidos/rutasInd/obtenerPedidos.routes'); +const rutasEliminarPedido = require('@altertex/pedidos/rutasInd/eliminarPedidos.routes'); +const rutasActualizarPedidos = require('@altertex/pedidos/rutasInd/actualizarPedidos.routes'); +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.use(RUTAS.PEDIDOS.BASE, rutasObtenerPedidos); +ruteador.use(RUTAS.PEDIDOS.BASE, rutasEliminarPedido); +ruteador.use(RUTAS.PEDIDOS.BASE, rutasActualizarPedidos); + +module.exports = ruteador; diff --git a/Productos/Controladores/consultarProductos.controller.js b/Productos/Controladores/consultarProductos.controller.js new file mode 100644 index 00000000..7a3feb18 --- /dev/null +++ b/Productos/Controladores/consultarProductos.controller.js @@ -0,0 +1,53 @@ +// RF[27] Consulta Lista de Productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF27] + +const repositorio = require('@altertex/pro/repos/repositorioConsultarProductos'); +const obtenerImagenFolder = require('@altertex/util/ser/obtenerImagenFolder'); +const MENSAJES_PRODUCTOS = require('@altertex/util/const/mensajesProductos'); + +/** + * Consulta los productos disponibles para un cliente seleccionado y asigna imágenes por defecto si no se encuentran imágenes. + * + * Obtiene la lista de productos mediante el repositorio `obtenerProductos`. Si no se encuentran productos o si hay un + * error al obtener las imágenes, se asignan imágenes por defecto a los productos. Finalmente, se devuelve la lista de + * productos con las imágenes asignadas. + * + * @param {Express.Request} req - La solicitud HTTP que contiene el ID del cliente en el usuario autenticado (`req.user.clienteSeleccionado`). + * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente. + * @returns {Promise} Responde con la lista de productos actualizada o un mensaje de error. + */ +exports.consultarProductos = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + + try { + const productos = await repositorio.obtenerProductos(idCliente); + req.productos = productos; + + if (!productos || productos.length === 0) { + return res.status(200).json({ + mensaje: MENSAJES_PRODUCTOS.SIN_RESULTADOS.mensaje, + }); + } + + const folder = 'productos/'; + + let productosActualizados; + try { + productosActualizados = await obtenerImagenFolder(req, folder); + } catch { + productosActualizados = productos.map((producto) => ({ + ...producto, + urlImagen: '/placeholder.png', + })); + } + + return res.status(MENSAJES_PRODUCTOS.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.CONSULTA_EXITOSA.mensaje, + listaProductos: productosActualizados, + }); + } catch (error) { + return res.status(MENSAJES_PRODUCTOS.ERROR_CONSULTAR_PRODUCTOS.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.ERROR_CONSULTAR_PRODUCTOS.mensaje, + error: error.message, + }); + } +}; diff --git a/Productos/Controladores/crearProducto.controller.js b/Productos/Controladores/crearProducto.controller.js new file mode 100644 index 00000000..8852ef24 --- /dev/null +++ b/Productos/Controladores/crearProducto.controller.js @@ -0,0 +1,179 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const multer = require('multer'); +const enviarS3 = require('@altertex/util/ser/enviarS3'); +const MENSAJES_PRODUCTOS = require('@altertex/util/const/mensajesProductos'); +const validarProducto = require('@altertex/util/vali/validarProducto'); +const validarVariante = require('@altertex/util/vali/validarVariante'); +const validarOpciones = require('@altertex/util/vali/validarOpciones'); +const repositorioCrearProducto = require('@altertex/pro/repos/repositorioCrearProducto'); +const repositorioProductoImagen = require('@altertex/pro/repos/repositorioProductoImagen'); +const repositorioCrearVariante = require('@altertex/pro/repos/repositorioCrearVariante'); +const repositorioVarianteImagen = require('@altertex/pro/repos/repositorioVarianteImagen'); +const repositorioCrearOpcion = require('@altertex/pro/repos/repositorioCrearOpcion'); +// Updated to use the connection pool correctly +const db = require('@altertex/util/bd/db'); + +const upload = multer({ storage: multer.memoryStorage() }); + +// Controller code remains the same but with updated transaction handling +exports.crearProducto = [ + upload.fields([ + { name: 'imagenProducto', maxCount: 1 }, + { name: 'imagenesVariante', maxCount: 100 }, + ]), + + async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + const producto = JSON.parse(req.body.producto); + const variantes = JSON.parse(req.body.variantes); + const mapaImagenes = JSON.parse(req.body.mapaImagenes); + const imagenProducto = req.files.imagenProducto ? req.files.imagenProducto[0] : null; + const imagenesVariante = req.files.imagenesVariante || []; + let conexion = null; + + // prettier-ignore + if ( + !idCliente + || !mapaImagenes + || !producto + || !Array.isArray(variantes) + || variantes.length === 0 + || !imagenProducto + || !imagenesVariante + ) { + return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.mensaje, + }); + } + + const errorProducto = validarProducto(producto); + if (errorProducto) { + return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: errorProducto.error, + }); + } + + if (imagenesVariante.length !== mapaImagenes.length) { + return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: 'La cantidad de imágenes no coincide con el mapa de imágenes', + }); + } + + try { + // Get connection from the pool + conexion = await db.getConnection(); + await conexion.beginTransaction(); + + const idProducto = await repositorioCrearProducto.crearProducto(idCliente, producto); + if (!idProducto) { + throw new Error('Error al crear producto'); + } + + const varianteIdMap = {}; + const variantesPromises = variantes.map(async (variante) => { + const errorVariante = validarVariante({ + nombreVariante: variante.nombreVariante, + descripcion: variante.descripcion, + }); + if (errorVariante) { + throw new Error(errorVariante.error); + } + + // Update repositorioCrearVariante if not already updated + const idVariante = await repositorioCrearVariante.crearVariante(idProducto, variante); + if (!idVariante) { + throw new Error('Error al crear variante'); + } + + varianteIdMap[variante.identificador] = { + id: idVariante, + nombre: variante.nombreVariante, + }; + + const errorOpciones = validarOpciones(variante.opciones); + if (errorOpciones) { + throw new Error(errorOpciones.error); + } + + await repositorioCrearOpcion.crearOpcion(idVariante, variante.opciones); + }); + + await Promise.all(variantesPromises); + + // Upload image processing remains unchanged + const urlImagenProductoPromise = imagenProducto + ? enviarS3({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: `productos/${imagenProducto.originalname}`, + Body: imagenProducto.buffer, + ContentType: imagenProducto.mimetype, + }) + : Promise.resolve(null); + + // prettier-ignore + const urlImagenVariantePromises = imagenesVariante.map((imagenVariante) => + enviarS3({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: `productos/${imagenVariante.originalname}`, + Body: imagenVariante.buffer, + ContentType: imagenVariante.mimetype, + })); + + const [urlImagenProducto, ...urlImagenVariantes] = await Promise.all([ + urlImagenProductoPromise, + ...urlImagenVariantePromises, + ]); + + if ((imagenProducto && !urlImagenProducto) || urlImagenVariantes.includes(null)) { + throw new Error('Error al subir imágenes al servidor'); + } + + if (imagenProducto) { + await repositorioProductoImagen.crearImagen( + idProducto, + imagenProducto.originalname, + producto.nombreComun + ); + } + + const imagenesVariantePromises = imagenesVariante.map(async (imagen, index) => { + const { idVariante: tempIdVariante } = mapaImagenes[index]; + const varianteInfo = varianteIdMap[tempIdVariante]; + + if (!varianteInfo) { + throw new Error(`Variante con ID temporal ${tempIdVariante} no encontrada`); + } + + await repositorioVarianteImagen.crearImagen( + varianteInfo.id, + imagen.originalname, + varianteInfo.nombre + ); + }); + + await Promise.all(imagenesVariantePromises); + + await conexion.commit(); + return res.status(200).json({ mensaje: 'Producto creado correctamente' }); + } catch (error) { + if (conexion) await conexion.rollback(); + + let errorMensaje = MENSAJES_PRODUCTOS.ERROR_CREAR_PRODUCTO; + + if (error.message.includes('Error al subir imágenes al servidor')) { + errorMensaje = MENSAJES_PRODUCTOS.ERROR_ENVIAR_IMAGENES_S3; + } else if (error.message.includes('Error al crear variante')) { + errorMensaje = MENSAJES_PRODUCTOS.ERROR_CREAR_VARIANTE; + } else if (error.message.includes('Error al asociar imagen con variante')) { + errorMensaje = MENSAJES_PRODUCTOS.ERROR_CREAR_IMAGEN_VARIANTE; + } + + return res.status(errorMensaje.codigo).json({ + mensaje: errorMensaje.mensaje, + error: error.message, + }); + } finally { + if (conexion) conexion.release(); + } + }, +]; \ No newline at end of file diff --git a/Productos/Controladores/eliminarProducto.controller.js b/Productos/Controladores/eliminarProducto.controller.js new file mode 100644 index 00000000..8e33cca8 --- /dev/null +++ b/Productos/Controladores/eliminarProducto.controller.js @@ -0,0 +1,58 @@ +// Importación de la función que elimina productos en el repositorio de datos. +const { eliminarProductos } = require('@altertex/pro/repos/productosRepositorio'); + +// Importación de las constantes de mensajes utilizados para respuestas del módulo de productos. +const { + RESPUESTA_ELIMINAR_PRODUCTO_EXITOSA, + RESPUESTA_ERROR_GENERAL, +} = require('../../Utilidades/Constantes/mensajesProductos'); + +/** + * RF30 - Eliminar Producto + * Requerimiento funcional: + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30 + */ + +/** + * Controlador encargado de eliminar uno o múltiples productos. + * + * @async + * @function eliminarProductoController + * @param {object} req - Objeto de solicitud HTTP (Request). Debe contener en el body un array de IDs bajo `req.body.ids`. + * @param {object} res - Objeto de respuesta HTTP (Response). + * @returns {Promise} No retorna datos directamente; envía la respuesta HTTP al cliente. + * + * @description + * Recibe un array de IDs de productos a eliminar a través del cuerpo de la solicitud (body). + * Valida que los IDs sean un arreglo no vacío. Llama al repositorio para realizar la eliminación + * y responde al cliente con éxito o error según corresponda. + */ +const eliminarProductoController = async (req, res) => { + try { + const { ids } = req.body; + + // Validación de los IDs recibidos. + if (!Array.isArray(ids) || ids.length === 0) { + return res.status(400).json({ + codigo: 400, + mensaje: 'Debes proporcionar al menos un ID de producto para eliminar.', + }); + } + // Se realiza la eliminación de los productos. + const resultado = await eliminarProductos(ids); + + // Se responde dependiendo del éxito o fallo de la operación. + if (resultado) { + return res.status(200).json(RESPUESTA_ELIMINAR_PRODUCTO_EXITOSA); + } else { + return res.status(400).json(RESPUESTA_ERROR_GENERAL); + } + } catch { + return res.status(500).json(RESPUESTA_ERROR_GENERAL); + } +}; + +// Exporta el controlador para su uso en las rutas correspondientes. +module.exports = { + eliminarProductoController, +}; diff --git a/Productos/Controladores/exportarProductos.controller.js b/Productos/Controladores/exportarProductos.controller.js new file mode 100644 index 00000000..ebdd243c --- /dev/null +++ b/Productos/Controladores/exportarProductos.controller.js @@ -0,0 +1,49 @@ +const repositorio = require('@altertex/pro/repos/repositorioExportarProducto'); +const MENSAJES_PRODUCTOS = require('@altertex/util/const/mensajesProductos'); + +/** + * Controlador para exportar productos seleccionados de un cliente y retornar Excel. + * + * @async + * @function exportarProductos + * @param {Request} req + * @param {Response} res + * @returns {Response} Archivo Excel con los productos exportados + * @see [RF58 - Exportar Productos](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF58) + */ +exports.exportarProductos = async (req, res) => { + try { + const idCliente = parseInt(req.user.clienteSeleccionado); + const idsProducto = req.body.idsProducto; + + if (!Array.isArray(idsProducto) || idsProducto.length === 0) { + return res.status(MENSAJES_PRODUCTOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: 'Debes seleccionar al menos un producto para exportar.', + }); + } + + const idsSeleccionados = idsProducto.map((id) => parseInt(id)); + const productos = await repositorio.obtenerProductosExportacion(idCliente, idsSeleccionados); + + if (!productos || productos.length === 0) { + return res.status(MENSAJES_PRODUCTOS.PRODUCTOS_NO_ENCONTRADOS.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.PRODUCTOS_NO_ENCONTRADOS.mensaje, + }); + } + + const buffer = await repositorio.generarArchivoExcel(productos); + + res.setHeader('Content-Disposition', 'attachment; filename=productos.xlsx'); + res.setHeader( + 'Content-Type', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ); + + return res.send(buffer); + } catch (error) { + console.error('Error al exportar productos:', error); + return res.status(MENSAJES_PRODUCTOS.ERROR_EXPORTACION.codigo).json({ + mensaje: MENSAJES_PRODUCTOS.ERROR_EXPORTACION.mensaje, + }); + } +}; diff --git a/Productos/Controladores/importarProductos.controller.js b/Productos/Controladores/importarProductos.controller.js new file mode 100644 index 00000000..35c59d7a --- /dev/null +++ b/Productos/Controladores/importarProductos.controller.js @@ -0,0 +1,141 @@ +const validarVariante = require('@altertex/util/vali/validarVarianteImportar'); +const validarOpcionesImportar = require('@altertex/util/vali/validarOpcionesImportar'); +const repositorioCrearProducto = require('@altertex/pro/repos/repositorioCrearProducto'); +const repositorioCrearVariante = require('@altertex/pro/repos/repositorioCrearVariante'); +const repositorioCrearOpcion = require('@altertex/pro/repos/repositorioCrearOpcion'); +const db = require('@altertex/util/bd/db'); +const validarProductoImportado = require('@altertex/util/vali/validarProductoImportado'); +const { crearGeneradorSKUConsecutivo } = require('@altertex/util/inter/generarSKUAuto'); +const { proveedorExiste } = require('@altertex/pro/repos/repositorioValidarProveedor'); + + +/** + * Importa productos y sus variantes/opciones para un cliente. + * + * Espera en req.body un array de objetos con la forma: + * [ + * { + * producto: { ... }, + * variantes: [ + * { + * ..., + * opciones: { ... } + * } + * ] + * } + * ] + * + * Valida cada producto, variante y opciones antes de insertar en la base de datos. + * Si hay errores en alguna fila, los acumula y los devuelve al finalizar. + * + * @async + * @function importarProductos + * @param {Express.Request} req - Request de Express, requiere req.user.clienteSeleccionado y req.body. + * @param { Express.Response} res - Response de Express. + * @returns {Promise} Devuelve un JSON con el resultado de la importación y los errores encontrados. + * + * @see RF[56] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF56] + */ +exports.importarProductos = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + const productos = req.body; + + const MENSAJE_PRODUCTOS_INVALIDOS = 'No se recibieron productos válidos.' + const MENSAJE_VARIANTES_INVALIDAS = 'No se recibieron productos válidos.' + const MENSAJE_ERRORES_ARCHIVO = 'Se encontraron errores en el archivo.' + const IMPORTACION_EXITOSA = 'Importación completada exitosamente.'; + const ERROR_AL_IMPORTAR = 'Error al importar productos.'; + + if (!Array.isArray(productos) || productos.length === 0) { + return res.status(400).json({ mensaje: MENSAJE_PRODUCTOS_INVALIDOS}); + } + + const errores = []; + let conexion = null; + + try { + conexion = await db.getConnection(); + await conexion.beginTransaction(); + + for (let im = 0; im < productos.length; im += 1) { + const { producto, variantes } = productos[im]; + const fila = im + 1; + + const errorProducto = validarProductoImportado(producto); + if (errorProducto) { + errores.push({ fila, error: errorProducto.error }); + continue; + } + if (producto.idProveedor !== null) { + const existe = await proveedorExiste(conexion, producto.idProveedor); + if (!existe) { + errores.push({ fila, error: `idProveedor ${producto.idProveedor} no existe en la base de datos.` }); + continue; + } + } + + if (!Array.isArray(variantes) || variantes.length === 0) { + errores.push({ fila, error: MENSAJE_VARIANTES_INVALIDAS }); + continue; + } + + for (const variante of variantes) { + const errorVariante = validarVariante(variante); + if (errorVariante) { + errores.push({ fila, error: errorVariante.error }); + continue; + } + + const errorOpciones = validarOpcionesImportar(variante.opciones); + if (errorOpciones) { + errores.push({ fila, error: errorOpciones.error }); + continue; + } + } + } + + if (errores.length > 0) { + await conexion.rollback(); + return res.status(200).json({ + mensaje: MENSAJE_ERRORES_ARCHIVO, + errores, + }); + } + const generarSKUConsecutivo = crearGeneradorSKUConsecutivo(); + for (let indice = 0; indice < productos.length; indice += 1) { + const { producto, variantes } = productos[indice]; + const idProducto = await repositorioCrearProducto.crearProducto(idCliente, producto); + + for (const variante of variantes) { + const idVariante = await repositorioCrearVariante.crearVariante(idProducto, variante); + + const opcionesConSKU = variante.opciones.map(opcion => ({ + ...opcion, + SKUautomatico: generarSKUConsecutivo( + producto.nombreComun, + variante.nombreVariante, + opcion.valorOpcion || 'SINVALOR' + ) + })); + + await repositorioCrearOpcion.crearOpcion(idVariante, opcionesConSKU); + } + } + + await conexion.commit(); + + return res.status(200).json({ + mensaje: IMPORTACION_EXITOSA, + errores: null, + }); + + } catch (err) { + if (conexion) await conexion.rollback(); + return res.status(500).json({ + mensaje: ERROR_AL_IMPORTAR, + error: err.message, + }); + } finally { + if (conexion) conexion.release(); + } +}; diff --git a/Productos/Controladores/leerProducto.controller.js b/Productos/Controladores/leerProducto.controller.js new file mode 100644 index 00000000..5303e6f6 --- /dev/null +++ b/Productos/Controladores/leerProducto.controller.js @@ -0,0 +1,38 @@ +const MENSAJES = require('@altertex/util/const/mensajesProductos'); +const repositorio = require('@altertex/pro/repos/repositorioLeerProducto'); +// RF[28] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF28] + +/** + * Controlador para leer la información de un producto específico. + * + * Este endpoint espera que el `idProducto` se encuentre en el cuerpo de la solicitud + * y que el `idCliente` esté disponible en el objeto `req.user.clienteSeleccionado`. + * + * @async + * @function leerProducto + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud. + * @param {string|number} req.body.idProducto - ID del producto a consultar. + * @param {object} req.user - Usuario autenticado con un cliente seleccionado. + * @param {string|number} req.user.clienteSeleccionado - ID del cliente asociado. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @returns {Promise} Retorna una respuesta HTTP con la información del producto o un mensaje de error. + * @throws {Error} Lanza un error si `idProducto` no es válido o si ocurre un error al consultar el repositorio. + */ +exports.leerProducto = async (req, res) => { + const idProducto = req.query.idProducto; + const idCliente = req.user.clienteSeleccionado; + + if (!idProducto) { + return res.status(MENSAJES.ID_INVALIDO.codigo).json({ mensaje: MENSAJES.ID_INVALIDO.mensaje }); + } + + try { + const infoProducto = await repositorio.leerProducto(idProducto, idCliente); + return res.status(MENSAJES.LEER_PRODUCTO_EXITO.codigo).json({ + infoProducto, + }); + } catch (error) { + return res.status(MENSAJES.ERROR_LEER_PRODUCTO.codigo).json({ mensaje: error.message }); + } +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/productosRepositorio.js b/Productos/Datos/Repositorios/productosRepositorio.js new file mode 100644 index 00000000..5af72a09 --- /dev/null +++ b/Productos/Datos/Repositorios/productosRepositorio.js @@ -0,0 +1,49 @@ +// RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30] +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProductos'); +const extraerNombreArchivoS3 = require('@altertex/util/ser/extraerNombreArchivoS3'); +const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3'); + +/** + * Funcion para eliminar productos de la base de datos. + * + * RF30 - Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30] + * + * @async + * @function eliminarProductos + * @param {Array} ids - Array de IDs de productos a eliminar. + * + * @returns {Promise} Resultado de la eliminación. + * @throws {Error} Si ocurre un error al ejecutar la consulta. + * - Retorna true si se eliminaron productos, false en caso contrario. + */ +const eliminarProductos = async (ids) => { + try { + // 1. Obtener imágenes asociadas + const placeholders = ids.map(() => '?').join(','); + const queryObtenerImagenes = consultas.OBTENER_IMAGENES_POR_IDS.replace('(?)', `(${placeholders})`); + const imagenes = await correrQuery(queryObtenerImagenes, ids); + + // 2. Eliminar imágenes válidas + for (const img of imagenes) { + if (img.urlImagen && !img.urlImagen.includes('placeholder')) { + const nombreReal = extraerNombreArchivoS3(img.urlImagen); + if (nombreReal) { + await eliminarImagenS3('productos/', nombreReal); + } + } + } + + // 3. Eliminar productos + const queryEliminar = consultas.ELIMINAR_PRODUCTOS.replace('(?)', `(${placeholders})`); + const resultado = await correrQuery(queryEliminar, ids); + + return resultado?.affectedRows > 0; + } catch { + return false; + } +}; + +module.exports = { + eliminarProductos, +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/repositorioConsultarProductos.js b/Productos/Datos/Repositorios/repositorioConsultarProductos.js new file mode 100644 index 00000000..66d90862 --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioConsultarProductos.js @@ -0,0 +1,22 @@ +// RF[27] Consulta Lista de Productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF27] +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProductos'); + +/** + * Obtiene la lista de productos disponibles para un cliente seleccionado. + * + * Realiza una consulta a la base de datos para obtener la lista de productos basándose en el `clienteSeleccionado`. + * Si ocurre algún error durante la consulta, se captura y se devuelve un arreglo vacío. + * + * @param {number} clienteSeleccionado - El ID del cliente para el que se obtendrán los productos. + * @returns {Promise} Una lista de productos del cliente o un arreglo vacío si ocurre un error. + */ +exports.obtenerProductos = async (clienteSeleccionado) => { + const query = consultas.OBTENER_LISTA; + try { + const resultados = await correrQuery(query, [clienteSeleccionado]); + return resultados; + } catch { + return []; + } +}; diff --git a/Productos/Datos/Repositorios/repositorioCrearOpcion.js b/Productos/Datos/Repositorios/repositorioCrearOpcion.js new file mode 100644 index 00000000..4237b61d --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioCrearOpcion.js @@ -0,0 +1,43 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const db = require('@altertex/util/bd/db'); +const consultas = require('@altertex/util/const/consultasOpciones'); + +/** + * Crea una o varias opciones relacionadas con una variante. + * + * @async + * @function crearOpcion + * @param {number} idVariante - ID de la variante a la que se le agregarán las opciones. + * @param {Array} opciones - Un arreglo con los objetos de opciones que se desean agregar. + * @returns {Promise} - Una promesa que se resuelve cuando todas las opciones se crean exitosamente. + */ +exports.crearOpcion = async (idVariante, opciones) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + for (const opcion of opciones) { + const params = [ + idVariante, + opcion.cantidad, + opcion.valorOpcion, + opcion.SKUautomatico, + opcion.SKUcomercial, + opcion.costoAdicional, + opcion.descuento, + opcion.estado, + ]; + + await conexion.query(consultas.CREAR, params); + } + + await conexion.commit(); + } catch (error) { + await conexion.rollback(); + console.error('Error al crear opciones:', error); + throw error; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/repositorioCrearProducto.js b/Productos/Datos/Repositorios/repositorioCrearProducto.js new file mode 100644 index 00000000..92cb133e --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioCrearProducto.js @@ -0,0 +1,63 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const db = require('@altertex/util/bd/db'); +const consultas = require('@altertex/util/const/consultasProductos'); + +/** + * Crea un nuevo producto en la base de datos. + * + * Esta función ejecuta una consulta SQL para insertar un producto en la base de datos, + * utilizando los parámetros proporcionados. Devuelve el ID del producto recién creado + * en caso de éxito, o un array vacío si ocurre algún error durante la operación. + * + * @param {number} clienteSeleccionado - ID del cliente para el cual se está creando el producto. + * @param {object} producto - Objeto que contiene la información del producto. + * @param {number} producto.idProveedor - ID del proveedor asociado al producto. + * @param {string} producto.nombreComun - Nombre común del producto. + * @param {string} producto.nombreComercial - Nombre comercial del producto. + * @param {string} producto.descripcion - Descripción del producto. + * @param {string} producto.marca - Marca del producto. + * @param {string} producto.modelo - Modelo del producto. + * @param {string} producto.tipoProducto - Tipo del producto. + * @param {number} producto.precioPuntos - Precio en puntos del producto. + * @param {number} producto.precioCliente - Precio para el cliente del producto. + * @param {number} producto.precioVenta - Precio de venta del producto. + * @param {number} producto.costo - Costo de producción del producto. + * @param {number} producto.impuesto - Impuesto aplicado al producto. + * @param {number} producto.descuento - Descuento aplicado al producto. + * @param {string} producto.estado - Estado del producto (activo/inactivo). + * @param {boolean} producto.envio - Indica si el producto es apto para envío. + * + * @returns {number|Array} El ID del producto recién creado en caso de éxito, o un array vacío en caso de error. + */ +exports.crearProducto = async (clienteSeleccionado, producto) => { + const conexion = await db.getConnection(); + + try { + const [resultados] = await conexion.query(consultas.CREAR, [ + clienteSeleccionado, + producto.idProveedor, + producto.nombreComun, + producto.nombreComercial, + producto.descripcion, + producto.marca, + producto.modelo, + producto.tipoProducto, + producto.precioPuntos, + producto.precioCliente, + producto.precioVenta, + producto.costo, + producto.impuesto, + producto.descuento, + producto.estado, + producto.envio, + ]); + + const idProducto = resultados.insertId; + return idProducto; + } catch (error) { + console.error('Error al crear producto:', error); + return []; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/repositorioCrearVariante.js b/Productos/Datos/Repositorios/repositorioCrearVariante.js new file mode 100644 index 00000000..5579abdc --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioCrearVariante.js @@ -0,0 +1,37 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const db = require('@altertex/util/bd/db'); +const consultas = require('@altertex/util/const/consultasVariantes'); + +/** + * Crea una nueva variante para un producto en la base de datos. + * + * Esta función ejecuta una consulta SQL para insertar una variante asociada a un producto, + * utilizando los parámetros proporcionados. Devuelve el ID de la variante recién creada + * en caso de éxito, o un array vacío si ocurre algún error durante la operación. + * + * @param {number} idProducto - ID del producto al que se le va a agregar la variante. + * @param {object} variante - Objeto que contiene la información de la variante. + * @param {string} variante.nombreVariante - Nombre de la variante (por ejemplo, color, tamaño). + * @param {string} variante.descripcion - Descripción de la variante. + * + * @returns {number|Array} El ID de la variante recién creada en caso de éxito, o un array vacío en caso de error. + */ +exports.crearVariante = async (idProducto, variante) => { + const conexion = await db.getConnection(); + + try { + const [resultados] = await conexion.query(consultas.CREAR, [ + idProducto, + variante.nombreVariante, + variante.descripcion + ]); + + const idVariante = resultados.insertId; + return idVariante; + } catch (error) { + console.error('Error al crear variante:', error); + return []; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/repositorioExportarProducto.js b/Productos/Datos/Repositorios/repositorioExportarProducto.js new file mode 100644 index 00000000..69b7af2c --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioExportarProducto.js @@ -0,0 +1,135 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_PRODUCTOS = require('@altertex/util/const/consultasProductos'); +const excelJS = require('exceljs'); + +/** + * Consulta la lista de productos seleccionados de un cliente para exportar en CSV. + * + * @param {number} idCliente + * @param {number[]} idsProducto + * @returns {Promise>} + * + * @see [RF58 - Documentación de requisitos](https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF58) + */ +exports.obtenerProductosExportacion = (idCliente, idsProducto) => { + const placeholders = idsProducto.map(() => '?').join(', '); + const query = CONSULTAS_PRODUCTOS.OBTENER_DATOS_EXPORTACION.replace('__IDS__', placeholders); + return correrQuery(query, [idCliente, ...idsProducto]); +}; + +/** + * Procesa y genera el archivo Excel con los productos exportados + * @param {Array} productos Lista de productos a exportar + * @returns {Promise} Buffer con el archivo Excel + */ +exports.generarArchivoExcel = async (productos) => { + const workbook = new excelJS.Workbook(); + + // Primera hoja - Información básica del producto + const hoja1 = workbook.addWorksheet('Información Producto'); + hoja1.columns = [ + { header: 'ID Producto', key: 'idProducto' }, + { header: 'ID Proveedor', key: 'idProveedor' }, + { header: 'Nombre Producto', key: 'nombreProducto' }, + { header: 'Nombre Comercial', key: 'nombreComercial' }, + { header: 'Descripción', key: 'descripcionProducto' }, + { header: 'Tipo Producto', key: 'tipoProducto' }, + { header: 'Marca', key: 'marca' }, + { header: 'Modelo', key: 'modelo' }, + { header: 'Costo', key: 'costo' }, + { header: 'Precio Venta', key: 'precioVenta' }, + { header: 'Precio Cliente', key: 'precioCliente' }, + { header: 'Precio Puntos', key: 'precioPuntos' }, + { header: 'Impuesto', key: 'impuesto' }, + { header: 'Descuento', key: 'descuento' }, + { header: 'Estado', key: 'estado' }, + { header: 'Envío', key: 'envio' }, + ]; + hoja1.addRows(productos); + + // Segunda hoja - Información desglosada de variantes + const hoja2 = workbook.addWorksheet('Variantes'); + hoja2.columns = [ + { header: 'ID Producto', key: 'idProducto' }, + { header: 'Nombre Producto', key: 'nombreProducto' }, + { header: 'Nombre Variante', key: 'nombreVariante' }, + { header: 'Descripción Variante', key: 'descripcionVariante' }, + ]; + + const variantesDesglosadas = procesarVariantes(productos); + hoja2.addRows(variantesDesglosadas); + + // Tercera hoja - Información desglosada de opciones + const hoja3 = workbook.addWorksheet('Opciones'); + hoja3.columns = [ + { header: 'ID Producto', key: 'idProducto' }, + { header: 'Nombre Producto', key: 'nombreProducto' }, + { header: 'Nombre Variante', key: 'nombreVariante' }, + { header: 'Valor Opción', key: 'valorOpcion' }, + { header: 'SKU Comercial', key: 'skuComercial' }, + { header: 'SKU Automático', key: 'skuAutomatico' }, + { header: 'Cantidad', key: 'cantidad' }, + ]; + + const opcionesDesglosadas = procesarOpciones(productos); + hoja3.addRows(opcionesDesglosadas); + + return await workbook.xlsx.writeBuffer(); +}; + +/** + * Procesa las variantes de los productos + * @param {Array} productos Lista de productos + * @returns {Array} Lista de variantes procesadas + */ +function procesarVariantes(productos) { + return productos.flatMap((producto) => { + const variantes = producto.variantes_opciones.split(' | ').map((variante) => { + const [datosVariante] = variante.split(','); + const [nombreVariante, descripcionVariante] = datosVariante.split('-'); + return { + idProducto: producto.idProducto, + nombreProducto: producto.nombreProducto, + nombreVariante, + descripcionVariante, + }; + }); + return variantes; + }); +} + +/** + * Procesa las opciones de los productos + * @param {Array} productos Lista de productos + * @returns {Array} Lista de opciones procesadas + */ +function procesarOpciones(productos) { + return productos.flatMap((producto) => { + return producto.variantes_opciones.split(' | ').flatMap((variante) => { + const [datosVariante, ...opcionesParts] = variante.split(','); + const [nombreVariante] = datosVariante.split('-'); + const opcionesCompletas = opcionesParts.join(',').trim(); + + if (!opcionesCompletas) return []; + + return opcionesCompletas + .split(', ') + .filter((opcion) => opcion.trim()) + .map((opcion) => { + const [valorOpcion, skuComercial, skuAutomatico, cantidad] = opcion + .split(':') + .map((valores) => valores.trim()); + + return { + idProducto: producto.idProducto, + nombreProducto: producto.nombreProducto, + nombreVariante, + valorOpcion, + skuComercial, + skuAutomatico, + cantidad, + }; + }); + }); + }); +} diff --git a/Productos/Datos/Repositorios/repositorioLeerProducto.js b/Productos/Datos/Repositorios/repositorioLeerProducto.js new file mode 100644 index 00000000..7c306d02 --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioLeerProducto.js @@ -0,0 +1,27 @@ +const CONSULTAS = require('@altertex/util/const/consultasProductos'); +const MENSAJES = require('@altertex/util/const/mensajesProductos'); +const correrQuery = require('@altertex/util/ser/correrQuery'); + +/** + * Lee la información de un producto específico para un cliente dado. + * + * @async + * @function leerProducto + * @param {number|string} idProducto - El identificador del producto a consultar. + * @param {number|string} idCliente - El identificador del cliente que realiza la consulta. + * @returns {Promise} Retorna una promesa que resuelve con los datos del producto si se encuentra. + * @throws {Error} Lanza un error si el producto no se encuentra o si ocurre un error durante la consulta. + */ +exports.leerProducto = async (idProducto, idCliente) => { + try { + const resultado = await correrQuery(CONSULTAS.LEER_PRODUCTO, [idProducto, idCliente]); + + if (resultado.length === 0) { + throw new Error(MENSAJES.PRODUCTO_NO_ENCONTRADO.mensaje); + } + + return resultado; + } catch (error) { + throw new Error(error.message); + } +}; diff --git a/Productos/Datos/Repositorios/repositorioProductoImagen.js b/Productos/Datos/Repositorios/repositorioProductoImagen.js new file mode 100644 index 00000000..706b00de --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioProductoImagen.js @@ -0,0 +1,48 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const db = require('@altertex/util/bd/db'); +const consultasProductos = require('@altertex/util/const/consultasProductos'); +const consultasImagenes = require('@altertex/util/const/consultasImagenes'); + +/** + * Crea una imagen en la base de datos y la asocia a un producto. + * + * Esta función realiza dos operaciones en una transacción: + * - Inserta una nueva imagen en la tabla de imágenes. + * - Crea una relación entre la imagen y el producto correspondiente. + * + * @param {number} idProducto - ID del producto al que se asociará la imagen. + * @param {string} urlImagenProducto - URL o ruta de la imagen del producto almacenada. + * @param {string} nombreComun - Nombre común o descriptivo asociado al producto. + * + * @returns {Promise} El resultado de la inserción de la imagen (incluyendo `insertId`) si es exitoso, o un arreglo vacío en caso de error. + */ +exports.crearImagen = async (idProducto, urlImagenProducto, nombreComun) => { + const parametrosImagen = [urlImagenProducto, 'Imagen Producto', nombreComun]; + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const [resultadoImagen] = await conexion.query( + consultasImagenes.CREAR, + parametrosImagen + ); + + const idImagen = resultadoImagen.insertId; + + const parametrosRelacion = [idImagen, idProducto]; + await conexion.query( + consultasProductos.CREAR_IMAGEN_PRODUCTO, + parametrosRelacion + ); + + await conexion.commit(); + return resultadoImagen; + } catch (error) { + console.error('Error al crear o asociar la imagen:', error); + await conexion.rollback(); + return []; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Productos/Datos/Repositorios/repositorioValidarProveedor.js b/Productos/Datos/Repositorios/repositorioValidarProveedor.js new file mode 100644 index 00000000..7886411c --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioValidarProveedor.js @@ -0,0 +1,19 @@ +const consultas = require('@altertex/util/const/consultasProveedores'); + + +/** + * Verifica si un proveedor existe en la base de datos. + * @param {object} conexion - La conexión a la base de datos. + * @param {number|null} idProveedor - El ID del proveedor a verificar. + * @returns {Promise} True si el proveedor existe o si idProveedor es null. + */ +async function proveedorExiste(conexion, idProveedor) { + if (idProveedor === null) return true; // Permitido por diseño + const [result] = await conexion.query( + consultas.VERIFICAR_EXISTE, + [idProveedor] + ); + return result.length > 0; +} + +module.exports = { proveedorExiste }; diff --git a/Productos/Datos/Repositorios/repositorioVarianteImagen.js b/Productos/Datos/Repositorios/repositorioVarianteImagen.js new file mode 100644 index 00000000..46686dcb --- /dev/null +++ b/Productos/Datos/Repositorios/repositorioVarianteImagen.js @@ -0,0 +1,48 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const db = require('@altertex/util/bd/db'); +const consultasVariantes = require('@altertex/util/const/consultasVariantes'); +const consultasImagenes = require('@altertex/util/const/consultasImagenes'); + +/** + * Crea una imagen en la base de datos y la asocia a una variante de producto. + * + * Esta función realiza dos operaciones dentro de una transacción: + * - Inserta una nueva imagen en la tabla de imágenes. + * - Crea una relación entre la imagen y la variante correspondiente. + * + * @param {number} idVariante - ID de la variante a la que se asociará la imagen. + * @param {string} urlImagenVariante - URL o ruta de la imagen de la variante almacenada. + * @param {string} nombreComun - Nombre común o descriptivo asociado a la variante. + * + * @returns {Promise} El resultado de la inserción de la imagen (incluyendo `insertId`) si es exitoso, o un arreglo vacío en caso de error. + */ +exports.crearImagen = async (idVariante, urlImagenVariante, nombreComun) => { + const parametrosImagen = [urlImagenVariante, 'Imagen Variante', nombreComun]; + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const [resultadoImagen] = await conexion.query( + consultasImagenes.CREAR, + parametrosImagen + ); + + const idImagen = resultadoImagen.insertId; + + const parametrosRelacion = [idImagen, idVariante]; + await conexion.query( + consultasVariantes.CREAR_IMAGEN_VARIANTE, + parametrosRelacion + ); + + await conexion.commit(); + return resultadoImagen; + } catch (error) { + console.error('Error al crear o asociar la imagen:', error); + await conexion.rollback(); + return []; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Productos/Rutas/RutasIndividuales/consultarProductos.routes.js b/Productos/Rutas/RutasIndividuales/consultarProductos.routes.js new file mode 100644 index 00000000..16475b06 --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/consultarProductos.routes.js @@ -0,0 +1,74 @@ +//RF27 Consulta Lista de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF27 +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pro/ctrl/consultarProductos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/productos/consultar-lista: + * post: + * summary: Consultar productos + * tags: [Productos] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: false + * content: + * application/json: + * schema: + * type: object + * properties: + * filtros: + * type: object + * description: Opcional, filtros para la búsqueda + * responses: + * 200: + * description: Consulta de productos exitosa + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Lista de productos consultada correctamente + * productos: + * type: array + * items: + * type: object + * properties: + * idProducto: + * type: integer + * example: 1 + * nombreComun: + * type: string + * example: Camiseta básica + * claveProducto: + * type: string + * example: CAM-001 + * activo: + * type: boolean + * example: true + * 401: + * description: No autorizado - Credenciales inválidas o sin permisos + * 500: + * description: Error del servidor al obtener los productos + */ + +ruteador.post( + RUTAS.PRODUCTOS.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_PRODUCTOS), + controlador.consultarProductos +); + +module.exports = ruteador; diff --git a/Productos/Rutas/RutasIndividuales/crearProducto.routes.js b/Productos/Rutas/RutasIndividuales/crearProducto.routes.js new file mode 100644 index 00000000..f8d4f63c --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/crearProducto.routes.js @@ -0,0 +1,118 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pro/ctrl/crearProducto.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/productos/crear: + * post: + * summary: Crear un nuevo producto + * tags: [Productos] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * consumes: + * - multipart/form-data + * requestBody: + * required: true + * content: + * multipart/form-data: + * schema: + * type: object + * required: + * - producto + * - variantes + * - mapaImagenes + * - imagenProducto + * - imagenesVariante + * properties: + * producto: + * type: string + * format: json + * description: Información del producto en formato JSON + * example: '{"nombreComun":"Camisa casual","nombreComercial":"Camisa formal","descripcion":"Camisa de algodón","marca":"Brand","modelo":"M-123","tipoProducto":"Ropa","precioPuntos":100,"precioCliente":50.99,"precioVenta":59.99,"costo":30.00,"impuesto":16,"descuento":0,"idProveedor":1,"estado":1,"envio":1}' + * variantes: + * type: string + * format: json + * description: Array de variantes del producto en formato JSON + * example: '[{"identificador":"var1","nombreVariante":"Color","descripcion":"Color de la prenda","opciones":[{"valorOpcion":"Azul","cantidad":10,"descuento":0,"costoAdicional":0,"SKUautomatico":"SKU-AUTO-1","SKUcomercial":"SKU-COM-1"}]}]' + * mapaImagenes: + * type: string + * format: json + * description: Mapa que relaciona imágenes con variantes en formato JSON + * example: '[{"idVariante":"var1"}]' + * imagenProducto: + * type: string + * format: binary + * description: Imagen principal del producto + * imagenesVariante: + * type: array + * items: + * type: string + * format: binary + * description: Imágenes asociadas a cada variante del producto + * responses: + * 200: + * description: Producto creado correctamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Producto creado correctamente + * 400: + * description: Error en los parámetros proporcionados + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Los parámetros proporcionados no son válidos + * 401: + * description: No autorizado, token inválido o falta de permisos + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No tiene permisos para realizar esta acción + * 500: + * description: Error interno del servidor + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al crear producto + * error: + * type: string + * example: Error al crear variante + */ + +ruteador.post( + RUTAS.PRODUCTOS.CREAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CREAR_PRODUCTO), + controlador.crearProducto +); + +module.exports = ruteador; diff --git a/Productos/Rutas/RutasIndividuales/eliminarProducto.routes.js b/Productos/Rutas/RutasIndividuales/eliminarProducto.routes.js new file mode 100644 index 00000000..263b9f2e --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/eliminarProducto.routes.js @@ -0,0 +1,55 @@ +// RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pro/ctrl/eliminarProducto.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30] + * + * @swagger + * /api/productos/eliminar: + * delete: + * summary: Eliminar uno o varios productos + * tags: [Productos] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * ids: + * type: array + * items: + * type: integer + * example: [1, 2, 3] + * responses: + * 200: + * description: Productos eliminados exitosamente + * 400: + * description: Error al eliminar los productos + * 401: + * description: No autorizado + * 500: + * description: Error interno del servidor + */ +ruteador.delete( + RUTAS.PRODUCTOS.ELIMINAR_PRODUCTO, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_PRODUCTO), + controlador.eliminarProductoController +); + +module.exports = ruteador; diff --git a/Productos/Rutas/RutasIndividuales/exportarProductos.routes.js b/Productos/Rutas/RutasIndividuales/exportarProductos.routes.js new file mode 100644 index 00000000..5fde8376 --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/exportarProductos.routes.js @@ -0,0 +1,77 @@ +//RF58 - Exportar Productos - https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF58 + +/** + * @swagger + * /api/productos/exportar: + * post: + * summary: Exporta la lista completa de empleados en formato CSV. + * tags: + * - Productos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * parameters: + * - in: header + * name: x-api-key + * required: true + * schema: + * type: string + * description: Clave de API + * - in: header + * name: Authorization + * required: true + * schema: + * type: string + * description: Token JWT con formato "Bearer " + * responses: + * 200: + * description: Archivo CSV generado correctamente. + * content: + * text/csv: + * schema: + * type: string + * format: binary + * 204: + * description: No hay productos para exportar. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No hay productos para exportar. + * 400: + * description: Error interno del servidor al exportar productos. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al exportar la lista de productos. + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pro/ctrl/exportarProductos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +ruteador.post( + RUTAS.PRODUCTOS.EXPORTAR_PRODUCTOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + validarYSanitizar, + verificarPermisos(PERMISOS.EXPORTAR_PRODUCTOS), + controlador.exportarProductos +); + +module.exports = ruteador; diff --git a/Productos/Rutas/RutasIndividuales/importarProductos.routes.js b/Productos/Rutas/RutasIndividuales/importarProductos.routes.js new file mode 100644 index 00000000..2c95b954 --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/importarProductos.routes.js @@ -0,0 +1,191 @@ +// RF[56] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF56] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/pro/ctrl/importarProductos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/productos/importar-productos: + * post: + * summary: Importa un lote de productos con variantes y opciones desde un archivo procesado. + * tags: + * - Productos + * security: + * - bearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: array + * items: + * type: object + * required: + * - producto + * - variantes + * properties: + * producto: + * type: object + * properties: + * idProveedor: + * type: integer + * nullable: true + * example: 12 + * nombreComun: + * type: string + * example: Tornillo galvanizado + * nombreComercial: + * type: string + * nullable: true + * example: Tornillo FG 3/4 + * descripcion: + * type: string + * nullable: true + * example: Tornillo de acero inoxidable + * marca: + * type: string + * nullable: true + * example: TRUPER + * modelo: + * type: string + * nullable: true + * example: M3X40 + * tipoProducto: + * type: string + * nullable: true + * example: Ferretería + * costo: + * type: number + * example: 3.5 + * precioVenta: + * type: number + * example: 5.0 + * precioCliente: + * type: number + * example: 4.5 + * precioPuntos: + * type: number + * example: 4.0 + * impuesto: + * type: number + * example: 0.16 + * descuento: + * type: number + * example: 0.1 + * estado: + * type: integer + * enum: [0, 1] + * example: 1 + * envio: + * type: integer + * enum: [0, 1] + * example: 1 + * variantes: + * type: array + * items: + * type: object + * required: + * - nombreVariante + * - opciones + * properties: + * nombreVariante: + * type: string + * example: Color + * descripcion: + * type: string + * nullable: true + * example: Variantes por color + * opciones: + * type: array + * items: + * type: object + * required: + * - cantidad + * - valorOpcion + * - SKUautomatico + * - SKUcomercial + * - costoAdicional + * - descuento + * - estado + * properties: + * cantidad: + * type: integer + * example: 10 + * valorOpcion: + * type: string + * example: Rojo + * SKUautomatico: + * type: string + * example: SKU-AUTO-1 + * SKUcomercial: + * type: string + * example: SKU-ROJO-123 + * costoAdicional: + * type: number + * example: 0.5 + * descuento: + * type: number + * example: 10 + * estado: + * type: integer + * enum: [0, 1] + * example: 1 + * responses: + * 200: + * description: Importación completada exitosamente o con errores parciales. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Importación completada exitosamente. + * errores: + * type: array + * items: + * type: object + * properties: + * fila: + * type: integer + * example: 3 + * error: + * type: string + * example: Producto sin variantes válidas. + * 400: + * description: No se recibieron productos válidos. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No se recibieron productos válidos. + * 401: + * description: No autorizado. Token inválido o faltante. + * 403: + * description: Acceso denegado por falta de permisos. + * 500: + * description: Error interno del servidor. + */ + +ruteador.post( + RUTAS.PRODUCTOS.IMPORTAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + validarYSanitizar, + verificarPermisos(PERMISOS.IMPORTAR_PRODUCTOS), + controlador.importarProductos +); + +module.exports = ruteador; diff --git a/Productos/Rutas/RutasIndividuales/leerProductos.routes.js b/Productos/Rutas/RutasIndividuales/leerProductos.routes.js new file mode 100644 index 00000000..d11e4424 --- /dev/null +++ b/Productos/Rutas/RutasIndividuales/leerProductos.routes.js @@ -0,0 +1,14 @@ +const express = require('express'); +const ruteador = express.Router(); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const controlador = require('@altertex/pro/ctrl/leerProducto.controller'); +// RF[28] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF28] + +ruteador.get(RUTAS.PRODUCTOS.LEER, revisarApiKey(), autorizarToken, verificarPermisos(PERMISOS.LEER_PRODUCTO), validarYSanitizar, controlador.leerProducto); + +module.exports = ruteador; \ No newline at end of file diff --git a/Productos/Rutas/indexProductos.routes.js b/Productos/Rutas/indexProductos.routes.js new file mode 100644 index 00000000..3f014e4e --- /dev/null +++ b/Productos/Rutas/indexProductos.routes.js @@ -0,0 +1,25 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutaConsultarLista = require('@altertex/pro/rutasInd/consultarProductos.routes'); +const rutaEliminar = require('@altertex/pro/rutasInd/eliminarProducto.routes'); +const rutaCrearProducto = require('@altertex/pro/rutasInd/crearProducto.routes'); +const rutaImportarProductos = require('@altertex/pro/rutasInd/importarProductos.routes'); +const rutasLeerProducto = require('@altertex/pro/rutasInd/leerProductos.routes'); +const rutasExportarProductos = require('@altertex/pro/rutasInd/exportarProductos.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +//RF27 Consulta Lista de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF27 +ruteador.use(RUTAS.PRODUCTOS.BASE, rutaConsultarLista); +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +ruteador.use(RUTAS.PRODUCTOS.BASE, rutaCrearProducto); +// RF[30] Eliminar Producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF30] +ruteador.use(RUTAS.PRODUCTOS.BASE, rutaEliminar); +// RF[28] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF28] +ruteador.use(RUTAS.PRODUCTOS.BASE, rutasLeerProducto); +// RF[56] Leer producto - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF56] +ruteador.use(RUTAS.PRODUCTOS.BASE, rutaImportarProductos); +// RF[58] Exportar Productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF58] +ruteador.use(RUTAS.PRODUCTOS.BASE, rutasExportarProductos); + +module.exports = ruteador; diff --git a/Proveedores/Controladores/consultarProveedores.controller.js b/Proveedores/Controladores/consultarProveedores.controller.js new file mode 100644 index 00000000..418c9b02 --- /dev/null +++ b/Proveedores/Controladores/consultarProveedores.controller.js @@ -0,0 +1,51 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const repositorio = require('@altertex/prove/repos/repositorioConsultarProveedores'); +const MENSAJES_PROVEEDORES = require('@altertex/util/const/mensajesProveedores'); + +/** + * Controlador para la consulta de la lista de proveedores de un cliente. + * + * @async + * @function consultarLista + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.user - Datos del usuario autenticado. + * @param {number} req.user.clienteSeleccionado - ID del cliente seleccionado para la consulta. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la consulta es exitosa, junto con los datos de los proveedores. + * - 204 si no hay proveedores registrados. + * - 400 si los parámetros proporcionados son inválidos. + * - 500 si ocurre un error en el servidor. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.consultarLista = async (req, res) => { + const idCliente = parseInt(req.user?.clienteSeleccionado); + + if (!idCliente || isNaN(idCliente)) { + return res.status(MENSAJES_PROVEEDORES.DATOS_PROVEEDOR_INVALIDOS.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.DATOS_PROVEEDOR_INVALIDOS.mensaje, + }); + } + + try { + const resultados = await repositorio.obtenerProveedores(idCliente); + + if (!resultados || resultados.length === 0) { + return res.status(MENSAJES_PROVEEDORES.LISTA_PROVEEDORES_VACIA.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.LISTA_PROVEEDORES_VACIA.mensaje, + }); + } + + return res.status(MENSAJES_PROVEEDORES.CONSULTA_PROVEEDORES_EXITOSA.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.CONSULTA_PROVEEDORES_EXITOSA.mensaje, + listaProveedores: resultados, + }); + } catch (error) { + console.error('Error al consultar proveedores:', error); + return res.status(MENSAJES_PROVEEDORES.ERROR_CONSULTAR_PROVEEDORES.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.ERROR_CONSULTAR_PROVEEDORES.mensaje, + }); + } +}; diff --git a/Proveedores/Controladores/crearProveedor.controller.js b/Proveedores/Controladores/crearProveedor.controller.js new file mode 100644 index 00000000..7d8a70ef --- /dev/null +++ b/Proveedores/Controladores/crearProveedor.controller.js @@ -0,0 +1,48 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const validarProveedor = require('@altertex/util/vali/validarProveedor'); +const repositorioCrearProveedor = require('@altertex/prove/repos/repositorioCrearProvedor'); +const MENSAJES_PROVEEDORES = require('@altertex/util/const/mensajesProveedores'); + +/** + * Controlador para crear un proveedor de forma independiente. + * + * @async + * @function crearProveedor + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la creación es exitosa, junto con el ID del proveedor. + * - 400 si los parámetros proporcionados son inválidos. + * - 500 si ocurre un error en el servidor. + */ +exports.crearProveedor = async (req, res) => { + const proveedor = req.body; + const idCliente = parseInt(req.user.clienteSeleccionado); + + const errorProveedor = validarProveedor(proveedor); + if (errorProveedor) { + return res.status(MENSAJES_PROVEEDORES.DATOS_PROVEEDOR_INVALIDOS.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.DATOS_PROVEEDOR_INVALIDOS.mensaje, + }); + } + + try { + const idProveedor = await repositorioCrearProveedor.crearProveedor(idCliente, proveedor); + + if (!idProveedor) { + throw new Error('Error al crear proveedor'); + } + + return res.status(MENSAJES_PROVEEDORES.PROVEEDOR_CREADO_EXITOSAMENTE.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.PROVEEDOR_CREADO_EXITOSAMENTE.mensaje, + }); + } catch (error) { + console.error('Error al crear proveedor:', error); + return res.status(MENSAJES_PROVEEDORES.ERROR_CREAR_PROVEEDOR.codigo).json({ + mensaje: MENSAJES_PROVEEDORES.ERROR_CREAR_PROVEEDOR.mensaje, + error: error.message, + }); + } +}; diff --git a/Proveedores/Datos/Repositorios/repositorioConsultarProveedores.js b/Proveedores/Datos/Repositorios/repositorioConsultarProveedores.js new file mode 100644 index 00000000..9f1ea2df --- /dev/null +++ b/Proveedores/Datos/Repositorios/repositorioConsultarProveedores.js @@ -0,0 +1,25 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProveedores'); + +/** + * Obtiene la lista de proveedores registrados para un cliente específico. + * + * Esta función ejecuta una consulta SQL para recuperar los proveedores asociados + * al cliente proporcionado. Devuelve un arreglo de proveedores si la operación + * es exitosa, o un arreglo vacío en caso de error. + * + * @param {string|number} clienteSeleccionado - ID o identificador del cliente del cual se quieren obtener los proveedores. + * + * @returns {Promise} Un arreglo de objetos que representan los proveedores, o un arreglo vacío si ocurre un error. + */ +exports.obtenerProveedores = async (clienteSeleccionado) => { + const query = consultas.OBTENER_LISTA; + try { + const resultados = await correrQuery(query, [clienteSeleccionado]); + return resultados; + } catch (error) { + console.error('Error al obtener los proveedores:', error); + return []; + } +}; diff --git a/Proveedores/Datos/Repositorios/repositorioCrearProvedor.js b/Proveedores/Datos/Repositorios/repositorioCrearProvedor.js new file mode 100644 index 00000000..cc674bb4 --- /dev/null +++ b/Proveedores/Datos/Repositorios/repositorioCrearProvedor.js @@ -0,0 +1,46 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const correrQuery = require('@altertex/util/ser/correrQuery'); +const consultas = require('@altertex/util/const/consultasProveedores'); + +/** + * Crea un nuevo proveedor en la base de datos. + * + * Esta función ejecuta una consulta SQL para insertar los datos de un proveedor + * utilizando los parámetros proporcionados. Devuelve el ID del proveedor recién creado + * en caso de éxito, o un array vacío si ocurre algún error durante la operación. + * + * @param {string|number} clienteSeleccionado - ID o identificador del cliente del cual se quieren obtener los proveedores. + * @param {object} proveedor - Objeto que contiene la información del proveedor. + * @param {string} proveedor.nombre - Nombre del contacto del proveedor. + * @param {string} proveedor.nombreCompania - Nombre de la compañía del proveedor. + * @param {string} proveedor.telefonoContacto - Teléfono de contacto del proveedor. + * @param {string} proveedor.direccion - Dirección del proveedor. + * @param {string} proveedor.codigoPostal - Código postal de la dirección del proveedor. + * @param {string} proveedor.pais - País del proveedor. + * @param {string} proveedor.estado - Estado o provincia del proveedor. + * + * @returns {Promise} El ID del proveedor recién creado en caso de éxito, o un arreglo vacío en caso de error. + */ +exports.crearProveedor = async (clienteSeleccionado, proveedor) => { + const query = consultas.CREAR; + const parametros = [ + clienteSeleccionado, + proveedor.nombre, + proveedor.nombreCompania, + proveedor.telefonoContacto, + proveedor.direccion, + proveedor.codigoPostal, + proveedor.pais, + proveedor.estado, + ]; + + try { + const resultados = await correrQuery(query, parametros); + + const idProveedor = resultados.insertId; + return idProveedor; + } catch (error) { + console.error('Error al crear proveedor:', error); + return []; + } +}; diff --git a/Proveedores/Rutas/RutasIndividuales/consultarProveedores.routes.js b/Proveedores/Rutas/RutasIndividuales/consultarProveedores.routes.js new file mode 100644 index 00000000..c520bf2c --- /dev/null +++ b/Proveedores/Rutas/RutasIndividuales/consultarProveedores.routes.js @@ -0,0 +1,129 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/prove/ctrl/consultarProveedores.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/proveedores/consultar-lista: + * post: + * summary: Consultar lista de proveedores + * description: Obtiene la lista de proveedores asociados al cliente seleccionado + * tags: [Proveedores] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: false + * content: + * application/json: + * schema: + * type: object + * example: {} + * responses: + * 200: + * description: Consulta exitosa + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Consulta de proveedores exitosa + * listaProveedores: + * type: array + * items: + * type: object + * properties: + * id: + * type: integer + * example: 1 + * nombre: + * type: string + * example: Juan Pérez + * nombreCompania: + * type: string + * example: Textiles del Norte S.A. + * telefonoContacto: + * type: string + * example: +52 55 1234 5678 + * correoContacto: + * type: string + * example: juan.perez@textilesnorte.com + * direccion: + * type: string + * example: Av. Industrial 123, Col. Centro + * codigoPostal: + * type: string + * example: 12345 + * pais: + * type: string + * example: México + * estado: + * type: integer + * example: 1 + * fechaCreacion: + * type: string + * format: date-time + * example: 2023-10-15T14:30:00Z + * 204: + * description: No hay proveedores registrados + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No hay proveedores registrados + * 400: + * description: Parámetros inválidos + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Los datos del proveedor son inválidos + * 401: + * description: No autorizado, token inválido o falta de permisos + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No tiene permisos para realizar esta acción + * 500: + * description: Error interno del servidor + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al consultar proveedores + */ + +ruteador.post( + RUTAS.PROVEEDORES.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_PRODUCTOS), + controlador.consultarLista +); + +module.exports = ruteador; diff --git a/Proveedores/Rutas/RutasIndividuales/crearProveedor.routes.js b/Proveedores/Rutas/RutasIndividuales/crearProveedor.routes.js new file mode 100644 index 00000000..944fe067 --- /dev/null +++ b/Proveedores/Rutas/RutasIndividuales/crearProveedor.routes.js @@ -0,0 +1,128 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/prove/ctrl/crearProveedor.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/proveedores/crear: + * post: + * summary: Crear un nuevo proveedor + * description: Crea un nuevo proveedor asociado al cliente seleccionado + * tags: [Proveedores] + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - nombre + * - nombreCompania + * - telefonoContacto + * - correoContacto + * - direccion + * - codigoPostal + * - pais + * - estado + * properties: + * nombre: + * type: string + * description: Nombre del contacto del proveedor + * example: Juan Pérez + * nombreCompania: + * type: string + * description: Nombre de la compañía del proveedor + * example: Textiles del Norte S.A. + * telefonoContacto: + * type: string + * description: Número telefónico de contacto + * example: +52 55 1234 5678 + * correoContacto: + * type: string + * description: Correo electrónico de contacto + * example: juan.perez@textilesnorte.com + * direccion: + * type: string + * description: Dirección física del proveedor + * example: Av. Industrial 123, Col. Centro + * codigoPostal: + * type: string + * description: Código postal de la dirección + * example: 12345 + * pais: + * type: string + * description: País donde se encuentra el proveedor + * example: México + * estado: + * type: number + * description: Estado del proveedor (1 activo, 0 inactivo) + * enum: [0, 1] + * example: 1 + * responses: + * 200: + * description: Proveedor creado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Proveedor creado exitosamente + * 400: + * description: Datos del proveedor inválidos + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Los datos del proveedor son inválidos + * 401: + * description: No autorizado, token inválido o falta de permisos + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No tiene permisos para realizar esta acción + * 500: + * description: Error interno del servidor + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al crear proveedor + * error: + * type: string + * example: Error al crear proveedor + */ + +ruteador.post( + RUTAS.PROVEEDORES.CREAR, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CREAR_PRODUCTO), + controlador.crearProveedor +); + +module.exports = ruteador; diff --git a/Proveedores/Rutas/indexProveedores.routes.js b/Proveedores/Rutas/indexProveedores.routes.js new file mode 100644 index 00000000..f464092b --- /dev/null +++ b/Proveedores/Rutas/indexProveedores.routes.js @@ -0,0 +1,13 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutaConsultarLista = require('@altertex/prove/rutasInd/consultarProveedores.routes'); +const rutaCrearProveedor = require('@altertex/prove/rutasInd/crearProveedor.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +ruteador.use(RUTAS.PROVEEDORES.BASE, rutaConsultarLista); +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +ruteador.use(RUTAS.PROVEEDORES.BASE, rutaCrearProveedor); + +module.exports = ruteador; diff --git a/Roles/Controladores/actualizarRol.controller.js b/Roles/Controladores/actualizarRol.controller.js new file mode 100644 index 00000000..0e129abb --- /dev/null +++ b/Roles/Controladores/actualizarRol.controller.js @@ -0,0 +1,29 @@ +const MENSAJES = require('@altertex/util/const/mensajesRoles'); +const repositorio = require('@altertex/rol/repos/repositorioActualizarRol'); + +/** + * Controlador para actualizar un rol. + * + * Este controlador recibe los datos del rol a actualizar desde el cuerpo de la solicitud + * y el cliente seleccionado desde el usuario autenticado. Valida los datos y delega + * la lógica de actualización al repositorio. + * + * @function + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @returns {Promise} - Retorna una respuesta HTTP con el resultado de la operación. + */ +exports.actualizarRol = async (req, res) => { + const datosActualizacion = req.body.datosRolActualizacion; + + if (!datosActualizacion) { + return res.status(MENSAJES.PARAMETROS_INVALIDOS.codigo).json({ mensaje: MENSAJES.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + await repositorio.actualizarRol(datosActualizacion); + return res.status(MENSAJES.ACTUALIZAR_ROL.codigo).json({ mensaje: MENSAJES.ACTUALIZAR_ROL.mensaje }); + } catch (error) { + return res.status(400).json({ mensaje: error.message }); + } +}; \ No newline at end of file diff --git a/Roles/Controladores/consultarDetalleRol.controller.js b/Roles/Controladores/consultarDetalleRol.controller.js new file mode 100644 index 00000000..a402fe08 --- /dev/null +++ b/Roles/Controladores/consultarDetalleRol.controller.js @@ -0,0 +1,69 @@ +const repositorio = require('@altertex/rol/repos/repositorioDetalleRol'); +const MENSAJES_ROLES = require('@altertex/util/const/mensajesRoles'); + +/** + * RF8 - Leer rol + * Documentación del requisito funcional: + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF8 + * + * @function consultarDetalle + * @async + * @param {object} req - Objeto de solicitud HTTP (Request). + * @param {object} res - Objeto de respuesta HTTP (Response). + * @returns {Response} Respuesta HTTP con los detalles del rol solicitado. + * + * @description + * Este controlador obtiene el detalle de un rol: nombre, descripción, + * número de usuarios asociados y permisos relacionados. + */ +exports.consultarDetalle = async (req, res) => { + try { + const { idRol } = req.query; + + // Validación: verificar que se proporcione un ID válido + if (!idRol || isNaN(Number(idRol))) { + return res + .status(MENSAJES_ROLES.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_ROLES.PARAMETROS_INVALIDOS.mensaje }); + } + + // Se consulta al repositorio de roles para obtener el detalle por ID. + const resultado = await repositorio.obtenerDetalleRol(Number(idRol)); + + // Validación: si no se encontró el rol, se responde con mensaje de "sin resultados". + if (!resultado || resultado.length === 0) { + return res + .status(MENSAJES_ROLES.SIN_RESULTADOS.codigo) + .json({ mensaje: MENSAJES_ROLES.SIN_RESULTADOS.mensaje }); + } + + // Extrae los datos generales del rol desde la primera fila + const { nombreRol, descripcionRol, totalUsuarios } = resultado[0]; + + // Construye arreglo de permisos (omite si no tiene permisos) + const permisos = resultado + .filter(permiso => permiso.idPermiso !== null) + .map(permiso => ({ + id: permiso.idPermiso, + nombre: permiso.nombrePermiso, + descripcion: permiso.descripcionPermiso, + })); + + // Respuesta exitosa con datos + return res.status(MENSAJES_ROLES.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_ROLES.CONSULTA_EXITOSA.mensaje, + rol: { + idRol: Number(idRol), + nombre: nombreRol, + descripcion: descripcionRol, + totalUsuarios, + permisos, + }, + }); + } catch { + // Error inesperado en el servidor + return res + .status(MENSAJES_ROLES.ERROR_CONSULTAR_ROLES.codigo) + .json({ mensaje: MENSAJES_ROLES.ERROR_CONSULTAR_ROLES.mensaje }); + } +}; \ No newline at end of file diff --git a/Roles/Controladores/consultarLista.controller.js b/Roles/Controladores/consultarLista.controller.js new file mode 100644 index 00000000..0245c902 --- /dev/null +++ b/Roles/Controladores/consultarLista.controller.js @@ -0,0 +1,44 @@ +// Importación del repositorio de roles y los mensajes constantes relacionados a la funcionalidad de roles. +const repositorio = require('@altertex/rol/repos/repositorioRoles'); +const MENSAJES_ROLES = require('@altertex/util/const/mensajesRoles'); + +/** + * Controlador para consultar la lista de roles. + * + * @function consultarLista + * @async + * @param {object} req - Objeto de solicitud HTTP (Request). + * @param {object} res - Objeto de respuesta HTTP (Response). + * @returns {Response} Respuesta HTTP con el resultado de la consulta. + * + * @description + * Este controlador realiza una consulta al repositorio de roles para obtener + * la lista completa. Si no se encuentran resultados, responde con el código y mensaje + * correspondiente a resultados vacíos. En caso de éxito, devuelve la lista con un mensaje + * de éxito. Ante cualquier error inesperado, devuelve un error 500. + */ +exports.consultarLista = async (req, res) => { + try { + // Se consulta al repositorio de roles para obtener todos los registros. + const resultados = await repositorio.obtenerRoles(); + + + // Validación: si no se encontraron resultados, se responde con el mensaje de "sin resultados". + if (!resultados || resultados.length === 0) { + return res + .status(MENSAJES_ROLES.SIN_RESULTADOS.codigo) + .json({ mensaje: MENSAJES_ROLES.SIN_RESULTADOS.mensaje }); + } + + // En caso de éxito, se responde con el mensaje correspondiente y los datos encontrados. + return res.status(MENSAJES_ROLES.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_ROLES.CONSULTA_EXITOSA.mensaje, + roles: resultados, + }); + } catch { + // Manejo de errores inesperados, con log en consola para facilitar el diagnóstico. + + // Se responde con un error 500 y un mensaje genérico para el cliente. + return res.status(500).json({ mensaje: 'Error al consultar roles' }); + } +}; diff --git a/Roles/Controladores/crearRol.controller.js b/Roles/Controladores/crearRol.controller.js new file mode 100644 index 00000000..bdec2217 --- /dev/null +++ b/Roles/Controladores/crearRol.controller.js @@ -0,0 +1,62 @@ +const repositorio = require('@altertex/rol/repos/repositorioCrearRol'); +const MENSAJES = require('@altertex/util/const/mensajesRoles'); + +/** + * Controlador para crear un nuevo rol. + * + * Este controlador realiza las siguientes validaciones y operaciones: + * - Verifica que el nombre del rol sea válido. + * - Verifica que los permisos sean un arreglo no vacío. + * - Valida que el nombre del rol no esté duplicado en la base de datos. + * - Valida que todos los IDs de permisos proporcionados existan en la base de datos. + * - Inserta el rol y asocia los permisos si todas las validaciones son exitosas. + * + * @async + * @function crearRol + * @param {Express.Request} req - Objeto de solicitud HTTP. Se espera que el cuerpo (`req.body`) contenga: + * @param {string} req.body.nombre - Nombre del rol a crear. + * @param {string} [req.body.descripcion] - Descripción opcional del rol. + * @param {number[]} req.body.permisos - Lista de IDs de permisos a asociar. + * @param {Express.Response} res - Objeto de respuesta HTTP. + * @returns {Promise} - Respuesta JSON con el estado de la creación del rol. + */ +exports.crearRol = async (req, res) => { + const { nombre, descripcion, permisos } = req.body; + + // Validación del nombre del rol + if (!nombre || typeof nombre !== 'string') { + return res.status(400).json({ mensaje: MENSAJES.NOMBRE_OBLIGATORIO }); + } + + // Validación de los permisos + if (!Array.isArray(permisos) || permisos.length === 0) { + return res.status(400).json({ mensaje: MENSAJES.PERMISOS_OBLIGATORIOS }); + } + + try { + // Verificar si el nombre del rol ya existe + const existe = await repositorio.verificarNombreRol(nombre); + if (existe) { + return res.status(400).json({ mensaje: MENSAJES.ROL_EXISTENTE }); + } + + // Verificar que todos los permisos sean válidos + for (const idPermiso of permisos) { + const valido = await repositorio.verificarPermiso(idPermiso); + if (!valido) { + return res.status(400).json({ mensaje: MENSAJES.PERMISO_INVALIDO(idPermiso) }); + } + } + + // Crear el rol y asociar los permisos + const resultado = await repositorio.crearRol(nombre, descripcion); + if (resultado.insertId) { + await repositorio.asociarPermisosARol(resultado.insertId, permisos); + return res.status(201).json({ mensaje: MENSAJES.ROL_CREADO }); + } else { + return res.status(400).json({ mensaje: MENSAJES.ERROR_CREACION }); + } + } catch { + return res.status(500).json({ mensaje: MENSAJES.ERROR_CREACION }); + } +}; diff --git a/Roles/Controladores/eliminarRol.controller.js b/Roles/Controladores/eliminarRol.controller.js new file mode 100644 index 00000000..3d7a2d59 --- /dev/null +++ b/Roles/Controladores/eliminarRol.controller.js @@ -0,0 +1,30 @@ +const MENSAJES = require('@altertex/util/const/mensajesRoles'); +const repositorio = require('@altertex/rol/repos/repositoriorEliminar'); +// RF10 - Eliminar rol - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF10 + +/** + * Controlador para eliminar un rol existente. + * + * @async + * @function eliminarRol + * @param {Express.Request} req - Objeto de solicitud de Express, debe contener `idRol` en `req.body`. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @returns {Promise} - No retorna un valor, responde al cliente con un JSON indicando éxito o error. + * + * @description + * Esta función intenta eliminar un rol utilizando el repositorio correspondiente. + * Si la eliminación es exitosa, responde con un código 200 y un mensaje de éxito. + * Si ocurre un error, responde con un código 400 y un mensaje de error. + */ +exports.eliminarRol = async (req, res) => { + const idRol = req.body.idsRol; + try { + await repositorio.eliminarRol(idRol); + return res + .status(MENSAJES.ELIMINAR_ROL_EXITO.codigo) + .json({ mensaje: MENSAJES.ELIMINAR_ROL_EXITO.mensaje }); + } catch (error) { + const mensaje = error.message || MENSAJES.ELIMINAR_ROL_ERROR.mensaje; + return res.status(400).json({ mensaje }); + } +}; diff --git a/Roles/Controladores/obtenerOpcionesRol.controller.js b/Roles/Controladores/obtenerOpcionesRol.controller.js new file mode 100644 index 00000000..46705bbe --- /dev/null +++ b/Roles/Controladores/obtenerOpcionesRol.controller.js @@ -0,0 +1,28 @@ +const repositorio = require('@altertex/rol/repos/obtenerOpcionesRolRepositorio'); +const MENSAJES = require('@altertex/util/const/mensajesRoles'); + +/** + * Controlador para obtener la lista de permisos disponibles para asociar a un rol. + * + * Este endpoint se utiliza generalmente cuando se va a crear o editar un rol, + * y se necesita mostrar las opciones de permisos disponibles en el sistema. + * + * @async + * @function obtenerOpcionesRol + * @param {Express.Request} req - Objeto de solicitud HTTP (no se espera ningún parámetro específico). + * @param {Express.Response} res - Objeto de respuesta HTTP. + * @returns {Promise} Devuelve una respuesta JSON con el estado y la lista de permisos. + */ +exports.obtenerOpcionesRol = async (req, res) => { + try { + const resultado = await repositorio.obtenerPermisos(); + return res.status(200).json({ + mensaje: MENSAJES.PERMISOS_OBTENIDOS, + resultado, + }); + } catch { + return res.status(500).json({ + mensaje: MENSAJES.ERROR_OBTENIENDO_PERMISOS, + }); + } +}; diff --git a/Roles/Datos/Repositorios/obtenerOpcionesRolRepositorio.js b/Roles/Datos/Repositorios/obtenerOpcionesRolRepositorio.js new file mode 100644 index 00000000..74110abf --- /dev/null +++ b/Roles/Datos/Repositorios/obtenerOpcionesRolRepositorio.js @@ -0,0 +1,13 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); + +/** + * Obtiene la lista de permisos disponibles desde la base de datos. + * + * @async + * @function obtenerPermisos + * @returns {Promise>} Arreglo de objetos con los permisos disponibles. + */ +exports.obtenerPermisos = async () => { + const consulta = 'SELECT idPermiso AS id, nombre FROM permiso'; + return await correrQuery(consulta); +}; diff --git a/Roles/Datos/Repositorios/repositorioActualizarRol.js b/Roles/Datos/Repositorios/repositorioActualizarRol.js new file mode 100644 index 00000000..22e67561 --- /dev/null +++ b/Roles/Datos/Repositorios/repositorioActualizarRol.js @@ -0,0 +1,94 @@ +const MENSAJES = require('@altertex/util/const/mensajesRoles'); +const conexion = require('@altertex/util/bd/db'); + +/** + * Actualiza un rol existente, incluyendo su nombre, descripción y permisos asociados. + * + * @param {object} datosActualizarRol - Datos necesarios para actualizar el rol. + * @param {object} datosActualizarRol.datosRol - Contiene el nombre, descripción y permisos del rol. + * @param {number} datosActualizarRol.idRol - ID del rol a actualizar. + * @throws {Error} Si hay parámetros inválidos o errores en la base de datos. + */ +exports.actualizarRol = async (datosActualizarRol) => { + const datosRol = datosActualizarRol.datosRol; + const idRol = datosActualizarRol.idRol; + const permisos = datosRol.permisos || []; + + if (!datosActualizarRol || !idRol) { + throw new Error(MENSAJES.PARAMETROS_INVALIDOS.mensaje); + } + + const conexionBD = await conexion.getConnection(); + + try { + await conexionBD.beginTransaction(); + + // Check for duplicate name only if name is being updated + if (datosRol.nombre !== null && datosRol.nombre !== undefined) { + const resultadoNombreDuplicado = await conexionBD.query( + 'SELECT idRol FROM rol WHERE nombre = ? AND idRol != ?', + [datosRol.nombre, idRol], + ); + + if (resultadoNombreDuplicado[0].length > 0) { + throw new Error(MENSAJES.ROL_EXISTENTE); + } + } + + // Build dynamic UPDATE query based on which fields are provided + const camposActualizar = []; + const valoresActualizar = []; + + if (datosRol.nombre !== null && datosRol.nombre !== undefined) { + camposActualizar.push('nombre = ?'); + valoresActualizar.push(datosRol.nombre); + } + + if (datosRol.descripcion !== null && datosRol.descripcion !== undefined) { + camposActualizar.push('descripcion = ?'); + valoresActualizar.push(datosRol.descripcion); + } + + // Only run UPDATE if there are fields to update + if (camposActualizar.length > 0) { + const consultaActualizar = `UPDATE rol + SET ${camposActualizar.join(', ')} + WHERE idRol = ?`; + valoresActualizar.push(idRol); + + await conexionBD.query(consultaActualizar, valoresActualizar); + } + + // Always handle permissions (delete old ones) + await conexionBD.query( + 'DELETE FROM rol_permiso WHERE idRol = ?', + [idRol], + ); + + // Insert new permissions if any + if (permisos.length > 0) { + const valores = permisos.map(idPermiso => [idRol, idPermiso]); + const marcadores = permisos.map(() => '(?, ?)').join(', '); + const consultaInsertar = `INSERT INTO rol_permiso (idRol, idPermiso) + VALUES ${marcadores}`; + const valoresAplanados = valores.flat(); + + await conexionBD.query(consultaInsertar, valoresAplanados); + } + + await conexionBD.commit(); + + } catch (error) { + await conexionBD.rollback(); + console.error('Error actualizando rol:', error); + + if (error.message === MENSAJES.ROL_EXISTENTE + || error.message === MENSAJES.PARAMETROS_INVALIDOS.mensaje) { + throw new Error(error.message); + } else { + throw new Error('Ocurrio un error al actualizar rol.'); + } + } finally { + conexionBD.release(); + } +}; \ No newline at end of file diff --git a/Roles/Datos/Repositorios/repositorioCrearRol.js b/Roles/Datos/Repositorios/repositorioCrearRol.js new file mode 100644 index 00000000..c2734053 --- /dev/null +++ b/Roles/Datos/Repositorios/repositorioCrearRol.js @@ -0,0 +1,72 @@ +const db = require('@altertex/util/bd/db'); +const QUERY = require('@altertex/util/const/consultasRoles'); + +/** + * Verifica si un rol con el nombre especificado ya existe en la base de datos. + * + * @async + * @function verificarNombreRol + * @param {string} nombre - Nombre del rol a verificar. + * @returns {Promise} Retorna `true` si el rol existe, `false` en caso contrario. + */ +exports.verificarNombreRol = async (nombre) => { + const [rows] = await db.execute(QUERY.VERIFICAR_NOMBRE_ROL, [nombre]); + return rows.length > 0; +}; + +/** + * Verifica si un permiso con el ID especificado existe en la base de datos. + * + * @async + * @function verificarPermiso + * @param {number} idPermiso - ID del permiso a verificar. + * @returns {Promise} Retorna `true` si el permiso existe, `false` en caso contrario. + */ +exports.verificarPermiso = async (idPermiso) => { + const [rows] = await db.execute(QUERY.VERIFICAR_PERMISO, [idPermiso]); + return rows.length > 0; +}; + +/** + * Inserta un nuevo rol en la base de datos. + * + * @async + * @function crearRol + * @param {string} nombre - Nombre del nuevo rol. + * @param {string} descripcion - Descripción del nuevo rol. + * @returns {Promise} Retorna el resultado de la operación de inserción, incluyendo el ID del nuevo rol. + */ +exports.crearRol = async (nombre, descripcion) => { + const [resultado] = await db.execute(QUERY.INSERTAR_ROL, [nombre, descripcion]); + return resultado; +}; + +/** + * Asocia una lista de permisos a un rol específico en la base de datos. + * Realiza la operación en una transacción para asegurar la integridad de los datos. + * + * @async + * @function asociarPermisosARol + * @param {number} idRol - ID del rol al que se asociarán los permisos. + * @param {number[]} permisos - Lista de IDs de permisos a asociar. + * @throws {Error} Lanza un error si ocurre algún fallo en la transacción. + * @returns {Promise} + */ +exports.asociarPermisosARol = async (idRol, permisos) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + for (const idPermiso of permisos) { + await conexion.execute(QUERY.INSERTAR_ROL_PERMISO, [idRol, idPermiso]); + } + + await conexion.commit(); + } catch { + await conexion.rollback(); + throw new Error('Error asociando permisos al rol'); + } finally { + conexion.release(); + } +}; \ No newline at end of file diff --git a/Roles/Datos/Repositorios/repositorioDetalleRol.js b/Roles/Datos/Repositorios/repositorioDetalleRol.js new file mode 100644 index 00000000..b950287a --- /dev/null +++ b/Roles/Datos/Repositorios/repositorioDetalleRol.js @@ -0,0 +1,29 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_ROLES = require('@altertex/util/const/consultasRoles'); +/** + * RF8 - Leer detalle de un rol + * Documentación del requisito funcional: + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF8 + * + * @async + * @function obtenerDetalleRol + * @param {number} idRol - ID del rol que se desea consultar. + * @returns {Promise>} Arreglo de objetos con datos del rol y sus permisos asociados. + * + * @throws {Error} Si ocurre un error en la consulta a la base de datos. + */ +exports.obtenerDetalleRol = async (idRol) => { + const query = CONSULTAS_ROLES.OBTENER_DETALLE_ROL; + + try { + const resultado = await correrQuery(query, [idRol]); + + if (!resultado || resultado.length === 0) { + throw new Error('Rol no encontrado'); + } + + return resultado; + } catch { + throw new Error('Error al consultar el detalle del rol'); + } +}; \ No newline at end of file diff --git a/Roles/Datos/Repositorios/repositorioRoles.js b/Roles/Datos/Repositorios/repositorioRoles.js new file mode 100644 index 00000000..c406dd2b --- /dev/null +++ b/Roles/Datos/Repositorios/repositorioRoles.js @@ -0,0 +1,44 @@ +// Importa la función encargada de ejecutar consultas SQL hacia la base de datos. +const correrQuery = require('@altertex/util/ser/correrQuery'); + +// Importa el objeto que contiene las sentencias SQL relacionadas con la entidad de roles. +const CONSULTAS_ROLES = require('@altertex/util/const/consultasRoles'); + +/** + * RF7 - Consultar lista de roles + * Documentación del requisito funcional: + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF7 + * + * @async + * @function obtenerRoles + * @returns {Promise>} Retorna una lista de objetos que representan los roles obtenidos desde la base de datos. + * + * @throws {Error} Si no se encuentran resultados o si ocurre un error al ejecutar la consulta. + * + * @description + * Esta función ejecuta la consulta SQL para obtener la lista de roles desde la base de datos. + * En caso de que no se encuentren registros, arroja un error indicando que no hay roles registrados. + * Si ocurre un error en la ejecución de la consulta, se lanza un error genérico para manejo por el controlador. + */ +exports.obtenerRoles = async () => { + // Consulta SQL para obtener la lista completa de roles. + const query = CONSULTAS_ROLES.OBTENER_LISTA; + + try { + // Ejecuta la consulta SQL utilizando la función utilitaria. + const roles = await correrQuery(query); + + // Verifica si la respuesta está vacía. + if (!roles || roles.length === 0) { + throw new Error('No hay roles registrados'); + } + + // Retorna los resultados si existen. + return roles; + } catch { + // Imprime en consola el error para fines de depuración. + + // Lanza un nuevo error genérico para ser manejado por el controlador correspondiente. + throw new Error('Error al consultar roles'); + } +}; diff --git a/Roles/Datos/Repositorios/repositoriorEliminar.js b/Roles/Datos/Repositorios/repositoriorEliminar.js new file mode 100644 index 00000000..9da5d8c2 --- /dev/null +++ b/Roles/Datos/Repositorios/repositoriorEliminar.js @@ -0,0 +1,49 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS = require('@altertex/util/const/consultasRoles'); +const MENSAJES = require('@altertex/util/const/mensajesRoles'); + +// RF10 - Eliminar rol - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF10 + +/** + * Elimina uno o varios roles de la base de datos según los IDs proporcionados. + * + * @async + * @function eliminarRol + * @param {number[]} ids - Un arreglo de identificadores de roles a eliminar. + * @returns {Promise} - No retorna ningún valor si la operación es exitosa. + * @throws {Error} - Lanza un error si ocurre un fallo en la consulta o si no se eliminó ningún rol. + */ +exports.eliminarRol = async (ids) => { + try { + if (!Array.isArray(ids) || ids.length === 0) return; + + const placeholdersValidar = ids.map(() => '?').join(', '); + const queryValidar = CONSULTAS.VALIDAR_ROL_SIN_USUARIOS.replace('__IDS__', placeholdersValidar); + const resultadoValidacion = await correrQuery(queryValidar, ids); + + if (resultadoValidacion[0].cantidad > 0) { + throw new Error(MENSAJES.ELIMINAR_ROL_ERROR.mensaje_rol_asignado); + } + + const placeholders = ids.map(() => '?').join(', '); + const query = CONSULTAS.ELIMINAR_ROL.replace('__IDS__', placeholders); + const resultado = await correrQuery(query, ids); + + if (resultado.affectedRows === 0) { + throw new Error(MENSAJES.ELIMINAR_ROL_ERROR.mensaje_no_existe); + } + + return; + } catch (error) { + // Si el error es uno de los definidos (mensaje de MENSAJES), lo relanzamos tal cual + if ( + error.message === MENSAJES.ELIMINAR_ROL_ERROR.mensaje_rol_asignado + || error.message === MENSAJES.ELIMINAR_ROL_ERROR.mensaje_no_existe + ) { + throw error; + } + + // Si es un error técnico (como SQL), se reemplaza por el mensaje general + throw new Error(MENSAJES.ELIMINAR_ROL_ERROR.mensaje); + } +}; diff --git a/Roles/Rutas/RutasIndividuales/actualizarRol.routes.js b/Roles/Rutas/RutasIndividuales/actualizarRol.routes.js new file mode 100644 index 00000000..76fe2262 --- /dev/null +++ b/Roles/Rutas/RutasIndividuales/actualizarRol.routes.js @@ -0,0 +1,15 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/rol/ctrl/actualizarRol.controller'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const revisarPermisos = require('@altertex/util/inter/verificarPermisos'); + +ruteador.put(RUTAS.ROLES.ACTUALIZAR, validarYSanitizar, revisarApiKey(), autorizarToken, limitePeticionesDiarias, revisarPermisos(PERMISOS.ACTUALIZAR_ROL), controlador.actualizarRol); + +module.exports = ruteador; \ No newline at end of file diff --git a/Roles/Rutas/RutasIndividuales/consultarDetalleRol.routes.js b/Roles/Rutas/RutasIndividuales/consultarDetalleRol.routes.js new file mode 100644 index 00000000..027bbac2 --- /dev/null +++ b/Roles/Rutas/RutasIndividuales/consultarDetalleRol.routes.js @@ -0,0 +1,54 @@ +const express = require('express'); +const ruteador = express.Router(); + +const controlador = require('@altertex/rol/ctrl/consultarDetalleRol.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF## - Leer detalle de un rol - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF## + */ + +/** + * @swagger + * /api/roles/leer: + * get: + * summary: Obtener detalle de un rol + * description: Devuelve nombre, descripción, cantidad de usuarios y permisos de un rol específico. + * tags: [Roles] + * security: + * - ApiKeyAuth: [] + * parameters: + * - in: query + * name: idRol + * required: true + * schema: + * type: integer + * description: ID del rol a consultar. + * responses: + * 200: + * description: Detalle del rol obtenido exitosamente. + * 400: + * description: Parámetros inválidos. + * 404: + * description: Rol no encontrado. + * 500: + * description: Error interno del servidor. + */ +ruteador.get( + RUTAS.ROLES.LEER_ROL, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.LEER_ROL), + controlador.consultarDetalle, +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Roles/Rutas/RutasIndividuales/consultarLista.routes.js b/Roles/Rutas/RutasIndividuales/consultarLista.routes.js new file mode 100644 index 00000000..873bdf8d --- /dev/null +++ b/Roles/Rutas/RutasIndividuales/consultarLista.routes.js @@ -0,0 +1,62 @@ +// Importación del framework Express para crear rutas HTTP. +const express = require('express'); + +// Se crea una instancia del enrutador de Express para definir rutas específicas del módulo de roles. +const ruteador = express.Router(); + +// Importación del controlador que contiene la lógica para manejar la consulta de lista de roles. +const controlador = require('@altertex/rol/ctrl/consultarLista.controller'); + +// Importación de middlewares de seguridad: +// - revisarApiKey: valida la API Key. +// - autorizarToken: valida el token JWT del usuario. +// - verificarPermisos: verifica que el usuario tenga el permiso necesario para acceder al recurso. +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + + +// Importación de constantes que definen los permisos y las rutas de la aplicación. +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/roles/consultar-lista: + * post: + * summary: Consulta la lista de roles disponibles en el sistema. + * tags: + * - Roles + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Consulta exitosa. Devuelve la lista de roles. + * 204: + * description: No se encontraron roles registrados. + * 401: + * description: Token inválido o expirado. + * 403: + * description: Permisos insuficientes para acceder a esta ruta. + * 500: + * description: Error interno del servidor. + */ + +// Se define la ruta POST para consultar la lista de roles. +// Esta ruta está protegida por tres middlewares: +// 1. Validación de API Key. +// 2. Validación del token JWT del usuario. +// 3. Verificación de que el usuario tenga el permiso "CONSULTAR_ROLES". +ruteador.post( + RUTAS.ROLES.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_ROLES), + controlador.consultarLista +); + +// Exporta el enrutador configurado para que pueda ser usado por el enrutador principal de la aplicación. +module.exports = ruteador; diff --git a/Roles/Rutas/RutasIndividuales/crearRol.routes.js b/Roles/Rutas/RutasIndividuales/crearRol.routes.js new file mode 100644 index 00000000..850d00a4 --- /dev/null +++ b/Roles/Rutas/RutasIndividuales/crearRol.routes.js @@ -0,0 +1,126 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/rol/ctrl/crearRol.controller'); + +const RUTAS = require('@altertex/util/const/rutas'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +/** + * @swagger + * /api/roles/crear-rol: + * post: + * summary: Crear un nuevo rol en el sistema + * description: Crea un nuevo rol con sus permisos asociados. Solo usuarios autorizados pueden crear roles. + * tags: + * - Roles + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - nombre + * - descripcion + * - permisos + * properties: + * nombre: + * type: string + * description: Nombre del rol + * example: "Supervisor de Producción" + * descripcion: + * type: string + * description: Descripción detallada del rol + * example: "Supervisa las actividades de producción y gestiona el personal operativo" + * permisos: + * type: array + * description: Lista de IDs de permisos asignados al rol + * items: + * type: integer + * example: [1, 2, 3, 4] + * responses: + * 201: + * description: Rol creado exitosamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Rol creado exitosamente" + * rol: + * type: object + * properties: + * id: + * type: integer + * example: 1 + * nombre: + * type: string + * example: "Supervisor de Producción" + * descripcion: + * type: string + * example: "Supervisa las actividades de producción y gestiona el personal operativo" + * permisos: + * type: array + * items: + * type: integer + * example: [1, 2, 3, 4] + * 400: + * description: Error en los datos proporcionados + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Datos inválidos para crear el rol" + * 401: + * description: No autorizado + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No autorizado" + * 403: + * description: Forbidden - No tiene permisos suficientes + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No tiene permisos para crear roles" + * 500: + * description: Error interno del servidor + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al crear el rol" + */ + +ruteador.post( + RUTAS.ROLES.CREAR_ROL, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + controlador.crearRol +); + +module.exports = ruteador; diff --git a/Roles/Rutas/RutasIndividuales/eliminarRol.routes.js b/Roles/Rutas/RutasIndividuales/eliminarRol.routes.js new file mode 100644 index 00000000..4f830d63 --- /dev/null +++ b/Roles/Rutas/RutasIndividuales/eliminarRol.routes.js @@ -0,0 +1,78 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/rol/ctrl/eliminarRol.controller'); +const PERMISOS = require('@altertex/util/const/permisos'); + +const RUTAS = require('@altertex/util/const/rutas'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +// RF10 - Eliminar rol - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF10 + +/** + * @swagger + * /api/roles/eliminar: + * delete: + * tags: + * - Roles + * summary: Eliminar uno o varios roles + * description: | + * Elimina uno o varios roles del sistema según los IDs proporcionados. + * Requiere API Key, token JWT y permisos adecuados. + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idsRol: + * type: array + * items: + * type: integer + * example: [1, 2, 3] + * required: + * - idsRol + * responses: + * 200: + * description: Rol(es) eliminado(s) correctamente + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Se elimino el rol correctamente + * 400: + * description: Error en la solicitud o el rol no existe + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Ocurrió un error al eliminar rol + * 401: + * description: No autorizado - Token JWT faltante o inválido + * 403: + * description: Prohibido - Permiso insuficiente + * 500: + * description: Error inesperado del servidor + */ +ruteador.post( + RUTAS.ROLES.ELIMINAR_ROL, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + revisarPermisos(PERMISOS.ELIMINAR_ROL), + controlador.eliminarRol +); + +module.exports = ruteador; diff --git a/Roles/Rutas/RutasIndividuales/obtenerOpcionesRol.routes.js b/Roles/Rutas/RutasIndividuales/obtenerOpcionesRol.routes.js new file mode 100644 index 00000000..c3e8974c --- /dev/null +++ b/Roles/Rutas/RutasIndividuales/obtenerOpcionesRol.routes.js @@ -0,0 +1,33 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/rol/ctrl/obtenerOpcionesRol.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); + +/** + * @file obtenerOpcionesRol.routes.js + * @description Define la ruta para obtener las opciones de permisos disponibles al crear o editar un rol. + * + * Esta ruta aplica la verificación de API Key como medida de seguridad antes de ejecutar el controlador. + */ + +/** + * POST /obtener-opciones + * + * Obtiene los permisos disponibles para ser asignados a un rol. + * - Protegida con middleware que verifica que se incluya una API Key válida. + * + * @route {POST} /api/roles/obtener-opciones + * @middleware revisarApiKey + * @controller obtenerOpcionesRol + */ +ruteador.post( + '/obtener-opciones', + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + controlador.obtenerOpcionesRol, +); + +module.exports = ruteador; diff --git a/Roles/Rutas/indexRoles.routes.js b/Roles/Rutas/indexRoles.routes.js new file mode 100644 index 00000000..5f70fd94 --- /dev/null +++ b/Roles/Rutas/indexRoles.routes.js @@ -0,0 +1,52 @@ +/** + * @file indexRoles.routes.js + * @description + * Encargado de centralizar y registrar todas las rutas relacionadas con la entidad "Rol". + * Este archivo importa las rutas individuales (por funcionalidad) y las monta bajo un prefijo base común, + * facilitando así la organización modular de las rutas dentro del sistema. + */ + +// Importación del framework Express para la gestión de rutas HTTP. +const express = require('express'); + +// Se crea una nueva instancia del enrutador de Express para agrupar rutas del módulo de roles. +const ruteador = express.Router(); + +// Importación del archivo que contiene las rutas individuales para consultar la lista de roles. +const rutasConsultarLista = require('@altertex/rol/rutasInd/consultarLista.routes'); + +const rutasCrearRol = require('@altertex/rol/rutasInd/crearRol.routes'); + +const rutasObtenerOpcionesRol = require('@altertex/rol/rutasInd/obtenerOpcionesRol.routes'); + +const rutasEliminarRol = require('@altertex/rol/rutasInd/eliminarRol.routes'); + +const rutasConsultarDetalle = require('@altertex/rol/rutasInd/consultarDetalleRol.routes'); + +const rutasActualizarRol = require('@altertex/rol/rutasInd/actualizarRol.routes'); + +// Importación del archivo de constantes donde están definidas las rutas base del sistema. +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * Se monta el grupo de rutas relacionadas con roles bajo el prefijo definido. + * Por convención, este prefijo suele ser: /api/roles + * + * Ejemplo final de ruta expuesta: + * POST /api/roles/consultar-lista + */ +ruteador.use(RUTAS.ROLES.BASE, rutasConsultarLista); + +ruteador.use(RUTAS.ROLES.BASE, rutasCrearRol); + +ruteador.use(RUTAS.ROLES.BASE, rutasObtenerOpcionesRol); + +ruteador.use(RUTAS.ROLES.BASE, rutasEliminarRol); + +ruteador.use(RUTAS.ROLES.BASE, rutasConsultarDetalle); + +ruteador.use(RUTAS.ROLES.BASE, rutasActualizarRol); + + +// Exporta el enrutador para ser utilizado en el archivo principal de rutas de la aplicación (por ejemplo: app.js). +module.exports = ruteador; \ No newline at end of file diff --git a/SetsProductos/Controladores/actualizarSetsProductos.controller.js b/SetsProductos/Controladores/actualizarSetsProductos.controller.js new file mode 100644 index 00000000..ae6d0fcf --- /dev/null +++ b/SetsProductos/Controladores/actualizarSetsProductos.controller.js @@ -0,0 +1,53 @@ +const MENSAJES = require('@altertex/util/const/mensajesSetsProductos'); +const repositorio = require('@altertex/setspro/repos/repositorioActualizarSetsProductos'); +//RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44] + +/** + * Controlador HTTP para actualizar un set de productos. + * + * Valida que el cuerpo de la solicitud contenga los datos necesarios, + * y delega la lógica de actualización al repositorio correspondiente. + * + * Respuestas posibles: + * - 400 si faltan datos en el cuerpo de la solicitud. + * - 200 si el set se actualiza correctamente. + * - 500 si ocurre un error en el proceso de actualización. + * + * @async + * @function actualizarSetProductos + * @param {Express.Request} req - Objeto de solicitud HTTP de Express. + * @param {Express.Response} res - Objeto de respuesta HTTP de Express. + * @returns {Promise} La respuesta HTTP con el estado y mensaje correspondiente. + */ +exports.actualizarSetProductos = async (req, res) => { + const datosActualizacion = req.body; + const cliente = req.user.clienteSeleccionado; + + // Validación básica de datos requeridos + if ( + !datosActualizacion + || !datosActualizacion.nombre + || !datosActualizacion.productos + || !Array.isArray(datosActualizacion.productos) + ) { + return res.status(MENSAJES.FORMATO_INVALIDO_DATOS.codigo).json({ + mensaje: MENSAJES.FORMATO_INVALIDO_DATOS.mensaje, + detalles: 'Se requieren nombre y lista de productos', + }); + } + + try { + await repositorio.actualizarSetProductos(cliente, datosActualizacion); + + return res.status(MENSAJES.SET_ACTUALIZADO.codigo).json({ + mensaje: MENSAJES.SET_ACTUALIZADO.mensaje, + datos: datosActualizacion, + }); + } catch (error) { + console.error('Error al actualizar set de productos:', error); + + return res.status(MENSAJES.ERROR_ACTUALIZAR_SET.codigo).json({ + mensaje: error.message, + }); + } +}; diff --git a/SetsProductos/Controladores/consultarSetsProductos.controller.js b/SetsProductos/Controladores/consultarSetsProductos.controller.js new file mode 100644 index 00000000..26c4b7d0 --- /dev/null +++ b/SetsProductos/Controladores/consultarSetsProductos.controller.js @@ -0,0 +1,52 @@ +const repositorio = require('@altertex/setspro/repos/repositorioConsultarSetsProductos'); +const MENSAJES_SETS_PRODUCTOS = require('@altertex/util/const/mensajesSetsProductos'); + +/** + * Controlador para la consulta de la lista de sets de productos de un cliente. + * + * RF42 - Super Administrador, Cliente Consulta Lista de Sets de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF42 + * + * @async + * @function consultarLista + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {object} req.user - Datos del usuario autenticado. + * @param {number} req.user.clienteSeleccionado - ID del cliente seleccionado para la consulta. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 200 si la consulta es exitosa, junto con los datos de los sets de productos. + * - 204 y mensaje si no hay usuarios en la base de datos. + * - 500 si ocurre un error en el servidor. + * + * @throws {Error} Si ocurre un error inesperado durante la operación. + */ +exports.consultarLista = async (req, res) => { + const idCliente = parseInt(req.user.clienteSeleccionado); + + // Validación del ID de cliente + if (!idCliente) { + return res + .status(MENSAJES_SETS_PRODUCTOS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_SETS_PRODUCTOS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const resultados = await repositorio.obtenerSetsProductos(idCliente); + + if (!resultados || resultados.length === 0) { + return res + .status(MENSAJES_SETS_PRODUCTOS.SIN_RESULTADOS.codigo) + .json({ mensaje: MENSAJES_SETS_PRODUCTOS.SIN_RESULTADOS.mensaje }); + } + + return res.status(MENSAJES_SETS_PRODUCTOS.CONSULTA_EXITOSA.codigo).json({ + mensaje: MENSAJES_SETS_PRODUCTOS.CONSULTA_EXITOSA.mensaje, + setsProductos: resultados, + }); + } catch { + return res + .status(MENSAJES_SETS_PRODUCTOS.ERROR_CONSULTAR_SETS_PRODUCTOS.codigo) + .json({ mensaje: MENSAJES_SETS_PRODUCTOS.ERROR_CONSULTAR_SETS_PRODUCTOS.mensaje }); + } +}; diff --git a/SetsProductos/Controladores/crearSetsProductos.controller.js b/SetsProductos/Controladores/crearSetsProductos.controller.js new file mode 100644 index 00000000..96fc79a7 --- /dev/null +++ b/SetsProductos/Controladores/crearSetsProductos.controller.js @@ -0,0 +1,49 @@ +const MENSAJES_SETS_PRODUCTOS = require('@altertex/util/const/mensajesSetsProductos'); +const repositorio = require('@altertex/setspro/repos/repositorioCrearSetsProductos'); + +/** + * Controlador de Express que maneja la creación de un nuevo set de productos para un cliente. + * + * Valida: + * - Que se haya enviado el cuerpo de la solicitud con los datos requeridos. + * - Que se hayan proporcionado todos los campos necesarios. + * - Que exista un cliente seleccionado en el usuario autenticado. + * + * Llama al repositorio para realizar la lógica de negocio y persistencia, y responde + * con el estado adecuado dependiendo del resultado. + * + * @async + * @function crearSetsProductos + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {Express.Response} res - Objeto de respuesta de Express. + * + * @returns {Promise} Devuelve una respuesta HTTP con el estado correspondiente. + * + * @throws {500} En caso de error inesperado en la creación del set. + */ +exports.crearSetsProductos = async (req, res) => { + const datos = req.body.nuevoSetsProductos; + const cliente = req.user.clienteSeleccionado; + + if (!req.body.nuevoSetsProductos) { + return res.status(MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.mensaje }); + } + + + if (!datos.nombre || !datos.nombreVisible || !datos.descripcion || !datos.activo || !datos.idProductos) { + return res.status(MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.DATOS_INVALIDOS_ERROR.mensaje }); + } + + if (!cliente) { + return res.status(MENSAJES_SETS_PRODUCTOS.CLIENTE_NO_SELECCIONADO.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.CLIENTE_NO_SELECCIONADO.mensaje }); + } + + try { + await repositorio.crearSetsProductos(cliente, datos); + return res.status(MENSAJES_SETS_PRODUCTOS.SETS_PRODUCTOS_CREADO_EXITO.codigo).json({ mensaje: MENSAJES_SETS_PRODUCTOS.SETS_PRODUCTOS_CREADO_EXITO.mensaje }); + } catch (error) { + return res.status(500).json({ mensaje: error.message }); + } + + +}; \ No newline at end of file diff --git a/SetsProductos/Controladores/eliminarSetsProductos.controller.js b/SetsProductos/Controladores/eliminarSetsProductos.controller.js new file mode 100644 index 00000000..92316f69 --- /dev/null +++ b/SetsProductos/Controladores/eliminarSetsProductos.controller.js @@ -0,0 +1,48 @@ +const repositorio = require('@altertex/setspro/repos/repositorioEliminarSetsProductos'); +const MENSAJES_SETS_PRODUCTOS = require('@altertex/util/const/mensajesSetsProductos'); + +/** + * Controlador para eliminar uno o más set de productos. + * //RF[45] Elimina set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF45] + * + * @async + * @function eliminarSetProductos + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {number[]} req.body.idsSetProductos - Array de IDs numéricos de los sets de productos a eliminar. + * @param {object} res - Objeto de respuesta de Express. + * @returns {Promise} Respuesta HTTP con estado: + * - 200 si los sets de productos fueron eliminadas correctamente. + * - 404 si no se encontraron los sets de productos. + * - 500 si ocurre un error en el servidor. + * @throws {Error} Si ocurre un error durante la eliminación. + */ +exports.eliminarSetProductos = async (req, res) => { + try { + const idsSetsProductos = req.body.idsSetProductos; + + if (!Array.isArray(idsSetsProductos) || idsSetsProductos.length === 0) { + return res.status(MENSAJES_SETS_PRODUCTOS.SET_PRODUCTO_NO_ENCONTRADO.codigo).json({ + mensaje: MENSAJES_SETS_PRODUCTOS.SET_PRODUCTO_NO_ENCONTRADO.mensaje, + }); + } + + await Promise.all( + idsSetsProductos.map(async (idSetProducto) => { + const resultadoSetProducto = await repositorio.eliminarSetProducto(idSetProducto); + + if (resultadoSetProducto.affectedRows === 0) { + throw new Error(`Set de productos con ID ${idSetProducto} no encontrado`); + } + }) + ); + + return res.status(MENSAJES_SETS_PRODUCTOS.SET_PRODUCTOS_ELIMINADO.codigo).json({ + mensaje: MENSAJES_SETS_PRODUCTOS.SET_PRODUCTOS_ELIMINADO.mensaje, + }); + } catch { + return res.status(MENSAJES_SETS_PRODUCTOS.ERROR_ELIMINAR_SET_PRODUCTOS.codigo).json({ + mensaje: MENSAJES_SETS_PRODUCTOS.ERROR_ELIMINAR_SET_PRODUCTOS.mensaje, + }); + } +}; diff --git a/SetsProductos/Datos/Repositorios/repositorioActualizarSetsProductos.js b/SetsProductos/Datos/Repositorios/repositorioActualizarSetsProductos.js new file mode 100644 index 00000000..0e91e086 --- /dev/null +++ b/SetsProductos/Datos/Repositorios/repositorioActualizarSetsProductos.js @@ -0,0 +1,75 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES = require('@altertex/util/const/mensajesSetsProductos'); +const CONSULTAS = require('@altertex/util/const/consultasSetsProductos'); + +// RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44] + +// RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44] + +/** + * Actualiza un set de productos con su información general y productos asociados. + * + * - Verifica que el nuevo nombre no esté duplicado para el cliente. + * - Actualiza los campos básicos del set: nombre, descripción y estado activo. + * - Si se especifica un arreglo de productos: + * - Elimina las asociaciones que ya no existen. + * - Agrega las nuevas asociaciones. + * - Si el arreglo está vacío, elimina todas las asociaciones. + * + * @async + * @function actualizarSetProductos + * @param {number} idCliente - ID del cliente propietario del set. + * @param {object} datos - Objeto con los datos para actualizar el set. + * @param {number} datos.idSetProducto - ID del set de productos a actualizar. + * @param {string} datos.nombre - Nombre interno del set. + * @param {string} datos.descripcion - Descripción del set. + * @param {boolean} datos.activo - Estado activo o inactivo del set. + * @param {number[]} datos.productos - Lista de IDs de productos asociados al set. + * Si es un arreglo vacío, se eliminarán todas las asociaciones. + * @throws {Error} Si ocurre un error durante la actualización o si el nombre está duplicado. + */ +exports.actualizarSetProductos = async (idCliente, datos) => { + const { idSetProducto, nombre, descripcion, activo, productos } = datos; + + try { + + const duplicados = await correrQuery(CONSULTAS.CONSULTAR_NOMBRE_DUPLICADO, [ + idCliente, + idSetProducto, + nombre, + ]); + + if (duplicados.length > 0) { + throw new Error(MENSAJES.ERROR_NOMBRE_NORMAL_DUPLICADO.mensaje); + } + // 1. Actualizar info básica del set + await correrQuery(CONSULTAS.ACTUALIZAR_SET_INFO, [nombre, descripcion, activo, idSetProducto]); + + // 2. Manejar productos asociados al set + if (Array.isArray(productos)) { + if (productos.length > 0) { + // Verificar que los productos pertenezcan al cliente + const productosSTR = productos.join(', '); + const valores = productos + .map((idProducto) => `(${idSetProducto}, ${idProducto})`) + .join(', '); + + // Eliminar asociaciones actuales que no estén en la nueva lista + await correrQuery( + CONSULTAS.ELIMINAR_PRODUCTOS_DEL_SET.replace('__ID__', idSetProducto).replace( + '__PRODUCTOS__', + productosSTR + ) + ); + + // Agregar nuevos productos al set + await correrQuery(CONSULTAS.AGREGAR_PRODUCTOS_AL_SET.replace('__VALORES__', valores)); + } else { + // Eliminar todas las asociaciones si el array está vacío + await correrQuery(CONSULTAS.ELIMINAR_TODOS_PRODUCTOS_DEL_SET, [idSetProducto]); + } + } + } catch (error) { + throw new Error(error.message || MENSAJES.ERROR_ACTUALIZAR_SET_PRODUCTOS.mensaje); + } +}; diff --git a/SetsProductos/Datos/Repositorios/repositorioConsultarSetsProductos.js b/SetsProductos/Datos/Repositorios/repositorioConsultarSetsProductos.js new file mode 100644 index 00000000..fabff938 --- /dev/null +++ b/SetsProductos/Datos/Repositorios/repositorioConsultarSetsProductos.js @@ -0,0 +1,28 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_SETS_PRODUCTOS = require('@altertex/util/const/consultasSetsProductos'); + +/** + * Función para obtener los sets de productos de un cliente específico. + * + * RF42 - Super Administrador, Cliente Consulta Lista de Sets de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF42 + * + * @async + * @function obtenerSetsProductos + * @param {number} idCliente - ID del cliente cuyos sets de productos se desea obtener. + * + * @returns {Promise} Lista de sets de productos del cliente. + * - Si no se encuentran sets, se retorna un array vacío. + * + * @throws {Error} Si ocurre un error al ejecutar la consulta o si no se encuentran resultados. + */ +exports.obtenerSetsProductos = async (idCliente) => { + const query = CONSULTAS_SETS_PRODUCTOS.OBTENER_LISTA; + + try { + const setsProductos = await correrQuery(query, [idCliente]); + + return setsProductos; + } catch { + return []; + } +}; diff --git a/SetsProductos/Datos/Repositorios/repositorioCrearSetsProductos.js b/SetsProductos/Datos/Repositorios/repositorioCrearSetsProductos.js new file mode 100644 index 00000000..21623f34 --- /dev/null +++ b/SetsProductos/Datos/Repositorios/repositorioCrearSetsProductos.js @@ -0,0 +1,61 @@ +const MENSAJES_SETS_PRODUCTOS = require('@altertex/util/const/mensajesSetsProductos'); +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS = require('@altertex/util/const/consultasSetsProductos'); + +/** + * Crea un nuevo set de productos para un cliente específico, validando que: + * - El nombre y nombre visible del set no estén duplicados. + * - Todos los productos asociados existan. + * + * Si todo es válido, crea el set de productos y asigna los productos especificados al set. + * + * @async + * @function crearSetsProductos + * @param {number} idCliente - ID del cliente al que se le asignará el set de productos. + * @param {object} datosSetsProducto - Datos del set de productos a crear. + * @param {string} datosSetsProducto.nombre - Nombre interno del set. + * @param {string} datosSetsProducto.nombreVisible - Nombre visible del set. + * @param {string} [datosSetsProducto.descripcion] - Descripción del set (opcional). + * @param {boolean} datosSetsProducto.activo - Indicador de si el set estará activo. + * @param {number[]} datosSetsProducto.idProductos - IDs de los productos que se asociarán al set. + * + * @throws {Error} Si el nombre o nombre visible del set ya están en uso. + * @throws {Error} Si uno o más productos no existen en la base de datos. + * @throws {Error} Si ocurre un error inesperado durante la ejecución. + * + * @returns {Promise} No retorna un valor directamente, pero lanza errores si algo falla. + */ +exports.crearSetsProductos = async (idCliente, datosSetsProducto) => { + try { + + const duplicados = await correrQuery(CONSULTAS.CONSULTAR_DUPLICADOS, [ + idCliente, + datosSetsProducto.nombre, + datosSetsProducto.nombreVisible, + ]); + + if (duplicados.length > 0) { + throw new Error(MENSAJES_SETS_PRODUCTOS.ERROR_NOMBRE_DUPLICADO.mensaje); + } + + const ids = datosSetsProducto.idProductos; + const temporal = ids.map(() => '?').join(', '); + const queryProductos = CONSULTAS.CONSULTAR_PRODUCTOS_EXISTENTES.replace('__IDS__', temporal); + const productosExistentes = await correrQuery(queryProductos, ids); + + if (productosExistentes.length !== ids.length) { + throw new Error(MENSAJES_SETS_PRODUCTOS.ERROR_PRODUCTOS_INVALIDOS.mensaje); + } + + const resultado = await correrQuery(CONSULTAS.CREAR_SET_PRODUCTO, [idCliente, datosSetsProducto.nombre, datosSetsProducto.nombreVisible, datosSetsProducto.descripcion, datosSetsProducto.activo]); + const idSetProducto = resultado.insertId; + + + for (const producto of datosSetsProducto.idProductos) { + await correrQuery(CONSULTAS.ASIGNAR_PRODUCTO_SET_PRODUCTO, [producto, idSetProducto]); + } + } catch (error) { + throw new Error(error.message); + } + +}; \ No newline at end of file diff --git a/SetsProductos/Datos/Repositorios/repositorioEliminarSetsProductos.js b/SetsProductos/Datos/Repositorios/repositorioEliminarSetsProductos.js new file mode 100644 index 00000000..589d6cb8 --- /dev/null +++ b/SetsProductos/Datos/Repositorios/repositorioEliminarSetsProductos.js @@ -0,0 +1,53 @@ +const db = require('@altertex/util/bd/db'); +const CONSULTAS_SETS_PRODUCTOS = require('@altertex/util/const/consultasSetsProductos'); + +/** + * Elimina un set de productos de la base de datos. + * + * @async + * @function eliminarSetProducto + * @param {number} idSetProducto - ID del set de productos a eliminar. + * @returns {Promise} Objeto de resultado de la operación MySQL (por ejemplo, `affectedRows`). + * @throws {Error} Si ocurre un error durante la ejecución del query para eliminar el set de productos. + */ +exports.eliminarSetProducto = async (idSetProducto) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const [resultadoSetGrupo] = await conexion.query( + CONSULTAS_SETS_PRODUCTOS.ELIMINAR_SET_PRODUCTOS_GRUPO_EMPLEADOS, + [idSetProducto] + ); + + const [resultadoProductosSetProductos] = await conexion.query( + CONSULTAS_SETS_PRODUCTOS.ELIMINAR_PRODUCTOS_SET_PRODUCTOS, + [idSetProducto] + ); + + const [resultadoSetProductos] = await conexion.query( + CONSULTAS_SETS_PRODUCTOS.ELIMINAR_SET_PRODUCTOS, + [idSetProducto] + ); + + if (resultadoSetProductos.affectedRows === 0) { + throw new Error(`Set de productos con ID ${idSetProducto} no encontrado`); + } + + // Confirmar la transacción + await conexion.commit(); + + return { + mensaje: 'Set de productos eliminado correctamente', + resultadoSetGrupo, + resultadoProductosSetProductos, + resultadoSetProductos, + }; + } catch { + await conexion.rollback(); + throw new Error('Error eliminando set de productos'); + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/SetsProductos/Rutas/RutasIndividuales/actualizarSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/actualizarSetsProductos.routes.js new file mode 100644 index 00000000..d2f5892b --- /dev/null +++ b/SetsProductos/Rutas/RutasIndividuales/actualizarSetsProductos.routes.js @@ -0,0 +1,29 @@ +const express = require('express'); +const ruteador = express.Router(); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const controlador = require('@altertex/setspro/ctrl/actualizarSetsProductos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const revisarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +//RF[19] Actualizar Empleado - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF19] + +/** + * @swagger... + */ + +ruteador.put( + RUTAS.SETS_PRODUCTOS.ACTUALIZAR, + revisarApiKey(), + validarYSanitizar, + autorizarToken, + limitePeticionesDiarias, + revisarPermisos(PERMISOS.ACTUALIZAR_SET_PRODUCTOS), + controlador.actualizarSetProductos +); + +module.exports = ruteador; diff --git a/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js new file mode 100644 index 00000000..2e7299fb --- /dev/null +++ b/SetsProductos/Rutas/RutasIndividuales/consultarSetsProductos.routes.js @@ -0,0 +1,69 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/setspro/ctrl/consultarSetsProductos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * RF42 - Super Administrador, Cliente Consulta Lista de Sets de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF42 + */ + +/** + * @swagger + * /api/sets-productos/consultar-lista: + * post: + * summary: Consulta la lista de sets de productos disponibles. + * description: Permite a un Super Administrador o Cliente consultar la lista de sets de productos registrados. + * tags: + * - Sets de Productos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * responses: + * 200: + * description: Lista de sets de productos obtenida exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * setsProductos: + * type: array + * items: + * type: object + * properties: + * idSetProducto: + * type: string + * description: ID único del set de producto. + * nombre: + * type: string + * description: Nombre del set de producto. + * descripcion: + * type: string + * description: Descripción del set de producto. + * activo: + * type: integer + * description: Indica si el set de producto está activo (1) o inactivo (0). + * 401: + * description: No autorizado. El token es inválido o ha expirado. + * 403: + * description: No tiene permisos para consultar la lista de sets de productos. + * 500: + * description: Error interno del servidor. + */ + +ruteador.post( + RUTAS.SETS_PRODUCTOS.CONSULTAR_LISTA, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_SETS_PRODUCTOS), + controlador.consultarLista +); + +module.exports = ruteador; diff --git a/SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js new file mode 100644 index 00000000..22a05c6b --- /dev/null +++ b/SetsProductos/Rutas/RutasIndividuales/crearSetsProductos.routes.js @@ -0,0 +1,89 @@ +const express = require('express'); +const ruteador = express.Router(); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); + +const controlador = require('@altertex/setspro/ctrl/crearSetsProductos.controller'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/sets-productos/crear: + * post: + * summary: Crea un nuevo set de productos para un cliente autenticado. + * tags: + * - Sets de Productos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * nuevoSetsProductos: + * type: object + * required: + * - nombre + * - nombreVisible + * - descripcion + * - activo + * - idProductos + * properties: + * nombre: + * type: string + * example: "combo-verano" + * nombreVisible: + * type: string + * example: "Combo de Verano" + * descripcion: + * type: string + * example: "Incluye productos para la temporada de verano" + * activo: + * type: boolean + * example: true + * idProductos: + * type: array + * items: + * type: integer + * example: [1, 2, 3] + * responses: + * 201: + * description: Set de productos creado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Set de productos creado correctamente." + * 400: + * description: Datos inválidos o faltantes en la solicitud. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Los datos enviados son inválidos." + * 401: + * description: Token de autenticación no válido o ausente. + * 403: + * description: Permisos insuficientes para crear sets de productos. + * 429: + * description: Límite diario de peticiones alcanzado. + * 500: + * description: Error interno del servidor al crear el set de productos. + */ +ruteador.post(RUTAS.SETS_PRODUCTOS.CREAR, validarYSanitizar, revisarApiKey(), autorizarToken, limitePeticionesDiarias, verificarPermisos(PERMISOS.CREAR_SET_PRODUCTOS), controlador.crearSetsProductos); + +module.exports = ruteador; \ No newline at end of file diff --git a/SetsProductos/Rutas/RutasIndividuales/eliminarSetsProductos.routes.js b/SetsProductos/Rutas/RutasIndividuales/eliminarSetsProductos.routes.js new file mode 100644 index 00000000..d3e9b14f --- /dev/null +++ b/SetsProductos/Rutas/RutasIndividuales/eliminarSetsProductos.routes.js @@ -0,0 +1,77 @@ +//RF[45] Elimina set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF45] +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/setspro/ctrl/eliminarSetsProductos.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/sets-productos/eliminar: + * delete: + * summary: Eliminar sets de productos. + * description: Elimina uno o varios sets de productos de la base de datos. Requiere autenticación y permisos específicos. + * tags: + * - Sets de Productos + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idsSet: + * type: array + * items: + * type: integer + * example: [101, 102, 103] + * responses: + * 200: + * description: Sets eliminados exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Sets eliminados correctamente. + * 404: + * description: Sets no encontrados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: No se encontraron los sets especificados. + * 500: + * description: Error interno al eliminar los sets. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * example: Error al eliminar los sets. + */ + +ruteador.post( + RUTAS.SETS_PRODUCTOS.ELIMINAR_SET_PRODUCTOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_SET_PRODUCTOS), + controlador.eliminarSetProductos +); + +module.exports = ruteador; diff --git a/SetsProductos/Rutas/indexSetsProductos.routes.js b/SetsProductos/Rutas/indexSetsProductos.routes.js new file mode 100644 index 00000000..b1f8e1c2 --- /dev/null +++ b/SetsProductos/Rutas/indexSetsProductos.routes.js @@ -0,0 +1,21 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutasConsultarSetsProductos = require('@altertex/setspro/rutasInd/consultarSetsProductos.routes'); +const rutasEliminarSetsProductos = require('@altertex/setspro/rutasInd/eliminarSetsProductos.routes'); +const rutasCrearSetsProductos = require('@altertex/setspro/rutasInd/crearSetsProductos.routes'); +const rutasActualizarSetsProductos = require('@altertex/setspro/rutasInd/actualizarSetsProductos.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +//RF42 - Super Administrador, Cliente Consulta Lista de Sets de Productos - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF42 +ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasConsultarSetsProductos); + +//RF[45] Elimina set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF45] +ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasEliminarSetsProductos); + +ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasCrearSetsProductos); + +//RF[44] Actualizar set de productos - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF44] +ruteador.use(RUTAS.SETS_PRODUCTOS.BASE, rutasActualizarSetsProductos); + +module.exports = ruteador; diff --git a/Usuarios/Controladores/consultarListaUsuarios.controller.js b/Usuarios/Controladores/consultarListaUsuarios.controller.js new file mode 100644 index 00000000..1a52225f --- /dev/null +++ b/Usuarios/Controladores/consultarListaUsuarios.controller.js @@ -0,0 +1,38 @@ +const repositorio = require('@altertex/usu/repos/repositorioConsultarListaUsuarios'); +const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios'); + +/** + * Controlador que maneja la consulta de la lista de usuarios. + * RF02 - Super Administrador Consulta Lista de Usuarios - + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF2 + * + * @function consultarListaUsuarios + * @async + * @param {object} req - Objeto de solicitud HTTP (Request). + * @param {object} res - Objeto de respuesta HTTP (Response). + * + * @returns {Response} Retorna una respuesta con: + * - Código 200 y lista de usuarios si se encuentran resultados. + * - Código 200 y mensaje si no hay usuarios en la base de datos. + * - Código 500 y mensaje de error si ocurre una falla en la consulta. + */ +exports.consultarListaUsuarios = async (req, res) => { + try { + const resultados = await repositorio.consultarListaUsuarios(); + + if (!resultados || resultados.length === 0) { + return res + .status(MENSAJES_USUARIOS.USUARIOS_NO_ENCONTRADOS.codigo) + .json({ mensaje: MENSAJES_USUARIOS.USUARIOS_NO_ENCONTRADOS.mensaje }); + } + + return res.status(MENSAJES_USUARIOS.LISTA_USUARIOS_OBTENIDA.codigo).json({ + mensaje: MENSAJES_USUARIOS.LISTA_USUARIOS_OBTENIDA.mensaje, + listaUsuarios: resultados, + }); + } catch { + return res + .status(MENSAJES_USUARIOS.ERROR_OBTENER_USUARIOS.codigo) + .json({ mensaje: MENSAJES_USUARIOS.ERROR_OBTENER_USUARIOS.mensaje }); + } +}; diff --git a/Usuarios/Controladores/crearUsuario.controller.js b/Usuarios/Controladores/crearUsuario.controller.js new file mode 100644 index 00000000..ddac86e4 --- /dev/null +++ b/Usuarios/Controladores/crearUsuario.controller.js @@ -0,0 +1,120 @@ +const repositorio = require('@altertex/usu/repos/repositorioCrearUsuario'); +const bcrypt = require('bcryptjs'); +const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios'); + +/** + * Controlador para crear un nuevo usuario. + * RF1 - Crear Usuario - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF1 + * @async + * @function crearUsuario + * @param {object} req - Objeto de solicitud de Express. + * @param {object} req.body - Cuerpo de la solicitud HTTP. + * @param {string} req.body.nombreCompleto - Nombre completo del usuario. + * @param {string} req.body.correoElectronico - Correo electrónico del usuario. + * @param {string} req.body.contrasenia - Contraseña proporcionada por el usuario (sin hashear). + * @param {string} req.body.numeroTelefono - Número de teléfono del usuario. + * @param {string} req.body.direccion - Dirección del usuario. + * @param {string} req.body.fechaNacimiento - Fecha de nacimiento en formato YYYY-MM-DD. + * @param {string} req.body.genero - Género del usuario. + * @param {boolean} req.body.estatus - Estatus activo/inactivo del usuario. + * @param {object} res - Objeto de respuesta de Express. + * + * @returns {Response} Respuesta HTTP con estado: + * - 201 si el usuario se creó correctamente. + * - 400 si faltan campos requeridos. + * - 401 si no se pudo crear el usuario. + * - 500 si ocurre un error en el servidor. + * + * @throws {Error} + * + */ +exports.crearUsuario = async (req, res) => { + const { + nombreCompleto, + correoElectronico, + contrasenia, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus, + idRol, + idCliente, + } = req.body; + + if ( + !nombreCompleto + || !correoElectronico + || !contrasenia + || !numeroTelefono + || !direccion + || !fechaNacimiento + || !genero + || estatus === undefined + || !idRol + || idCliente === undefined + || (Array.isArray(idCliente) && idCliente.length === 0) + ) { + return res.status(400).json({ mensaje: 'Faltan campos requeridos' }); + } + + const correoValido = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!correoValido.test(correoElectronico)) { + return res + .status(MENSAJES_USUARIOS.CORREO_INVALIDO.codigo) + .json({ mensaje: MENSAJES_USUARIOS.CORREO_INVALIDO.mensaje }); + } + + const tieneCaracterEspecial = /[!@#$%^&*(),.?":{}|<>]/; + const tieneMayuscula = /[A-Z]/; + if (contrasenia.length < 8) { + return res + .status(MENSAJES_USUARIOS.CONTRASENA_DEBIL.codigo) + .json({ mensaje: MENSAJES_USUARIOS.CONTRASENA_DEBIL.mensaje }); + } + + if (!tieneCaracterEspecial.test(contrasenia)) { + return res + .status(MENSAJES_USUARIOS.CONTRASENA_DEBIL.codigo) + .json({ mensaje: MENSAJES_USUARIOS.CONTRASENA_DEBIL.mensaje }); + } + + if (!tieneMayuscula.test(contrasenia)) { + return res.status(MENSAJES_USUARIOS.CONTRASENA_DEBIL.codigo).json({ + mensaje: 'La contraseña debe contener al menos una letra mayúscula.', + }); + } + + const telefonoValido = /^\d{10}$/; + if (!telefonoValido.test(numeroTelefono)) { + return res + .status(MENSAJES_USUARIOS.TELEFONO_INVALIDO.codigo) + .json({ mensaje: MENSAJES_USUARIOS.TELEFONO_INVALIDO.mensaje }); + } + + try { + const contraseniaEncriptada = await bcrypt.hash(contrasenia, 10); + + const resultado = await repositorio.crearUsuarioConAsociaciones( + nombreCompleto, + correoElectronico, + contraseniaEncriptada, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus, + idRol, + idCliente + ); + + return res.status(MENSAJES_USUARIOS.USUARIO_CREADO.codigo).json({ + mensaje: MENSAJES_USUARIOS.USUARIO_CREADO.mensaje, + idUsuario: resultado.idUsuario, + }); + } catch { + return res + .status(MENSAJES_USUARIOS.ERROR_CREAR_USUARIO.codigo) + .json({ mensaje: MENSAJES_USUARIOS.ERROR_CREAR_USUARIO.mensaje }); + } +}; diff --git a/Usuarios/Controladores/eliminarUsuario.controller.js b/Usuarios/Controladores/eliminarUsuario.controller.js new file mode 100644 index 00000000..7078f34a --- /dev/null +++ b/Usuarios/Controladores/eliminarUsuario.controller.js @@ -0,0 +1,94 @@ +const repositorio = require('@altertex/usu/repos/repositorioEliminarUsuario'); +const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios'); +const db = require('@altertex/util/bd/db'); +const { verificarCodigo2FA } = require('@altertex/util/ser/verificarCodigo2FA.servicio'); +const repoUsuariosProtegidos = require('@altertex/usu/repos/repositorioConsultarUsuariosProtegidos'); + +/** + * Controlador para eliminar uno o varios usuarios, con validación adicional si hay Superadmins involucrados. + * + * RF5 - Eliminar Usuario + * Documentación: https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf5/ + * + * @async + * @function eliminarUsuario + * @param {object} req - Objeto de solicitud HTTP. + * @param {object} res - Objeto de respuesta HTTP. + * @returns {Promise} Retorna una respuesta JSON con el resultado de la operación. + * + * @description + * Este controlador: + * 1. Recibe IDs de usuarios a eliminar. + * 2. Valida si hay Superadmins entre ellos. + * 3. Si los hay, valida que el usuario solicitante tenga permiso y pase la verificación 2FA. + * 4. Luego delega la eliminación al repositorio. + */ +exports.eliminarUsuario = async (req, res) => { + try { + + let idsUsuarios = req.body.ids; + const codigo2FA = req.body.codigo2FA; + const idSolicitante = req.user?.idUsuario; + + // Validación: Se requiere al menos un ID + if (!idsUsuarios || (Array.isArray(idsUsuarios) && idsUsuarios.length === 0)) { + return res.status(MENSAJES_USUARIOS.PARAMETROS_INVALIDOS.codigo).json({ + mensaje: MENSAJES_USUARIOS.PARAMETROS_INVALIDOS.mensaje, + }); + } + + if (!Array.isArray(idsUsuarios)) { + idsUsuarios = [idsUsuarios]; + } + + const idsNumericos = idsUsuarios.map(Number); + + const [usuariosObjetivo] = await db.query(` + SELECT u.idUsuario, r.nombre AS rol + FROM usuario u + JOIN usuario_rol ur ON u.idUsuario = ur.idUsuario + JOIN rol r ON ur.idRol = r.idRol + WHERE u.idUsuario IN (?) + `, [idsNumericos]); + + const contieneSuperadmins = usuariosObjetivo.some(usuar => usuar.rol === 'Super Administrador'); + + if (contieneSuperadmins) { + const [resultadoPermiso] = await db.query( + 'SELECT puedeEliminarSuperadmins FROM usuarios_2fa WHERE idUsuario = ? AND tiene2FA = true', + [idSolicitante] + ); + + const tienePermiso = resultadoPermiso?.[0]?.puedeEliminarSuperadmins; + + if (!tienePermiso) { + return res.status(403).json({ + mensaje: 'No tienes permiso para eliminar Superadmins.', + }); + } + + const esCodigoValido = await verificarCodigo2FA(idSolicitante, codigo2FA); + if (!esCodigoValido) { + return res.status(401).json({ + mensaje: 'Código 2FA inválido o expirado.', + }); + } + } + + const usuariosProtegidos = await repoUsuariosProtegidos.consultarUsuariosProtegidos(idsNumericos); + + if (usuariosProtegidos.length > 0) { + return res.status(403).json({ + mensaje: 'No puedes eliminar Super administradores protegidos del sistema.', + }); + } + + await repositorio.eliminarUsuarios(idsNumericos); + + return res.status(200).json({ mensaje: 'Usuarios eliminados correctamente' }); + } catch { + return res.status(MENSAJES_USUARIOS.ERROR_ELIMINAR_USUARIO.codigo).json({ + mensaje: MENSAJES_USUARIOS.ERROR_ELIMINAR_USUARIO.mensaje, + }); + } +}; \ No newline at end of file diff --git a/Usuarios/Controladores/leerUsuario.controller.js b/Usuarios/Controladores/leerUsuario.controller.js new file mode 100644 index 00000000..2fd2b690 --- /dev/null +++ b/Usuarios/Controladores/leerUsuario.controller.js @@ -0,0 +1,43 @@ +const repositorio = require('@altertex/usu/repos/repositorioLeerUsuario'); +const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios'); + +/** + * Lee los detalles de un usuario desde la base de datos utilizando su ID. + * + * Valida el parámetro `idUsuario` y obtiene la información del usuario a través del repositorio. + * Si el usuario no es encontrado o el parámetro es inválido, retorna un error. + * + * @param {Express.Request} req - La solicitud HTTP que contiene el `idUsuario` en el cuerpo. + * @param {Express.Response} res - La respuesta HTTP para enviar el resultado al cliente. + * @returns {Promise} Responde con el usuario encontrado o un mensaje de error. + * + * @see [RF03 Leer usuario](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF3) + */ +exports.leerUsuario = async (req, res) => { + const idUsuario = parseInt(req.body.idUsuario); + + if (isNaN(idUsuario)) { + return res + .status(MENSAJES_USUARIOS.PARAMETROS_INVALIDOS.codigo) + .json({ mensaje: MENSAJES_USUARIOS.PARAMETROS_INVALIDOS.mensaje }); + } + + try { + const usuario = await repositorio.obtenerUsuarioPorId(idUsuario); + + if (!usuario) { + return res + .status(MENSAJES_USUARIOS.USUARIO_NO_ENCONTRADO.codigo) + .json({ mensaje: MENSAJES_USUARIOS.USUARIO_NO_ENCONTRADO.mensaje }); + } + + return res.status(MENSAJES_USUARIOS.USUARIO_OBTENIDO.codigo).json({ + mensaje: MENSAJES_USUARIOS.USUARIO_OBTENIDO.mensaje, + usuario, + }); + } catch { + return res + .status(MENSAJES_USUARIOS.ERROR_OBTENER_USUARIO.codigo) + .json({ mensaje: MENSAJES_USUARIOS.ERROR_OBTENER_USUARIO.mensaje }); + } +}; \ No newline at end of file diff --git a/Usuarios/Datos/Repositorios/repositorioConsultarListaUsuarios.js b/Usuarios/Datos/Repositorios/repositorioConsultarListaUsuarios.js new file mode 100644 index 00000000..af6c8dca --- /dev/null +++ b/Usuarios/Datos/Repositorios/repositorioConsultarListaUsuarios.js @@ -0,0 +1,30 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios'); + +/** + * Consulta la lista de usuarios en la base de datos. + * RF02 - Super Administrador Consulta Lista de Usuarios - + * https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF2 + * + * @async + * @function consultarListaUsuarios + * + * @returns {Promise} Arreglo de objetos con los datos de los usuarios. + * Cada objeto puede incluir propiedades como `id`, `nombre`, `correo`, `rol`, etc. + * + * @throws {Error} Si ocurre un error durante la ejecución del query a la base de datos. + * + * @description + * Ejecuta una consulta SQL definida en `CONSULTAS_USUARIOS.OBTENER_LISTA` utilizando el + * servicio `correrQuery`. Se utiliza para obtener la lista completa de usuarios registrados. + */ +exports.consultarListaUsuarios = async () => { + const query = CONSULTAS_USUARIOS.OBTENER_LISTA; + + try { + const listaUsuarios = await correrQuery(query); + return listaUsuarios; + } catch { + throw new Error('Error consultando la lista de usuarios'); + } +}; diff --git a/Usuarios/Datos/Repositorios/repositorioConsultarUsuariosProtegidos.js b/Usuarios/Datos/Repositorios/repositorioConsultarUsuariosProtegidos.js new file mode 100644 index 00000000..7ba32d7d --- /dev/null +++ b/Usuarios/Datos/Repositorios/repositorioConsultarUsuariosProtegidos.js @@ -0,0 +1,20 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios'); + +/** + * Consulta si existen usuarios protegidos dentro de una lista de IDs. + * @param {number[]} idsUsuarios - Lista de IDs a verificar + * @returns {Promise} Lista de usuarios protegidos encontrados + */ +exports.consultarUsuariosProtegidos = async (idsUsuarios) => { + try { + const resultado = await correrQuery( + CONSULTAS_USUARIOS.CONSULTAR_USUARIOS_PROTEGIDOS, + [idsUsuarios] + ); + return resultado; + } catch (error) { + console.error('Error en consultarUsuariosProtegidos:', error); + throw new Error('Error consultando usuarios protegidos'); + } +}; \ No newline at end of file diff --git a/Usuarios/Datos/Repositorios/repositorioCrearUsuario.js b/Usuarios/Datos/Repositorios/repositorioCrearUsuario.js new file mode 100644 index 00000000..6abbf94d --- /dev/null +++ b/Usuarios/Datos/Repositorios/repositorioCrearUsuario.js @@ -0,0 +1,70 @@ +const db = require('@altertex/util/bd/db'); +const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios'); + +/** + * Crea un usuario y lo asocia a un rol y a uno o varios clientes en una transacción. + * + * @param {string} nombreCompleto + * @param {string} correoElectronico + * @param {string} contrasenia + * @param {string} numeroTelefono + * @param {string} direccion + * @param {string} fechaNacimiento + * @param {string} genero + * @param {boolean} estatus + * @param {number} idRol + * @param {number[]|number} idCliente + * @returns {Promise} Resultado con idUsuario + */ +exports.crearUsuarioConAsociaciones = async ( + nombreCompleto, + correoElectronico, + contrasenia, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus, + idRol, + idCliente +) => { + const conexion = await db.getConnection(); + + try { + await conexion.beginTransaction(); + + const valoresUsuario = [ + nombreCompleto, + correoElectronico, + contrasenia, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus + ]; + + // 1. Insertar usuario + const [resultadoUsuario] = await conexion.query(CONSULTAS_USUARIOS.CREAR_USUARIO, valoresUsuario); + const idUsuario = resultadoUsuario.insertId; + + // 2. Asociar rol + await conexion.query(CONSULTAS_USUARIOS.ASIGNAR_ROL_A_USUARIO, [idUsuario, idRol]); + + // 3. Asociar a clientes + const clientes = Array.isArray(idCliente) ? idCliente : [idCliente]; + + for (const clienteId of clientes) { + await conexion.query(CONSULTAS_USUARIOS.ASOCIAR_USUARIO_A_CLIENTE, [idUsuario, clienteId]); + } + + await conexion.commit(); + return { success: true, idUsuario }; + + } catch (error) { + if (conexion) await conexion.rollback(); + throw error; + } finally { + if (conexion) conexion.release(); + } +}; \ No newline at end of file diff --git a/Usuarios/Datos/Repositorios/repositorioEliminarUsuario.js b/Usuarios/Datos/Repositorios/repositorioEliminarUsuario.js new file mode 100644 index 00000000..c3b223e2 --- /dev/null +++ b/Usuarios/Datos/Repositorios/repositorioEliminarUsuario.js @@ -0,0 +1,68 @@ +// RF5 - Eliminar Usuario - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf5/ + +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios'); + +/** + * Elimina uno o varios usuarios de la base de datos junto con todas sus relaciones. + * @param {Array} usuarios - IDs de los usuarios a eliminar + * @returns {Promise} - `true` si la eliminación fue exitosa, `false` si no se encontraron usuarios. + */ +exports.eliminarUsuarios = async (usuarios) => { + try { + // 1. Obtener los IDs de empleados asociados a estos usuarios + const empleados = await correrQuery(CONSULTAS_USUARIOS.OBTENER_EMPLEADOS_POR_USUARIOS, [ + usuarios, + ]); + + if (empleados && empleados.length > 0) { + const idsEmpleados = empleados.map((empleado) => empleado.idEmpleado); + + // 2. Eliminar todas las relaciones de empleado en orden + + // 2.1 Eliminar cuota_set_grupo_empleado + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_CUOTA_SET_GRUPO_EMPLEADO, [idsEmpleados]); + + // 2.2 Eliminar empleado_evento + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_EMPLEADO_EVENTO, [idsEmpleados]); + + // 2.3 Eliminar empleado_grupo + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_EMPLEADO_GRUPO, [idsEmpleados]); + + // 2.4 Eliminar empleado_pedido + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_EMPLEADO_PEDIDO, [idsEmpleados]); + + // 2.5 Eliminar tipo_pago_empleado + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_TIPO_PAGO_EMPLEADO, [idsEmpleados]); + } + + // 3. Obtener los IDs de carritos asociados a estos usuarios + const carritos = await correrQuery(CONSULTAS_USUARIOS.OBTENER_CARRITOS_POR_USUARIOS, [ + usuarios, + ]); + + if (carritos && carritos.length > 0) { + const idsCarritos = carritos.map((carrito) => carrito.idCarrito); + + // 3.1 Eliminar registros relacionados en carrito_opcion + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_CARRITO_OPCION, [idsCarritos]); + + // 3.2 Eliminar registros del carrito + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_CARRITO_POR_USUARIOS, [usuarios]); + } + + // 4. Eliminar registros de usuario_rol y usuario_cliente + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_USUARIO_ROL, [usuarios]); + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_USUARIO_CLIENTE, [usuarios]); + + // 5. Ahora sí podemos eliminar los empleados + await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_EMPLEADO_POR_USUARIOS, [usuarios]); + + // 6. Finalmente, eliminar los usuarios + const resultado = await correrQuery(CONSULTAS_USUARIOS.ELIMINAR_USUARIOS_POR_IDS, [usuarios]); + + return resultado.affectedRows > 0; + } catch { + throw new Error('No se pudieron eliminar los usuarios. Consulta el log para más detalles.'); + } +}; diff --git a/Usuarios/Datos/Repositorios/repositorioLeerUsuario.js b/Usuarios/Datos/Repositorios/repositorioLeerUsuario.js new file mode 100644 index 00000000..c32a2a92 --- /dev/null +++ b/Usuarios/Datos/Repositorios/repositorioLeerUsuario.js @@ -0,0 +1,38 @@ +const correrQuery = require('@altertex/util/ser/correrQuery'); +const CONSULTAS_USUARIOS = require('@altertex/util/const/consultasUsuarios'); + +/** + * Obtiene un usuario desde la base de datos mediante su ID. + * + * Ejecuta una consulta SQL y retorna el primer usuario encontrado o `null` si no existe. + * + * @param {number|string} idUsuario - ID del usuario a buscar. + * @returns {Promise} El usuario encontrado o `null` si no existe. + * @throws {Error} Si ocurre un error al ejecutar la consulta. + * + * @see [RF03 Leer usuario](https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF3) + */ +exports.obtenerUsuarioPorId = async (idUsuario) => { + const query = CONSULTAS_USUARIOS.LEER_USUARIO; + const resultado = await correrQuery(query, [idUsuario]); + + if (resultado.length === 0) return null; + + const usuario = { + idUsuario: resultado[0].idUsuario, + nombreCompleto: resultado[0].nombreCompleto, + correoElectronico: resultado[0].correoElectronico, + numeroTelefono: resultado[0].numeroTelefono, + direccion: resultado[0].direccion, + fechaNacimiento: resultado[0].fechaNacimiento, + genero: resultado[0].genero, + estatus: resultado[0].estatus, + rol: resultado[0].rol, + clientes: resultado.map((row) => ({ + idCliente: row.idCliente, + nombreCliente: row.nombreCliente, + })), + }; + + return usuario; +}; \ No newline at end of file diff --git a/Usuarios/Rutas/RutasIndividuales/consultarListaUsuarios.routes.js b/Usuarios/Rutas/RutasIndividuales/consultarListaUsuarios.routes.js new file mode 100644 index 00000000..6f2f575f --- /dev/null +++ b/Usuarios/Rutas/RutasIndividuales/consultarListaUsuarios.routes.js @@ -0,0 +1,97 @@ +//RF02 Super Administrador Consulta Lista de Usuarios - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF2 + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/usu/ctrl/consultarListaUsuarios.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/usuarios/consultar-lista-usuarios: + * post: + * summary: Consulta la lista de usuarios del sistema. + * tags: [Usuarios] + * security: + * - ApiKeyAuth: [] + * responses: + * 200: + * description: Lista de usuarios obtenida exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Lista de usuarios obtenida exitosamente" + * usuarios: + * type: array + * items: + * type: object + * properties: + * idUsuario: + * type: integer + * example: 123 + * nombre: + * type: string + * example: "Juan Pérez" + * correo: + * type: string + * example: "juan.perez@example.com" + * telefono: + * type: string + * example: "5551234567" + * rol: + * type: string + * example: "Administrador" + * estatus: + * type: integer + * example: 1 + * 401: + * description: No autorizado - Token inválido o faltante. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No autorizado" + * 403: + * description: Prohibido - No tiene permisos para realizar esta acción. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No tiene permisos para consultar la lista de usuarios" + * 500: + * description: Error interno del servidor. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Error al obtener la lista de usuarios" + */ + +ruteador.post( + RUTAS.USUARIOS.CONSULTAR_LISTA_USUARIOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CONSULTAR_USUARIOS), + controlador.consultarListaUsuarios +); + +module.exports = ruteador; diff --git a/Usuarios/Rutas/RutasIndividuales/crearUsuario.routes.js b/Usuarios/Rutas/RutasIndividuales/crearUsuario.routes.js new file mode 100644 index 00000000..132ef0d0 --- /dev/null +++ b/Usuarios/Rutas/RutasIndividuales/crearUsuario.routes.js @@ -0,0 +1,123 @@ +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/usu/ctrl/crearUsuario.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +/** + * RF1 - Crear Usuario - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF1 + */ + +/** + * @swagger + * /api/usuarios/crear: + * post: + * summary: Crea un nuevo usuario en el sistema. + * tags: + * - Usuarios + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * required: + * - nombreCompleto + * - correoElectronico + * - contrasenia + * - numeroTelefono + * - direccion + * - fechaNacimiento + * - genero + * - estatus + * - idRol + * - idCliente + * properties: + * nombreCompleto: + * type: string + * example: Juan Pérez + * correoElectronico: + * type: string + * format: email + * example: juan.perez@example.com + * contrasenia: + * type: string + * example: Segura$123 + * numeroTelefono: + * type: string + * example: "5551234567" + * direccion: + * type: string + * example: Av. Central 123, CDMX + * fechaNacimiento: + * type: string + * format: date + * example: 1990-05-20 + * genero: + * type: string + * enum: [Hombre, Mujer, Otro] + * estatus: + * type: integer + * example: 1 + * idRol: + * type: integer + * example: 2 + * idCliente: + * type: integer + * example: 101 + * responses: + * 201: + * description: Usuario creado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Usuario creado correctamente. + * 400: + * description: Error de validación o entrada sospechosa. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Entrada sospechosa en el campo "correoElectronico". + * 401: + * description: No autorizado. + * 403: + * description: Permisos insuficientes. + * 500: + * description: Error interno del servidor. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Error al crear usuario. + */ + +ruteador.post( + RUTAS.USUARIOS.CREAR, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.CREAR_USUARIO), + controlador.crearUsuario +); + +module.exports = ruteador; diff --git a/Usuarios/Rutas/RutasIndividuales/eliminarUsuario.routes.js b/Usuarios/Rutas/RutasIndividuales/eliminarUsuario.routes.js new file mode 100644 index 00000000..bb37ec27 --- /dev/null +++ b/Usuarios/Rutas/RutasIndividuales/eliminarUsuario.routes.js @@ -0,0 +1,84 @@ +// RF5 - Eliminar Usuario - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf5/ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/usu/ctrl/eliminarUsuario.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +/** + * @swagger + * /api/usuarios/eliminar-usuarios: + * delete: + * summary: Eliminar usuarios + * description: Elimina uno o varios usuarios de la base de datos. Requiere autenticación y permisos específicos. + * tags: + * - Usuarios + * security: + * - ApiKeyAuth: [] + * - BearerAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * ids: + * oneOf: + * - type: integer + * example: 93 + * - type: array + * items: + * type: integer + * example: [1, 2, 3] + * responses: + * 200: + * description: Usuarios eliminados correctamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: Usuarios eliminados correctamente. + * 404: + * description: Usuarios no encontrados. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: + * mensaje: No se encontraron los usuarios especificados. + * 500: + * description: Error interno al eliminar los usuarios. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: + * mensaje: Error al eliminar los usuarios. + */ + +ruteador.post( + RUTAS.USUARIOS.ELIMINAR_USUARIOS, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.ELIMINAR_USUARIOS), + controlador.eliminarUsuario +); + +module.exports = ruteador; diff --git a/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js b/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js new file mode 100644 index 00000000..4485e114 --- /dev/null +++ b/Usuarios/Rutas/RutasIndividuales/leerUsuario.routes.js @@ -0,0 +1,122 @@ +/** + * RF[03] Leer usuario - [https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF3 + */ + +/** + * @swagger + * /api/usuarios/consultar-usuario: + * post: + * summary: Consulta la información de un usuario específico. + * tags: [Usuarios] + * security: + * - ApiKeyAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * idUsuario: + * type: integer + * example: 123 + * required: + * - idUsuario + * responses: + * 200: + * description: Usuario encontrado exitosamente. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Información del usuario obtenida exitosamente." + * usuario: + * type: object + * properties: + * idUsuario: + * type: integer + * example: 123 + * nombre: + * type: string + * example: "Juan Pérez" + * correo: + * type: string + * example: "juan.perez@example.com" + * telefono: + * type: string + * example: "5551234567" + * direccion: + * type: string + * example: "Calle Ejemplo 123" + * fechaNacimiento: + * type: string + * format: date + * example: "1990-01-01" + * genero: + * type: string + * example: "Masculino" + * rol: + * type: string + * example: "Administrador" + * clientes: + * type: array + * items: + * type: object + * properties: + * idCliente: + * type: integer + * example: 123 + * nombreCliente: + * type: string + * example: "Cliente ABC" + * estatus: + * type: integer + * example: 1 + * 404: + * description: Usuario no encontrado. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "No se encontró un usuario con el ID proporcionado." + * 400: + * description: Error interno del servidor al consultar el usuario. + * content: + * application/json: + * schema: + * type: object + * properties: + * mensaje: + * type: string + * example: "Ocurrió un error al obtener los datos del usuario." + */ + +const express = require('express'); +const ruteador = express.Router(); +const controlador = require('@altertex/usu/ctrl/leerUsuario.controller'); +const revisarApiKey = require('@altertex/util/inter/revisarApiKey'); +const autorizarToken = require('@altertex/util/inter/autorizarToken'); +const validarYSanitizar = require('@altertex/util/inter/validarYSanitizar'); +const verificarPermisos = require('@altertex/util/inter/verificarPermisos'); +const limitePeticionesDiarias = require('@altertex/util/inter/limitePeticiones'); + +const PERMISOS = require('@altertex/util/const/permisos'); +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.post( + RUTAS.USUARIOS.LEER, + validarYSanitizar, + revisarApiKey(), + autorizarToken, + limitePeticionesDiarias, + verificarPermisos(PERMISOS.LEER_USUARIO), + controlador.leerUsuario +); + +module.exports = ruteador; \ No newline at end of file diff --git a/Usuarios/Rutas/indexUsuarios.routes.js b/Usuarios/Rutas/indexUsuarios.routes.js new file mode 100644 index 00000000..5dc5ed19 --- /dev/null +++ b/Usuarios/Rutas/indexUsuarios.routes.js @@ -0,0 +1,15 @@ +const express = require('express'); +const ruteador = express.Router(); +const rutasCrearUsuario = require('@altertex/usu/rutasInd/crearUsuario.routes'); +const rutasLeerUsuario = require('@altertex/usu/rutasInd/leerUsuario.routes'); +const rutasConsultarListaUsuarios = require('@altertex/usu/rutasInd/consultarListaUsuarios.routes'); +const rutasEliminarUsuario = require('@altertex/usu/rutasInd/eliminarUsuario.routes'); + +const RUTAS = require('@altertex/util/const/rutas'); + +ruteador.use(RUTAS.USUARIOS.BASE, rutasConsultarListaUsuarios); +ruteador.use(RUTAS.USUARIOS.BASE, rutasCrearUsuario); +ruteador.use(RUTAS.USUARIOS.BASE, rutasLeerUsuario); +ruteador.use(RUTAS.USUARIOS.BASE, rutasEliminarUsuario); + +module.exports = ruteador; diff --git a/Utilidades/BaseDeDatos/db.js b/Utilidades/BaseDeDatos/db.js new file mode 100644 index 00000000..1ce0b300 --- /dev/null +++ b/Utilidades/BaseDeDatos/db.js @@ -0,0 +1,53 @@ +const mysql = require('mysql2/promise'); + +/** + * Establece un pool de conexiones con una base de datos MySQL utilizando las credenciales definidas + * en las variables de entorno. Incluye verificación inicial de conexión y soporte para UTF-8 multibyte. + * + * @module conexionMySQL + * @requires mysql2/promise + * + * @constant {object} pool - Pool de conexiones MySQL. + * @property {Function} query - Método para realizar consultas a la base de datos. + */ + +const pool = mysql.createPool({ + host: process.env.DB_HOST, + port: process.env.DB_PORT || 3306, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_NAME, // elimina espacios accidentales + charset: 'utf8mb4', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, + enableKeepAlive: true, + keepAliveInitialDelay: 0, // Iniciar keepAlive inmediatamente + // Aumentar timeouts para mayor estabilidad + connectTimeout: 60000, // 60 segundos para conectar +}); + +// Verificar conexión inicial una vez al arrancar +(async () => { + try { + const connection = await pool.getConnection(); + console.log(`Conectado a MySQL con id ${connection.threadId}`); + connection.release(); // libera la conexión al pool + } catch (error) { + console.error('Error conectandose a MySQL:', error.stack); + } +})(); + +// Configurar un ping periódico para mantener las conexiones vivas +const pingInterval = 30000; // 30 segundos +setInterval(async () => { + try { + const connection = await pool.getConnection(); + await connection.query('SELECT 1'); + connection.release(); + } catch (error) { + console.error('Error en ping a MySQL:', error.message); + } +}, pingInterval); + +module.exports = pool; \ No newline at end of file diff --git a/Utilidades/Constantes/consultasCategorias.js b/Utilidades/Constantes/consultasCategorias.js new file mode 100644 index 00000000..9d9f34c9 --- /dev/null +++ b/Utilidades/Constantes/consultasCategorias.js @@ -0,0 +1,77 @@ +module.exports = { + OBTENER_CATEGORIAS_CON_PRODUCTOS: ` + SELECT + c.idCategoria, + c.nombreCategoria, + c.descripcion, + COUNT(p.idProducto) AS cantidadProductos + FROM + categoria c + LEFT JOIN categoria_producto cp ON c.idCategoria = cp.idCategoria + LEFT JOIN producto p ON cp.idProducto = p.idProducto AND p.idCliente = ? + GROUP BY + c.idCategoria, c.nombreCategoria, c.descripcion; + `, + + CREAR_CATEGORIAS: ` + INSERT INTO categoria (nombreCategoria, descripcion) + VALUES (?, ?); + `, + + CREAR_CATEGORIA_PRODUCTOS: ` + INSERT INTO categoria_producto (idCategoria, idProducto) + VALUES (?, ?); + `, + + ELIMINAR_CATEGORIA_PRODUCTO: ` + DELETE + FROM categoria_producto + WHERE idCategoria = ?; + `, + + ELIMINAR_CATEGORIA: ` + DELETE + FROM categoria + WHERE idCategoria = ?; + `, + + CATEGORIA_EXISTENTE_POR_NOMBRE: ` + SELECT idCategoria + FROM categoria + WHERE nombreCategoria = ?; + `, + + PRODUCTOS_EXISTENTES_POR_IDS: ` + SELECT idProducto + FROM producto + WHERE idProducto IN (?); + `, + + LEER_DETALLE_CATEGORIA: ` + SELECT + c.idCategoria, + c.nombreCategoria, + c.descripcion, + p.idProducto, + p.nombreComun + FROM categoria c + LEFT JOIN categoria_producto cp ON c.idCategoria = cp.idCategoria + LEFT JOIN producto p ON cp.idProducto = p.idProducto + WHERE c.idCategoria = ?; + `, + + ACTUALIZAR_CATEGORIA: ` + UPDATE categoria + SET nombreCategoria = ?, descripcion = ? + WHERE idCategoria = ?; + `, + + ELIMINAR_PRODUCTOS_CATEGORIA: ` + DELETE FROM categoria_producto WHERE idCategoria = ?; + `, + + ASIGNAR_PRODUCTOS_A_CATEGORIA: ` + INSERT INTO categoria_producto (idCategoria, idProducto) + VALUES ?; + `, +}; diff --git a/Utilidades/Constantes/consultasClientes.js b/Utilidades/Constantes/consultasClientes.js new file mode 100644 index 00000000..7a13ea8e --- /dev/null +++ b/Utilidades/Constantes/consultasClientes.js @@ -0,0 +1,102 @@ +module.exports = { + OBTENER_CLIENTE: ` + SELECT * + FROM cliente + WHERE idCliente = ?; + `, + OBTENER_LISTA: ` + SELECT * + FROM cliente c + JOIN imagen_cliente ic ON c.idCliente = ic.idCliente + JOIN imagen i ON ic.idImagen = i.idImagen + WHERE i.tipoImagen LIKE 'Logo' + AND c.idCliente IN (?); + `, + ELIMINAR_CLIENTE: ` + DELETE FROM cliente + WHERE idCliente = ?; + `, + VERIFICAR_NOMBRE_COMERCIAL: ` + SELECT IF(EXISTS(SELECT nombreComercial FROM cliente WHERE nombreComercial = ?), 1, 0)`, + + VERIFICAR_NOMBRE_FISCAL: ` + SELECT IF(EXISTS(SELECT nombreFiscal FROM cliente WHERE nombreFiscal = ?), 1, 0)`, + + CREAR_CLIENTE: ` + INSERT INTO cliente (nombreComercial, nombreFiscal) + VALUES (?, ?)`, + + CREAR_IMAGEN_CLIENTE: ` + INSERT INTO imagen (urlImagen, tipoImagen, textoAlternativo) + VALUES (?, 'Logo', ?) + `, + VINCULAR_USUARIO_CLIENTE: ` + INSERT INTO usuario_cliente (idUsuario, idCliente) + SELECT idUsuario, ? + FROM usuario_rol + WHERE idRol = 1; + `, + + VINCULAR_IMAGEN_CLIENTE: ` + INSERT INTO imagen_cliente (idImagen, idCliente) + VALUES (?, ?) +`, + + LEER_CLIENTE: ` + SELECT + c.idCliente, + c.nombreComercial, + c.nombreFiscal, + ( + SELECT COUNT(*) + FROM empleado e + WHERE e.idCliente = c.idCliente + ) AS numeroEmpleados, + ( + SELECT COUNT(DISTINCT uc.idUsuario) + FROM usuario_cliente uc + WHERE uc.idCliente = c.idCliente + AND uc.idUsuario NOT IN ( + SELECT ur.idUsuario + FROM usuario_rol ur + JOIN rol r ON ur.idRol = r.idRol + WHERE r.nombre = 'Empleado' + ) + ) AS usuariosAsignados, + i.urlImagen + FROM + cliente c + LEFT JOIN + imagen_cliente ic ON c.idCliente = ic.idCliente + LEFT JOIN + imagen i ON ic.idImagen = i.idImagen AND i.tipoImagen = "Logo" + WHERE + c.idCliente = ?; +`, + + // QUERIES ACTUALIZAR + ACTUALIZAR_NOMBRE_FISCAL: ` + UPDATE cliente + SET nombreFiscal = ? + WHERE idCliente = ?; + `, + ACTUALIZAR_NOMBRE_COMERCIAL: ` + UPDATE cliente + SET nombreComercial = ? + WHERE idCliente = ?; + `, + ACTUALIZAR_AMBOS_NOMBRES: ` + UPDATE cliente + SET nombreComercial = ?, + nombreFiscal = ? + WHERE idCliente = ?; + `, + + // OBTENER EL NOMBRE DE LA IMAGEN + OBTENER_NOMBRE_IMAGEN: ` + SELECT i.urlImagen + FROM imagen i + JOIN imagen_cliente ic ON i.idImagen = ic.idImagen + WHERE ic.idCliente = ?; + `, +}; diff --git a/Utilidades/Constantes/consultasCuotas.js b/Utilidades/Constantes/consultasCuotas.js new file mode 100644 index 00000000..3308c3d1 --- /dev/null +++ b/Utilidades/Constantes/consultasCuotas.js @@ -0,0 +1,83 @@ +module.exports = { + INSERTAR_CUOTA: ` + INSERT INTO cuota_set (idCliente, nombre, descripcion, periodoRenovacion, renovacionHabilitada, ultimaActualizacion) + VALUES (?, ?, ?, ?, ?, ?)`, + + SELECCIONAR_PRODUCTO: `SELECT idProducto FROM producto WHERE descripcion = ? LIMIT 1`, + + INSERTAR_CUOTA_PRODUCTO: ` + INSERT INTO cuota_set_producto (idCuotaSet, idProducto, limite, limite_actual) + VALUES (?, ?, ?, ?)`, + + RESETEAR_LIMITES: ` + UPDATE cuota_set_producto + JOIN cuota_set ON cuota_set_producto.idCuotaSet = cuota_set.idCuotaSet + SET cuota_set_producto.limite_actual = cuota_set_producto.limite + WHERE DATE_ADD(cuota_set.ultimaActualizacion, INTERVAL cuota_set.periodoRenovacion MONTH) <= CURRENT_DATE() + AND renovacionHabilitada = true;`, + + ACTUALIZAR_FECHAS: ` + UPDATE cuota_set + SET ultimaActualizacion = CURRENT_DATE() + WHERE DATE_ADD(ultimaActualizacion, INTERVAL periodoRenovacion MONTH) <= CURRENT_DATE(); + `, + + OBTENER_OPCIONES: ` + SELECT idProducto as id, nombreComun as nombreProducto, tipoProducto as tipo + FROM producto + WHERE idCliente = ?; + `, + OBTENER_CUOTAS: ` + SELECT idCuotaSet, idCliente, nombre, periodoRenovacion, renovacionHabilitada + FROM cuota_set + WHERE idCliente = ?; + `, + + ELIMINAR_CUOTA_SET_PRODUCTO: ` + DELETE FROM cuota_set_producto WHERE idCuotaSet = ?; + `, + + ELIMINAR_CUOTA_SET: ` + DELETE FROM cuota_set WHERE idCuotaSet = ?; + `, + LEER_CUOTA_SET: ` + SELECT + cs.idCuotaSet, + cs.nombre, + cs.descripcion, + cs.periodoRenovacion, + cs.renovacionHabilitada, + cs.ultimaActualizacion + FROM cuota_set cs + WHERE cs.idCuotaSet = ?; + `, + + LEER_CUOTA_SET_PRODUCTOS: + `SELECT + p.idProducto, + p.nombreComun, + csp.limite AS cuota_valor, + csp.limite_actual + FROM cuota_set cs + JOIN cuota_set_producto csp + ON cs.idCuotaSet = csp.idCuotaSet + JOIN producto p + ON p.idProducto = csp.idProducto + WHERE cs.idCuotaSet = ?`, + + ACTUALIZAR_CUOTA_SET: ` + UPDATE cuota_set + SET nombre = ?, descripcion = ?, periodoRenovacion = ?, renovacionHabilitada = ?, ultimaActualizacion = ? + WHERE idCuotaSet = ?; + `, + + INSERTAR_CUOTA_PRODUCTO_ACTUALIZAR: ` + INSERT INTO cuota_set_producto (idCuotaSet, idProducto, limite, limite_actual) + VALUES (?, ?, ?, ?) + `, + + ELIMINAR_PRODUCTOS_CUOTA_SET: ` + DELETE FROM cuota_set_producto WHERE idCuotaSet = ?; + `, + +}; diff --git a/Utilidades/Constantes/consultasEmpleados.js b/Utilidades/Constantes/consultasEmpleados.js new file mode 100644 index 00000000..fa19ab29 --- /dev/null +++ b/Utilidades/Constantes/consultasEmpleados.js @@ -0,0 +1,81 @@ +module.exports = { + OBTENER_LISTA: ` + SELECT u.idUsuario, u.nombreCompleto, u.correoElectronico, e.* + FROM empleado e + JOIN usuario u ON e.idUsuario = u.idUsuario + WHERE e.idCliente = ?; + `, + OBTENER_ID_USUARIO_POR_EMPLEADO: ` + SELECT idUsuario FROM empleado WHERE idEmpleado = ?; + `, + ELIMINAR_EMPLEADO: ` + DELETE FROM empleado + WHERE idEmpleado = ?; + `, + INSERTAR_EMPLEADO: ` + INSERT INTO empleado ( + idUsuario, idCliente, numeroEmergencia, + areaTrabajo, posicion, cantidadPuntos, antiguedad + ) + VALUES (?, ?, ?, ?, ?, ?, ?) + `, + ACTUALIZAR: ` + UPDATE empleado SET + numeroEmergencia = ?, areaTrabajo = ?, posicion = ?, + cantidadPuntos = ?, antiguedad = ? WHERE idEmpleado = ?; + `, + OBTENER_DATOS_EXPORTACION: ` + SELECT + e.idEmpleado, + u.nombreCompleto, + u.correoElectronico, + u.numeroTelefono, + u.direccion, + u.fechaNacimiento, + u.genero, + CASE + WHEN u.estatus = 1 THEN 'Activo' + WHEN u.estatus = 0 THEN 'Inactivo' + ELSE 'Desconocido' + END AS estatus, + e.numeroEmergencia, + e.areaTrabajo, + e.posicion, + e.cantidadPuntos, + e.antiguedad + FROM empleado e + JOIN usuario u ON e.idUsuario = u.idUsuario + WHERE e.idCliente = ? AND e.idEmpleado IN (__IDS__); + `, + OBTENER_ULTIMO_ID_EMPLEADO: ` + SELECT idEmpleado FROM empleado ORDER BY idEmpleado DESC LIMIT 1; + `, + CONSULTAR_ID_VALIDO: ` + SELECT + CASE + WHEN NOT EXISTS (SELECT 1 FROM usuarios WHERE idUsuario = ?) + THEN 'No hay ningún usuario registrado bajo este ID' + WHEN EXISTS (SELECT 1 FROM empleado WHERE idUsuario = ?) + THEN 'Este usuario ya está registrado como empleado, revisa de nuevo el ID a usar' + ELSE 'OK' + END AS resultado; + `, + + INSERTAR_USUARIO: ` + INSERT INTO usuario + (nombreCompleto, correoElectronico, contrasenia, numeroTelefono, direccion, fechaNacimiento, genero, estatus) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + + `, + + INSERTAR_ROL: ` + INSERT INTO usuario_rol (idUsuario, idRol) + VALUES (?, ?) + `, + + INSERTAR_USUARIO_CLIENTE: ` + INSERT INTO usuario_cliente (idUsuario, idCliente) + VALUES (?, ?) + `, + +}; diff --git a/Utilidades/Constantes/consultasEventos.js b/Utilidades/Constantes/consultasEventos.js new file mode 100644 index 00000000..022c9eca --- /dev/null +++ b/Utilidades/Constantes/consultasEventos.js @@ -0,0 +1,39 @@ +module.exports = { + CREAR_EVENTO: ` + INSERT INTO evento (idCliente, nombre, descripcion, puntos, multiplicador, periodoRenovacion, renovacion) + VALUES (?, ?, ?, ?, ?, ?, ?); + `, + VERIFICAR_CLIENTE: ` + SELECT idCliente FROM cliente WHERE idCliente = ?; + `, + OBTENER_LISTA_EVENTOS: ` + SELECT + e.idEvento, + e.nombre, + e.descripcion, + e.puntos, + e.periodoRenovacion, + e.renovacion + FROM + evento e + WHERE e.idCliente = ? + `, + ELIMINAR_EMPLEADO_EVENTO: ` + DELETE FROM empleado_evento WHERE idEvento = ?; + `, + ELIMINAR_EVENTO: ` + DELETE FROM evento WHERE idEvento = ?; + `, + LEER_EVENTO: ` + SELECT + e.idEvento, + e.nombre, + e.descripcion, + e.puntos, + e.multiplicador, + e.periodoRenovacion, + e.renovacion + FROM evento e + WHERE e.idEvento = ?; + `, +}; diff --git a/Utilidades/Constantes/consultasGrupoEmpleados.js b/Utilidades/Constantes/consultasGrupoEmpleados.js new file mode 100644 index 00000000..ffdd2b33 --- /dev/null +++ b/Utilidades/Constantes/consultasGrupoEmpleados.js @@ -0,0 +1,145 @@ +module.exports = { + OBTENER_LISTA: ` + SELECT ge.idGrupo, + ge.nombre AS geNombre, + ge.descripcion, + COUNT(eg.idEmpleado) AS totalEmpleados + FROM grupo_empleado ge + LEFT JOIN empleado_grupo eg ON ge.idGrupo = eg.idGrupo + WHERE ge.idCliente = ? + GROUP BY ge.idGrupo; + `, + ELIMINAR_SET_PRODUCTO_GRUPO: ` + DELETE + FROM set_producto_grupo_empleado + WHERE idGrupo = ?; + `, + ELIMINAR_GRUPO: ` + DELETE + FROM grupo_empleado + WHERE idGrupo = ?; + `, + ELIMINAR_EMPLEADO_GRUPO: ` + DELETE + FROM empleado_grupo + WHERE idGrupo = ?; + `, + LEER_GRUPO: ` + SELECT ge.idGrupo, + ge.nombre AS nombre, + ge.descripcion AS descripcion, + + IFNULL(GROUP_CONCAT(DISTINCT sp.nombre SEPARATOR ', '), 'Sin sets de productos asociados') AS setsProductos, + IFNULL(GROUP_CONCAT(DISTINCT sp.idSetProducto SEPARATOR ','), '') AS idsSetProductos, + + IFNULL(GROUP_CONCAT(DISTINCT CONCAT( + u.nombreCompleto, ' | ', + u.correoElectronico, ' | ', + e.areaTrabajo + ) SEPARATOR ' || '), 'Sin empleados asociados') AS infoEmpleados, + + IFNULL(GROUP_CONCAT(DISTINCT e.idEmpleado SEPARATOR ','), '') AS idsEmpleados, + IFNULL( + JSON_ARRAYAGG( + JSON_OBJECT( + 'id', e.idEmpleado, + 'correo', u.correoElectronico, + 'nombre', u.nombreCompleto, + 'area', e.areaTrabajo + ) + ), + JSON_ARRAY() + ) AS empleadosActualizar, + IFNULL( + JSON_ARRAYAGG( + JSON_OBJECT( + 'id', sp.idSetProducto, + 'nombreProducto', sp.nombre, + 'activo', sp.activo + ) + ), + JSON_ARRAY() + ) AS setProductosActualizar + FROM grupo_empleado ge + LEFT JOIN empleado_grupo eg ON ge.idGrupo = eg.idGrupo + LEFT JOIN empleado e ON eg.idEmpleado = e.idEmpleado + LEFT JOIN usuario u ON e.idUsuario = u.idUsuario + LEFT JOIN set_producto_grupo_empleado spge ON ge.idGrupo = spge.idGrupo + LEFT JOIN set_producto sp ON spge.idSetProducto = sp.idSetProducto + WHERE ge.idGrupo = ? + GROUP BY ge.idGrupo + ORDER BY ge.idGrupo; + `, + ACTUALIZAR_GRUPO_EMPLEADOS_NOMBRE_DESCRIPCION: ` + UPDATE grupo_empleado + SET nombre = ?, + descripcion = ? + WHERE idGrupo = ? + AND (nombre != ? OR descripcion != ?); + `, + ELIMINAR_EMPLEADOS_DE_GRUPO_BASE: ` + DELETE + FROM empleado_grupo + WHERE idGrupo = __ID__ + AND idEmpleado NOT IN (__EMPLEADOS__); + `, + AGREGAR_EMPLEADOS_NUEVOS_BASE: ` + INSERT + IGNORE INTO empleado_grupo (idEmpleado, idGrupo) + VALUES __VALORES__; + `, + VERIFICAR_EMPLEADOS_CLIENTE: ` + SELECT COUNT(*) AS validos + FROM empleado e + JOIN grupo_empleado g ON g.idGrupo = ? + WHERE e.idEmpleado IN (__EMPLEADOS__) + AND e.idCliente = g.idCliente + `, + VERIFICAR_SETS_CLIENTE: ` + SELECT COUNT(*) AS validos + FROM set_producto s + JOIN grupo_empleado g ON g.idGrupo = ? + WHERE s.idSetProducto IN (__SETS__) + AND s.idCliente = g.idCliente + `, + + ELIMINAR_SETS_DE_GRUPO_BASE: ` + DELETE + FROM set_producto_grupo_empleado + WHERE idGrupo = __ID__ + AND idSetProducto NOT IN (__SETS__); + `, + + AGREGAR_SETS_NUEVOS_BASE: ` + INSERT + IGNORE INTO set_producto_grupo_empleado (idSetProducto, idGrupo) + VALUES __VALORES__; + `, + + VALIDAR_NOMBRE_REPETIDO: ` + SELECT 1 + FROM grupo_empleado + WHERE idCliente = ? + AND nombre = ? LIMIT 1 + `, + CREAR_GRUPO: ` + INSERT INTO grupo_empleado (idCliente, nombre, descripcion) + VALUES (?, ?, ?); + `, + ASIGNAR_EMPLEADO_A_GRUPO: ` + INSERT INTO empleado_grupo (idEmpleado, idGrupo) + VALUES (?, ?); + `, + + ELIMINAR_TODOS_EMPLEADOS_DE_GRUPO: ` + DELETE + FROM empleado_grupo + WHERE idGrupo = ?; + `, + + ELIMINAR_TODOS_SETS_DE_GRUPO: ` + DELETE + FROM set_producto_grupo_empleado + WHERE idGrupo = ?; + `, +}; \ No newline at end of file diff --git a/Utilidades/Constantes/consultasImagenes.js b/Utilidades/Constantes/consultasImagenes.js new file mode 100644 index 00000000..0c559a6f --- /dev/null +++ b/Utilidades/Constantes/consultasImagenes.js @@ -0,0 +1,6 @@ +module.exports = { + CREAR: ` + INSERT INTO imagen(urlImagen, tipoImagen, textoAlternativo) + VALUES (?, ?, ?); + `, +}; diff --git a/Utilidades/Constantes/consultasImportarEmpleados.js b/Utilidades/Constantes/consultasImportarEmpleados.js new file mode 100644 index 00000000..abaded26 --- /dev/null +++ b/Utilidades/Constantes/consultasImportarEmpleados.js @@ -0,0 +1,27 @@ +module.exports = { + VALIDAR_CORREOS_DUPLICADOS: ` + SELECT correoElectronico FROM usuario WHERE correoElectronico IN (?) + `, + VALIDAR_TELEFONO_DUPLICADO: ` + SELECT numeroTelefono FROM usuario WHERE numeroTelefono IN (?) + `, + INSERTAR_USUARIO_EN_VOLUMEN: ` + INSERT INTO usuario + (nombreCompleto, correoElectronico, contrasenia, numeroTelefono, direccion, fechaNacimiento, genero, estatus) + VALUES ? + `, + OBTENER_ID_GENERADOS: ` + SELECT idUsuario, correoElectronico FROM usuario WHERE correoElectronico IN (?) + `, + INSERTAR_ROLES_EN_VOLUMEN: ` + INSERT INTO usuario_rol (idUsuario, idRol) VALUES ? + `, + INSERTAR_USUARIO_CLIENTE_EN_VOLUMEN: ` + INSERT INTO usuario_cliente (idUsuario, idCliente) VALUES ? + `, + INSERTAR_EMPLEADOS_EN_VOLUMEN: ` + INSERT INTO empleado + (idUsuario, idCliente, numeroEmergencia, areaTrabajo, posicion, cantidadPuntos, antiguedad) + VALUES ? + `, +}; diff --git a/Utilidades/Constantes/consultasOpciones.js b/Utilidades/Constantes/consultasOpciones.js new file mode 100644 index 00000000..825ebfe9 --- /dev/null +++ b/Utilidades/Constantes/consultasOpciones.js @@ -0,0 +1,6 @@ +module.exports = { + CREAR: ` + INSERT INTO opcion (idVariante, cantidad, valorOpcion, SKUautomatico, SKUcomercial, costoAdicional, descuento, estado) + VALUES (?, ?, ?, ?, ?, ?, ?, ?); + `, +}; diff --git a/Utilidades/Constantes/consultasPagos.js b/Utilidades/Constantes/consultasPagos.js new file mode 100644 index 00000000..2a4b2f6e --- /dev/null +++ b/Utilidades/Constantes/consultasPagos.js @@ -0,0 +1,8 @@ +module.exports = { + CONSULTAR_LISTA: ` + SELECT idTipoPago,metodo, habilitado + FROM tipo_pago + WHERE idCliente = ?;`, + ACTUALIZAR: ` + UPDATE tipo_pago SET habilitado = ? WHERE (idTipoPago = ?)`, +}; diff --git a/Utilidades/Constantes/consultasPedidos.js b/Utilidades/Constantes/consultasPedidos.js new file mode 100644 index 00000000..1426a12d --- /dev/null +++ b/Utilidades/Constantes/consultasPedidos.js @@ -0,0 +1,49 @@ +module.exports = { + OBTENER_LISTA: ` + SELECT + p.idPedido, + u.nombreCompleto AS nombreEmpleado, + p.fechaOrden, + p.estado AS estatusPedido, + p.precioTotal, + pg.estatus AS estatusPago, + e.estado AS estatusEnvio + FROM + pedido p + JOIN + empleado_pedido ep ON p.idPedido = ep.idPedido + JOIN + empleado em ON ep.idEmpleado = em.idEmpleado AND em.idCliente = ? + JOIN + usuario u ON em.idUsuario = u.idUsuario + JOIN + pago pg ON p.idPago = pg.idPago + JOIN + envio e ON p.idEnvio = e.idEnvio + ORDER BY + p.idPedido;`, + + ELIMINAR_PEDIDO_OPCION: ` + DELETE FROM pedido_opcion + WHERE idPedido = ?;`, + + ELIMINAR_EMPLEADO_PEDIDO: ` + DELETE FROM empleado_pedido + WHERE idPedido = ?;`, + + ELIMINAR_PEDIDO: ` + DELETE FROM pedido + WHERE idPedido = ?;`, + + ACTUALIZAR_PEDIDO: ` + UPDATE pedido + JOIN pago ON pedido.idPago = pago.idPago + JOIN envio ON pedido.idEnvio = envio.idEnvio + SET + pedido.estado = ?, + pedido.precioTotal = ?, + pago.estatus = ?, + envio.estado = ? + WHERE pedido.idPedido = ?; + `, +}; diff --git a/Utilidades/Constantes/consultasProductos.js b/Utilidades/Constantes/consultasProductos.js new file mode 100644 index 00000000..b3526c61 --- /dev/null +++ b/Utilidades/Constantes/consultasProductos.js @@ -0,0 +1,125 @@ +module.exports = { + OBTENER_LISTA: ` + SELECT p.idProducto, p.nombreComun, p.precioVenta, p.estado, i.urlImagen + FROM producto p + LEFT JOIN imagen_producto ip ON p.idProducto = ip.idProducto + LEFT JOIN imagen i ON ip.idImagen = i.idImagen AND i.tipoImagen = "Imagen Producto" + WHERE p.idCliente = ?; + `, + CREAR: ` + INSERT INTO producto (idCliente, idProveedor, nombreComun, nombreComercial, descripcion, + marca, modelo, tipoProducto, precioPuntos, precioCliente, + precioVenta, costo, impuesto, descuento, estado, envio) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); + `, + CREAR_IMAGEN_PRODUCTO: ` + INSERT INTO imagen_producto (idImagen, idProducto) + VALUES (?, ?); + `, + CREAR_DATOS_ENVIO: ` + INSERT INTO datos_envio (idProducto, peso, longitud, ancho, altura, volumen, tipoPaquete) + VALUES (?, ?, ?, ?, ?, ?, ?); + `, + + ELIMINAR_PRODUCTOS: + "DELETE FROM producto WHERE idProducto IN (?)", + + OBTENER_IMAGENES_POR_IDS: ` + SELECT p.idProducto, i.urlImagen + FROM producto p + LEFT JOIN imagen_producto ip ON p.idProducto = ip.idProducto + LEFT JOIN imagen i ON ip.idImagen = i.idImagen + WHERE p.idProducto IN (?); + `, + + LEER_PRODUCTO: ` + SELECT JSON_OBJECT( + 'idProducto', p.idProducto, + 'idProveedor', p.idProveedor, + 'nombreComun', p.nombreComun, + 'nombreComercial', p.nombreComercial, + 'marca', p.marca, + 'modelo', p.modelo, + 'tipoProducto', p.tipoProducto, + 'precioPuntos', p.precioPuntos, + 'precioCliente', p.precioCliente, + 'precioVenta', p.precioVenta, + 'costo', p.costo, + 'impuesto', p.impuesto, + 'descuento', p.descuento, + 'estado', p.estado, + 'envio', p.envio, + 'nombreProveedor', pr.nombreCompania, + 'variantes', (SELECT JSON_ARRAYAGG( + JSON_OBJECT( + 'idVariante', v.idVariante, + 'nombreVariante', v.nombreVariante, + 'descripcion', v.descripcion, + 'opciones', (SELECT JSON_ARRAYAGG( + JSON_OBJECT( + 'cantidad', o.cantidad, + 'valorOpcion', + o.valorOpcion, + 'SKUautomatico', + o.SKUautomatico, + 'SKUcomercial', + o.SKUcomercial, + 'costoAdicional', + o.costoAdicional, + 'descuento', o.descuento, + 'estado', o.estado + ) + ) + FROM opcion o + WHERE o.idVariante = v.idVariante) + ) + ) + FROM variante v + WHERE v.idProducto = p.idProducto) + ) AS producto + FROM producto p + LEFT JOIN proveedor pr ON p.idProveedor = pr.idProveedor + WHERE p.idProducto = ? + AND p.idCliente = ?; + `, + OBTENER_DATOS_EXPORTACION: ` + SELECT + p.idProducto, + p.idProveedor, + p.nombreComun AS nombreProducto, + p.nombreComercial, + p.descripcion AS descripcionProducto, + p.tipoProducto, + p.marca, + p.modelo, + p.costo, + p.precioVenta, + p.precioCliente, + p.precioPuntos, + p.impuesto, + p.descuento, + p.estado, + p.envio, + GROUP_CONCAT( + CONCAT( + v.nombreVariante, '-', + v.descripcion, ',', + (SELECT GROUP_CONCAT( + CONCAT(o.valorOpcion, ':', o.SKUcomercial, ':', o.SKUautomatico, ':', o.cantidad) + SEPARATOR ', ' + ) + FROM opcion o + WHERE o.idVariante = v.idVariante + ) + ) + SEPARATOR ' | ' + ) AS variantes_opciones + FROM producto p + JOIN variante v ON v.idProducto = p.idProducto + WHERE p.idCliente = ? AND p.idProducto IN (__IDS__) + GROUP BY p.idProducto, p.idProveedor, p.nombreComun, p.nombreComercial, + p.descripcion, p.tipoProducto, p.marca, p.modelo, p.costo, + p.precioVenta, p.precioCliente, p.precioPuntos, p.impuesto, + p.descuento, p.estado, p.envio; + `, +}; diff --git a/Utilidades/Constantes/consultasProveedores.js b/Utilidades/Constantes/consultasProveedores.js new file mode 100644 index 00000000..95edd293 --- /dev/null +++ b/Utilidades/Constantes/consultasProveedores.js @@ -0,0 +1,14 @@ +module.exports = { + OBTENER_LISTA: ` + SELECT * FROM proveedor + WHERE idCliente = ?; + `, + CREAR: ` + INSERT INTO proveedor (idCliente, nombre, nombreCompania, telefonoContacto, direccion, codigoPostal, pais, estado) + VALUES (?, ?, ?, ?, ?, ?, ?, ?); + `, + VERIFICAR_EXISTE:` + SELECT idProveedor FROM proveedor WHERE idProveedor = ? LIMIT 1 + ` +} + diff --git a/Utilidades/Constantes/consultasRoles.js b/Utilidades/Constantes/consultasRoles.js new file mode 100644 index 00000000..3f6de048 --- /dev/null +++ b/Utilidades/Constantes/consultasRoles.js @@ -0,0 +1,91 @@ +/** + * @file consultasRoles.js + * @description + * Contiene las consultas SQL utilizadas en el backend para interactuar con la entidad "Rol". + * Estas consultas se utilizan principalmente en el repositorio de roles para realizar operaciones + * de lectura sobre la base de datos. + * + * @exports OBTENER_LISTA Consulta SQL que permite obtener todos los roles registrados, + * junto con la cantidad de usuarios asociados a cada uno. + */ + +module.exports = { + /** + * Consulta SQL para obtener la lista de roles del sistema. + * + * @constant + * @type {string} + * + * @returns {Object[]} Lista de roles con: + * - idRol: Identificador único del rol. + * - nombre: Nombre del rol. + * - descripcion: Descripción del rol. + * - totalUsuarios: Número de usuarios asociados al rol. + * + * @description + * Realiza un LEFT JOIN entre las tablas `Rol` y `Usuario_Rol` para contabilizar + * cuántos usuarios están relacionados con cada rol. + * Agrupa los resultados por `idRol` para consolidar la información por rol. + */ + OBTENER_LISTA: ` + SELECT r.idRol, r.nombre, r.descripcion, COUNT(ur.idUsuario) AS totalUsuarios + FROM rol r + LEFT JOIN usuario_rol ur ON r.idRol = ur.idRol + GROUP BY r.idRol; + `, + VERIFICAR_NOMBRE_ROL: ` + SELECT idRol + FROM rol + WHERE nombre = ? LIMIT 1`, + + VERIFICAR_PERMISO: ` + SELECT idPermiso + FROM permiso + WHERE idPermiso = ? LIMIT 1`, + + INSERTAR_ROL: ` + INSERT INTO rol (nombre, descripcion) + VALUES (?, ?)`, + + INSERTAR_ROL_PERMISO: ` + INSERT INTO rol_permiso (idRol, idPermiso) + VALUES (?, ?)`, + + OBTENER_PERMISOS_POR_CLIENTE: ` + SELECT idPermiso AS id, nombre + FROM permiso; + `, + + ELIMINAR_ROL: ` + DELETE + FROM rol + WHERE idRol IN (__IDS__); + `, + VALIDAR_ROL_SIN_USUARIOS: ` + SELECT COUNT(*) AS cantidad + FROM usuario_rol + WHERE idRol IN (__IDS__); + `, + OBTENER_DETALLE_ROL: ` + SELECT r.idRol, + r.nombre AS nombreRol, + r.descripcion AS descripcionRol, + (SELECT COUNT(*) + FROM usuario_rol ur + WHERE ur.idRol = r.idRol) AS totalUsuarios, + p.idPermiso, + p.nombre AS nombrePermiso, + p.descripcion AS descripcionPermiso + FROM rol r + LEFT JOIN rol_permiso rp ON r.idRol = rp.idRol + LEFT JOIN permiso p ON rp.idPermiso = p.idPermiso + WHERE r.idRol = ?; + `, + + VERIFICAR_NOMBRE_DUPLICADO_ROL: ` + select * + from rol + where nombre = ?; + `, + +}; \ No newline at end of file diff --git a/Utilidades/Constantes/consultasSetsProductos.js b/Utilidades/Constantes/consultasSetsProductos.js new file mode 100644 index 00000000..07a20c80 --- /dev/null +++ b/Utilidades/Constantes/consultasSetsProductos.js @@ -0,0 +1,78 @@ +module.exports = { + OBTENER_LISTA: ` + SELECT sp.idSetProducto, + sp.nombre, + sp.descripcion, + sp.activo, + GROUP_CONCAT(DISTINCT p.nombreComun SEPARATOR ', ') AS productos, + GROUP_CONCAT(DISTINCT ge.nombre SEPARATOR ', ') AS grupos, + GROUP_CONCAT(p.idProducto SEPARATOR ', ') AS idsProductos + FROM set_producto sp + LEFT JOIN producto_set_producto psp ON psp.idSetProducto = sp.idSetProducto + LEFT JOIN producto p ON p.idProducto = psp.idProducto + LEFT JOIN set_producto_grupo_empleado spge ON spge.idSetProducto = sp.idSetProducto + LEFT JOIN grupo_empleado ge ON ge.idGrupo = spge.idGrupo + WHERE sp.idCliente = ? + GROUP BY sp.idSetProducto, sp.nombre, sp.descripcion, sp.activo; + `, + ELIMINAR_SET_PRODUCTOS_GRUPO_EMPLEADOS: ` + DELETE + FROM set_producto_grupo_empleado + WHERE idSetProducto = ?; + `, + ELIMINAR_PRODUCTOS_SET_PRODUCTOS: ` + DELETE + FROM producto_set_producto + WHERE idSetProducto = ?; + `, + ELIMINAR_SET_PRODUCTOS: ` + DELETE + FROM set_producto + WHERE idSetProducto = ?; + `, + CREAR_SET_PRODUCTO: ` + INSERT INTO set_producto (idCliente, nombre, nombreVisible, descripcion, activo) + VALUES (?, ?, ?, ?, ?); + `, + ASIGNAR_PRODUCTO_SET_PRODUCTO: ` + INSERT INTO producto_set_producto (idProducto, idSetProducto) + VALUES (?, ?); + `, + CONSULTAR_DUPLICADOS: ` + SELECT idSetProducto + FROM set_producto + WHERE idCliente = ? + AND (nombre = ? OR nombreVisible = ?); + `, + CONSULTAR_NOMBRE_DUPLICADO: ` + SELECT idSetProducto + FROM set_producto + WHERE idCliente = ? and idSetProducto!= ? + AND (nombre = ?); + `, + CONSULTAR_PRODUCTOS_EXISTENTES: ` + SELECT idProducto + FROM producto + WHERE idProducto IN (__IDS__); + `, + ACTUALIZAR_SET_INFO: ` + UPDATE set_producto + SET nombre = ?, activo = ?, descripcion = ? + WHERE idSetProducto = ? + `, + + ELIMINAR_PRODUCTOS_DEL_SET: ` + DELETE FROM producto_set_producto + WHERE idSetProducto = __ID__ AND idProducto NOT IN (__PRODUCTOS__) + `, + + AGREGAR_PRODUCTOS_AL_SET: ` + INSERT IGNORE INTO producto_set_producto (idSetProducto, idProducto) + VALUES __VALORES__ + `, + + ELIMINAR_TODOS_PRODUCTOS_DEL_SET: ` + DELETE FROM producto_set_producto + WHERE idSetProducto = ? + `, +}; diff --git a/Utilidades/Constantes/consultasUsuarios.js b/Utilidades/Constantes/consultasUsuarios.js new file mode 100644 index 00000000..0f679e91 --- /dev/null +++ b/Utilidades/Constantes/consultasUsuarios.js @@ -0,0 +1,188 @@ +module.exports = { + OBTENER_USUARIO: ` + SELECT * + FROM usuario u + WHERE u.correoElectronico = ? + AND u.estatus = true; + `, + + OBTENER_CLIENTES_ASOCIADOS: ` + SELECT uc.idCliente + FROM usuario u + JOIN usuario_cliente uc ON u.idUsuario = uc.idUsuario + WHERE u.correoElectronico = ?; + `, + + OBTENER_PERMISOS: ` + SELECT p.nombre + FROM usuario u + JOIN usuario_rol ur ON ur.idUsuario = u.idUsuario + JOIN rol r ON ur.idRol = r.idRol + JOIN rol_permiso rp ON rp.idRol = r.idRol + JOIN permiso p ON rp.idPermiso = p.idPermiso + WHERE u.correoElectronico = ?; + `, + + CREAR_USUARIO: ` + INSERT INTO usuario (nombreCompleto, + correoElectronico, + contrasenia, + numeroTelefono, + direccion, + fechaNacimiento, + genero, + estatus) + VALUES (?, ?, ?, ?, ?, ?, ?, ?); + `, + + ASIGNAR_ROL_A_USUARIO: ` + INSERT INTO usuario_rol (idUsuario, idRol) + VALUES (?, ?); + `, + + ASOCIAR_USUARIO_A_CLIENTE: ` + INSERT INTO usuario_cliente (idUsuario, idCliente) + VALUES (?, ?); + `, + + LEER_USUARIO: ` + SELECT u.idUsuario, + u.nombreCompleto, + u.correoElectronico, + u.numeroTelefono, + u.direccion, + u.fechaNacimiento, + u.genero, + u.estatus, + r.nombre AS rol, + uc.idCliente, + c.nombreComercial AS nombreCliente + FROM usuario u + LEFT JOIN usuario_rol ur ON u.idUsuario = ur.idUsuario + LEFT JOIN rol r ON ur.idRol = r.idRol + LEFT JOIN usuario_cliente uc ON u.idUsuario = uc.idUsuario + LEFT JOIN cliente c ON uc.idCliente = c.idCliente + WHERE u.idUsuario = ?; + `, + + OBTENER_LISTA: ` + SELECT u.idUsuario, + u.nombreCompleto AS nombre, + r.nombre AS rol, + c.nombreComercial AS cliente, + u.estatus, + u.correoElectronico AS correo, + u.numeroTelefono AS telefono + FROM usuario u + LEFT JOIN usuario_rol ur ON u.idUsuario = ur.idUsuario + LEFT JOIN rol r ON ur.idRol = r.idRol + LEFT JOIN usuario_cliente uc ON u.idUsuario = uc.idUsuario + LEFT JOIN cliente c ON uc.idCliente = c.idCliente + WHERE u.idUsuario NOT IN (SELECT ur2.idUsuario + FROM usuario_rol ur2 + WHERE ur2.idRol = 3); + + `, + + ELIMINAR_USUARIOS: ` + DELETE + FROM usuario + WHERE idUsuario = (?); + `, + VALIDAR_CORREO: ` + SELECT idUsuario + FROM usuario + WHERE correoElectronico = ?; + `, + VALIDAR_TELEFONO: ` + SELECT idUsuario + FROM usuario + WHERE numeroTelefono = ?;`, + + OBTENER_EMPLEADOS_POR_USUARIOS: ` + SELECT idEmpleado + FROM empleado + WHERE idUsuario IN (?); + `, + + ELIMINAR_CUOTA_SET_GRUPO_EMPLEADO: ` + DELETE + FROM cuota_set_grupo_empleado + WHERE idEmpleado IN (?); + `, + + ELIMINAR_EMPLEADO_EVENTO: ` + DELETE + FROM empleado_evento + WHERE idEmpleado IN (?); + `, + + ELIMINAR_EMPLEADO_GRUPO: ` + DELETE + FROM empleado_grupo + WHERE idEmpleado IN (?); + `, + + ELIMINAR_EMPLEADO_PEDIDO: ` + DELETE + FROM empleado_pedido + WHERE idEmpleado IN (?); + `, + + ELIMINAR_TIPO_PAGO_EMPLEADO: ` + DELETE + FROM tipo_pago_empleado + WHERE idEmpleado IN (?); + `, + + OBTENER_CARRITOS_POR_USUARIOS: ` + SELECT idCarrito + FROM carrito + WHERE idUsuario IN (?); + `, + + ELIMINAR_CARRITO_OPCION: ` + DELETE + FROM carrito_opcion + WHERE idCarrito IN (?); + `, + + ELIMINAR_CARRITO_POR_USUARIOS: ` + DELETE + FROM carrito + WHERE idUsuario IN (?); + `, + + ELIMINAR_USUARIO_ROL: ` + DELETE + FROM usuario_rol + WHERE idUsuario IN (?); + `, + + ELIMINAR_USUARIO_CLIENTE: ` + DELETE + FROM usuario_cliente + WHERE idUsuario IN (?); + `, + + ELIMINAR_EMPLEADO_POR_USUARIOS: ` + DELETE + FROM empleado + WHERE idUsuario IN (?); + `, + + ELIMINAR_USUARIOS_POR_IDS: ` + DELETE + FROM usuario + WHERE idUsuario IN (?); + `, + + CONSULTAR_USUARIOS_PROTEGIDOS: ` + SELECT idUsuario + FROM usuarios_2fa + WHERE idUsuario IN (?) + AND puedeActivar2FA = true; + `, + + +}; diff --git a/Utilidades/Constantes/consultasVariantes.js b/Utilidades/Constantes/consultasVariantes.js new file mode 100644 index 00000000..b9ebc210 --- /dev/null +++ b/Utilidades/Constantes/consultasVariantes.js @@ -0,0 +1,10 @@ +module.exports = { + CREAR: ` + INSERT INTO variante (idProducto, nombreVariante, descripcion) + VALUES (?, ?, ?); + `, + CREAR_IMAGEN_VARIANTE: ` + INSERT INTO imagen_variante (idImagen, idVariante) + VALUES (?, ?); + `, +}; diff --git a/Utilidades/Constantes/mensajesAutenticacion.js b/Utilidades/Constantes/mensajesAutenticacion.js new file mode 100644 index 00000000..59ee6fc1 --- /dev/null +++ b/Utilidades/Constantes/mensajesAutenticacion.js @@ -0,0 +1,107 @@ +module.exports = { + // 200 - OK + INICIO_SESION_EXITOSO: { + codigo: 200, + mensaje: "Inicio de sesión exitoso.", + }, + CIERRE_SESION_EXITOSO: { + codigo: 200, + mensaje: "Sesión cerrada correctamente.", + }, + + // 201 - Created + REGISTRO_ACCESO_EXITOSO: { + codigo: 201, + mensaje: "Acceso registrado exitosamente.", + }, + + // 204 - No Content + SESION_NO_EXISTENTE: { + codigo: 204, + mensaje: "No había sesión activa para cerrar.", + }, + + // 400 - Bad Request + CAMPOS_OBLIGATORIOS: { + codigo: 400, + mensaje: "Se necesita ingresar correo y contraseña.", + }, + FORMATO_CORREO_INVALIDO: { + codigo: 400, + mensaje: "El formato del correo electrónico no es válido.", + }, + CONTRASENIA_NO_SEGURA: { + codigo: 400, + mensaje: "La contraseña no cumple con los criterios de seguridad.", + }, + TOKEN_NO_PROPORCIONADO: { + codigo: 400, + mensaje: "No se proporcionó un token de autenticación.", + }, + + // 401 - Unauthorized + CREDENCIALES_INVALIDAS: { + codigo: 401, + mensaje: "Usuario o contraseña incorrectos.", + }, + USUARIO_NO_AUTENTICADO: { + codigo: 401, + mensaje: "No se ha iniciado sesión.", + }, + TOKEN_INVALIDO: { + codigo: 401, + mensaje: "El token proporcionado no es válido.", + }, + TOKEN_EXPIRADO: { + codigo: 401, + mensaje: "La sesión ha expirado. Por favor, inicie sesión nuevamente.", + }, + TOKEN_CORRUPTO: { + codigo: 401, + mensaje: "El token está dañado o tiene un formato incorrecto.", + }, + API_KEY_INVALIDA: { + codigo: 401, + mensaje: "API Key inválida o no proporcionada.", + }, + + // 403 - Forbidden + ACCESO_NO_AUTORIZADO: { + codigo: 403, + mensaje: "No tiene permisos para acceder a este recurso.", + }, + + // 404 - Not Found + USUARIO_NO_ENCONTRADO: { + codigo: 404, + mensaje: "No se encontró un usuario con ese correo electrónico.", + }, + + // 409 - Conflict + SESION_YA_INICIADA: { + codigo: 409, + mensaje: "Ya hay una sesión iniciada en este dispositivo.", + }, + + // 500 - Internal Server Error + ERROR_SERVIDOR: { + codigo: 500, + mensaje: "Ocurrió un error inesperado. Intente de nuevo más tarde.", + }, + ERROR_OBTENER_USUARIO: { + codigo: 500, + mensaje: "Error al obtener datos del usuario.", + }, + ERROR_GENERAR_TOKEN: { + codigo: 500, + mensaje: "No se pudo generar el token de autenticación.", + }, + ERROR_CIERRE_SESION: { + codigo: 500, + mensaje: "Hubo un problema al cerrar la sesión.", + }, + ERROR_VALIDAR_TOKEN: { + codigo: 500, + mensaje: "Hubo un problema al validar el token.", + }, +}; diff --git a/Utilidades/Constantes/mensajesCategorias.js b/Utilidades/Constantes/mensajesCategorias.js new file mode 100644 index 00000000..3629a3f0 --- /dev/null +++ b/Utilidades/Constantes/mensajesCategorias.js @@ -0,0 +1,90 @@ +module.exports = { + // 201 - Creado + CATEGORIA_CREADA: { + codigo: 201, + mensaje: 'Categoría creada correctamente.', + }, + + // 200 - OK + CATEGORIA_OBTENIDA: { + codigo: 200, + mensaje: 'Información de la categoría obtenida exitosamente.', + }, + LISTA_CATEGORIAS_OBTENIDA: { + codigo: 200, + mensaje: 'Lista de categorías obtenida exitosamente.', + }, + CATEGORIA_ELIMINADA: { + codigo: 200, + mensaje: 'Categoría eliminada correctamente.', + }, + + // 204 - Sin contenido + CATEGORIAS_NO_ENCONTRADAS: { + codigo: 204, + mensaje: 'No se encontraron categorías registradas.', + }, + + // 400 - Bad Request + DATOS_INCOMPLETOS: { + codigo: 400, + mensaje: 'Faltan campos requeridos para crear la categoría.', + }, + NOMBRE_CATEGORIA_INVALIDO: { + codigo: 400, + mensaje: 'El nombre de la categoría es obligatorio.', + }, + CATEGORIA_YA_EXISTE: { + codigo: 400, + mensaje: 'Ya existe una categoría con ese nombre.', + }, + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos.', + }, + ERROR_OBTENER_CATEGORIAS: { + codigo: 400, + mensaje: 'Ocurrió un error al obtener la lista de categorías.', + }, + + // 401 - No autorizado + CREDENCIALES_INVALIDAS: { + codigo: 401, + mensaje: 'Credenciales inválidas para consultar categorías.', + }, + + // 403 - Acceso denegado + ACCESO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para realizar esta acción sobre categorías.', + }, + + // 404 - No encontrado + CATEGORIA_NO_ENCONTRADA: { + codigo: 404, + mensaje: 'No se encontró una categoría con el ID proporcionado.', + }, + + // 500 - Error del servidor + ERROR_CREAR_CATEGORIA: { + codigo: 400, + mensaje: 'Ocurrió un error al intentar crear la categoría.', + }, + ERROR_OBTENER_CATEGORIA: { + codigo: 400, + mensaje: 'Ocurrió un error al obtener los datos de la categoría.', + }, + ERROR_ELIMINAR_CATEGORIA: { + codigo: 400, + mensaje: 'Ocurrió un error al eliminar la categoría.', + }, + PRODUCTO_NO_EXISTE: { + codigo: 400, + mensaje: 'El producto no existe en la base de datos', + }, + DESCRIPCION_INVALIDA: { + codigo: 400, + mensaje: 'La descripción proporcionada no es válida.', + }, + +}; diff --git a/Utilidades/Constantes/mensajesClientes.js b/Utilidades/Constantes/mensajesClientes.js new file mode 100644 index 00000000..3e5ea4e5 --- /dev/null +++ b/Utilidades/Constantes/mensajesClientes.js @@ -0,0 +1,112 @@ +module.exports = { + // 200 - OK + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Información del cliente obtenida exitosamente.', + }, + CONSULTA_LISTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de clientes obtenida exitosamente.', + }, + CLIENTE_ELIMINADO: { + codigo: 200, + mensaje: 'Cliente eliminado exitosamente.', + }, + + // 204 - No Content + CLIENTE_SIN_SISTEMA: { + codigo: 204, + mensaje: 'El cliente no tiene un sistema registrado.', + }, + LISTA_CLIENTES_VACIA: { + codigo: 204, + mensaje: 'No hay clientes registrados actualmente.', + }, + + // 400 - Bad Request + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos o están incompletos.', + }, + FORMATO_ID_CLIENTE_INVALIDO: { + codigo: 400, + mensaje: 'El formato del ID del cliente debe ser un número entero válido.', + }, + LISTA_CLIENTES_INVALIDA: { + codigo: 400, + mensaje: 'La lista de clientes asociados es inválida o no contiene IDs numéricos válidos.', + }, + CLIENTES_ASOCIADOS_NO_PROPORCIONADOS: { + codigo: 400, + mensaje: 'No se proporcionó la lista de clientes asociados.', + }, + CLIENTE_INVALIDO: { + codigo: 400, + mensaje: 'El ID del cliente debe ser un número entero válido.', + }, + ERROR_ELIMINAR_CLIENTE: { + codigo: 400, + mensaje: 'Ocurrió un error al eliminar el cliente.', + }, + ERROR_CONSULTAR_CLIENTE: { + codigo: 400, + mensaje: 'Ocurrió un error al obtener la información del cliente.', + }, + + // 403 - Forbidden + ACCESO_NO_AUTORIZADO: { + codigo: 403, + mensaje: 'No tiene permiso para consultar la información de este cliente.', + }, + + // 404 - Not Found + CLIENTE_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró un cliente con el ID proporcionado.', + }, + SISTEMA_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró el sistema asociado al cliente.', + }, + + // 500 - Internal Server Error + ERROR_CONSULTAR_SISTEMA: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la información del sistema del cliente.', + }, + ERROR_CONSULTAR_LISTA_CLIENTES: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de clientes.', + }, + + + // Crear cliente + CAMPO_OBLIGATORIO: { + codigo: 400, + mensaje: 'Ingresa el nombre fiscal, nombre comercial y la imagen.', + }, + CLIENTE_COMERCIAL_EXISTENTE: { + codigo: 400, + mensaje: 'Ya existe un cliente con el mismo nombre comercial.', + }, + CLIENTE_FISCAL_EXISTENTE: { + codigo: 400, + mensaje: 'Ya existe un cliente con el mismo nombre legal.', + }, + CLIENTE_CREADO: { + codigo: 201, + mensaje: 'Cliente creado con éxito.', + }, + ERROR_CREACION: { + codigo: 500, + mensaje: 'Error al crear cliente.', + }, + CLIENTE_ACTUALIZADO: { + codigo: 200, + mensaje: 'Cliente actualizado correctamente.', + }, + ERROR_CLIENTE_ACTUALIZADO: { + codigo: 400, + mensaje: 'Error actualizando cliente.', + }, +}; diff --git a/Utilidades/Constantes/mensajesCuotas.js b/Utilidades/Constantes/mensajesCuotas.js new file mode 100644 index 00000000..9ac28661 --- /dev/null +++ b/Utilidades/Constantes/mensajesCuotas.js @@ -0,0 +1,72 @@ +// mensajesCuotas.js + +module.exports = { + // crearCuota + FORMATO_INVALIDO: 'Formato de cuota set inválido', + CREACION_EXITOSA: 'Cuota set creado exitosamente', + ERROR_CREACION: 'Error creando cuota set', + + // obtenerOpcionesCuotas + FALTA_ID_CLIENTE: 'No hay idCliente', + OPCIONES_OBTENIDAS: 'Opciones producto para cuota', + ERROR_OBTENIENDO_OPCIONES: 'Error obteniendo opciones', + + // validarCuotaSet + NOMBRE_REQUERIDO: 'El campo "nombre" es obligatorio.', + PRODUCTOS_REQUERIDOS: 'Debes enviar al menos un producto con su límite.', + ID_PRODUCTO_INVALIDO: (pos) => `El producto en la posición ${pos} no tiene un idProducto válido.`, + LIMITE_INVALIDO: (id) => `El producto "${id}" tiene un "limite" inválido.`, + LIMITE_ACTUAL_INVALIDO: (id) => `El producto "${id}" tiene un "limiteActual" inválido.`, + + // consultarListaCuotas + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de sets de cuotas obtenida exitosamente.', + }, + + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron sets de cuotas registrados para el cliente.', + }, + + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Falta el ID del cliente para realizar la consulta.', + }, + + ERROR_CONSULTAR_CUOTAS: { + codigo: 500, + mensaje: 'Error al consultar los sets de cuotas.', + }, + + SET_CUOTA_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'Set de cuotas no encontrado.', + }, + + SET_CUOTA_ELIMINADO: { + codigo: 200, + mensaje: 'Set de cuotas eliminado correctamente.', + }, + + ERROR_ELIMINAR_SET_CUOTAS: { + codigo: 500, + mensaje: 'Ocurrió un error al eliminar el set de productos.', + }, + + ERROR_OBTENER_SET_CUOTA: { + codigo: 500, + mensaje: 'Error interno al obtener el set de cuotas.', + }, + + ACTUALIZACION_EXITOSA: { + codigo: 200, + mensaje: 'Set de cuotas actualizado correctamente.' + }, + + ERROR_ACTUALIZACION: { + codigo: 500, + mensaje: 'No se pudo actualizar el set de cuotas.' + }, + +}; diff --git a/Utilidades/Constantes/mensajesEmpleados.js b/Utilidades/Constantes/mensajesEmpleados.js new file mode 100644 index 00000000..dcb3049b --- /dev/null +++ b/Utilidades/Constantes/mensajesEmpleados.js @@ -0,0 +1,108 @@ +module.exports = { + // 200 - OK + EXITO_CREAR: { + codigo: 200, + mensaje: 'Empleado agregado exitosamente.', + }, + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de empleados obtenida exitosamente.', + }, + EXITO_ACTUALIZAR: { + codigo: 200, + mensaje: 'Actualización exitosa.', + }, + + // 204 - No Content + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron empleados registrados para el cliente.', + }, + + // 400 - Bad Request + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos o están incompletos.', + }, + LIMITE_OFFSET_INVALIDOS: { + codigo: 400, + mensaje: 'Los valores de límite u offset deben ser números positivos.', + }, + ERROR_ACTUALIZAR: { + codigo: 400, + mensaje: 'Error al actualizar', + }, + ERROR_EXPORTAR_EMPLEADOS: { + codigo: 400, + mensaje: 'Error al exportar la lista de empleados.', + }, + ERROR_CREAR: { + codigo: 400, + mensaje: 'Error al crear', + }, + CAMPOS_REQUERIDOS: { + codigo: 400, + mensaje: 'Faltan campos requeridos', + }, + + // 403 - Forbidden + PERMISO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para consultar empleados de este cliente.', + }, + + // 500 - Internal Server Error + ERROR_CONSULTAR_EMPLEADOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de empleados.', + }, + // 404 - Not Found + EMPLEADO_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró el empleado especificado.', + }, + // 200 - OK + EMPLEADO_ELIMINADO: { + codigo: 200, + mensaje: 'Empleado(s) eliminado(s) correctamente.', + }, + LISTA_EMPLEADOS_EXPORTADA: { + codigo: 200, + mensaje: 'Lista de empleados exportada exitosamente.', + }, + // 204 - No hay datos + EMPLEADOS_NO_ENCONTRADOS: { + codigo: 204, + mensaje: 'No hay empleados para exportar.', + }, + // 500 - Internal Server Error + ERROR_ELIMINAR_EMPLEADO: { + codigo: 500, + mensaje: 'Error al eliminar los empleados.', + }, + // 201 - OK + GRUPO_CREADO: { + codigo: 201, + mensaje: 'Grupo de empleados creado correctamente.', + }, + + // 400 - Bad Request + DATOS_INCOMPLETOS: { + codigo: 400, + mensaje: 'Faltan datos requeridos: nombre del grupo o lista de empleados.', + }, + + // 500 - Internal Server Error + ERROR_CREAR_GRUPO: { + codigo: 500, + mensaje: 'Ocurrió un error al crear el grupo de empleados.', + }, + GRUPO_NOMBRE_REPETIDO: { + codigo: 'GRUPO_NOMBRE_REPETIDO', + mensaje: 'Ya existe un grupo con ese nombre.', + }, + ERROR_CREACION: { + codigo: 500, + mensaje: 'Error al crear el empleado', + }, +}; diff --git a/Utilidades/Constantes/mensajesEventos.js b/Utilidades/Constantes/mensajesEventos.js new file mode 100644 index 00000000..78590e3b --- /dev/null +++ b/Utilidades/Constantes/mensajesEventos.js @@ -0,0 +1,98 @@ +module.exports = { + // 201 - Creado + EVENTO_CREADO: { + codigo: 201, + mensaje: 'Evento creado correctamente.', + }, + + // 200 - OK + EVENTO_OBTENIDO: { + codigo: 200, + mensaje: 'Información del evento obtenida exitosamente.', + }, + LISTA_EVENTOS_OBTENIDA: { + codigo: 200, + mensaje: 'Lista de eventos obtenida exitosamente.', + }, + EVENTO_ELIMINADO: { + codigo: 200, + mensaje: 'Evento eliminado correctamente.', + }, + + // 204 - Sin contenido + CATEGORIAS_NO_ENCONTRADAS: { + codigo: 204, + mensaje: 'No se encontraron categorías registradas.', + }, + EVENTOS_NO_ENCONTRADOS: { + codigo: 204, + mensaje: 'No se encontraron eventos registrados.', + }, + + // 400 - Bad Request + DATOS_INCOMPLETOS: { + codigo: 400, + mensaje: 'Faltan campos requeridos para crear la categoría.', + }, + NOMBRE_CATEGORIA_INVALIDO: { + codigo: 400, + mensaje: 'El nombre de la categoría proporcionado no es válido.', + }, + CATEGORIA_YA_EXISTE: { + codigo: 400, + mensaje: 'Ya existe una categoría con ese nombre.', + }, + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos.', + }, + NOMBRE_EVENTO_INVALIDO: { + codigo: 400, + mensaje: 'El nombre del evento proporcionado no es válido.', + }, + EVENTO_YA_EXISTE: { + codigo: 400, + mensaje: 'Ya existe un evento con ese nombre.', + }, + // 400 - Error de cliente + ERROR_CLIENTE_NO_EXISTE: { + codigo: 400, + mensaje: 'El cliente especificado no existe en el sistema.', + }, + + // 404 - No encontrado + EVENTO_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró un evento con el ID proporcionado.', + }, + + // 500 - Error del servidor + ERROR_CREAR_EVENTO: { + codigo: 500, + mensaje: 'Ocurrió un error al intentar crear el evento.', + }, + ERROR_OBTENER_EVENTOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de eventos.', + }, + ERROR_OBTENER_EVENTO: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener los datos del evento.', + }, + ERROR_ELIMINAR_EVENTO: { + codigo: 500, + mensaje: 'Ocurrió un error al eliminar el evento.', + }, + ERROR_INTERNO: { + codigo: 500, + mensaje: 'Ocurrió un error interno en el servidor.', + }, + ERROR_DB_CONEXION: { + codigo: 500, + mensaje: 'Error de conexión con la base de datos.', + }, + ERROR_INESPERADO: { + codigo: 500, + mensaje: 'Ocurrió un error inesperado al crear el evento.', + }, +}; diff --git a/Utilidades/Constantes/mensajesGrupoEmpleados.js b/Utilidades/Constantes/mensajesGrupoEmpleados.js new file mode 100644 index 00000000..1e3dd0ef --- /dev/null +++ b/Utilidades/Constantes/mensajesGrupoEmpleados.js @@ -0,0 +1,74 @@ +module.exports = { + // 200 - OK + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de grupos de empleados obtenida exitosamente.', + }, + + GRUPO_OBTENIDO: { + codigo: 200, + mensaje: 'Información del grupo de empleados obtenida exitosamente.', + }, + + // 204 - No Content + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron grupos de empleados registrados para el cliente.', + }, + + // 400 - Bad Request + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos o están incompletos.', + }, + + GRUPO_NO_ENCONTRADO: { + codigo: 400, + mensaje: 'No se encontró un grupo con el ID proporcionado.', + }, + + // 403 - Forbidden + PERMISO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para consultar grupos de empleados de este cliente.', + }, + + // 500 - Internal Server Error + ERROR_CONSULTAR_GRUPOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de grupos de empleados.', + }, + + ELIMINAR_GRUPO_EXITOSO: { + codigo: 200, + mensaje: 'Grupo de empleados eliminado exitosamente.', + }, + ELIMINAR_GRUPO_ERROR: { + codigo: 500, + mensaje: 'Ocurrió un error al eliminar el grupo de empleados.', + }, + ERROR_OBTENER_GRUPO: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener los datos del grupo de empleados.', + }, + GRUPO_ACTUALIZADO: { + codigo: 200, + mensaje: 'Se actualizo correctamente el grupo de empleados.', + }, + FORMATO_INVALIDO_DATOS: { + codigo: 400, + mensaje: 'No se obtuvieron los datos correctamente.', + }, + ERROR_ACTUALIZAR_GRUPOS: { + codigo: 400, + mensaje: 'Error actualizando el grupo de empleados.s', + }, + ERROR_VERIFICACION_CLIENTE_EMPLEADO: { + codigo: 400, + mensaje: 'Algunos empleados no pertenecen al mismo cliente que el grupo.', + }, + ERROR_VERIFICACION_CLIENTE_SET: { + codigo: 400, + mensaje: 'Algunos sets de productos no pertenecen al cliente de este grupo.', + }, +}; diff --git a/Utilidades/Constantes/mensajesPagos.js b/Utilidades/Constantes/mensajesPagos.js new file mode 100644 index 00000000..f821cd64 --- /dev/null +++ b/Utilidades/Constantes/mensajesPagos.js @@ -0,0 +1,22 @@ +module.exports = { + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Consulta de tipos de pago exitosa', + }, + ERROR_CONSULTA: { + codigo: 400, + mensaje: 'Error al consultar pagos', + }, + ERROR_CLIENTE_SELECCIONADO: { + codigo: 400, + mensaje: 'No se ha seleccionado un cliente', + }, + EXITO_ACTUALIZAR: { + codigo: 200, + mensaje: 'Actualizacion exitosa', + }, + ERROR_ACTUALIZAR: { + codigo: 400, + mensaje: 'Error al actualizar', + }, +}; diff --git a/Utilidades/Constantes/mensajesPedidos.js b/Utilidades/Constantes/mensajesPedidos.js new file mode 100644 index 00000000..978727e6 --- /dev/null +++ b/Utilidades/Constantes/mensajesPedidos.js @@ -0,0 +1,44 @@ +module.exports = { + // 204 - Sin contenido + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron pedidos.', + }, + // 200 - OK + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de pedidos obtenida exitosamente.', + }, + PEDIDO_ELIMINADO: { + codigo: 200, + mensaje: 'Pedido eliminado correctamente.', + }, + // 403 - Acceso denegado + PERMISO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para consultar pedidos de este cliente.', + }, + // 404 - No encontrado + PEDIDO_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró el pedido solicitado.', + }, + // 500 - Error del servidor + ERROR_CONSULTAR_PEDIDOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de pedidos.', + }, + ERROR_ELIMINAR_PEDIDO: { + codigo: 500, + mensaje: 'Ocurrió un error al eliminar el pedido.', + }, + PEDIDO_ACTUALIZADO: { + codigo: 200, + mensaje: 'Pedido actualizado correctamente.', + }, + ERROR_ACTUALIZAR_PEDIDO: { + codigo: 400, + mensaje: 'Error al actualizar el pedido.', + }, + +}; diff --git a/Utilidades/Constantes/mensajesProductos.js b/Utilidades/Constantes/mensajesProductos.js new file mode 100644 index 00000000..7f713977 --- /dev/null +++ b/Utilidades/Constantes/mensajesProductos.js @@ -0,0 +1,103 @@ +module.exports = { + // 200 - OK + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de productos obtenida exitosamente.', + }, + PRODUCTO_CREADO_EXITOSAMENTE: { + codigo: 200, + mensaje: 'Producto creado correctamente.', + }, + + // 204 - No Content + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron productos registrados para el cliente.', + }, + PRODUCTOS_NO_ENCONTRADOS: { + codigo: 204, + mensaje: 'No se encontraron productos para exportar.', + }, + + // 400 - Bad Request + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos o están incompletos.', + }, + LIMITE_OFFSET_INVALIDOS: { + codigo: 400, + mensaje: 'Los valores de límite u offset deben ser números positivos.', + }, + + // 403 - Forbidden + PERMISO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para consultar productos de este cliente.', + }, + + // 500 - Internal Server Error + ERROR_CONSULTAR_PRODUCTOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de productos.', + }, + ERROR_CREAR_PRODUCTO: { + codigo: 500, + mensaje: 'Ocurrió un error al crear el producto. Por favor, intente nuevamente más tarde.', + }, + ERROR_ENVIAR_IMAGENES_S3: { + codigo: 500, + mensaje: 'Ocurrió un error al subir las imágenes al servidor. Intente nuevamente.', + }, + ERROR_CREAR_VARIANTE: { + codigo: 500, + mensaje: + 'Ocurrió un error al crear una variante del producto. Verifique los datos de variantes.', + }, + ERROR_CREAR_OPCION: { + codigo: 500, + mensaje: 'Ocurrió un error al crear una opción para la variante. Revise las opciones enviadas.', + }, + + ERROR_CREAR_IMAGEN_VARIANTE: { + codigo: 500, + mensaje: + 'Ocurrió un error al asociar la imagen con la variante. Verifique los datos de las imágenes de variantes.', + }, + + // 200 - OK + RESPUESTA_ELIMINAR_PRODUCTO_EXITOSA: { + codigo: 200, + mensaje: 'Producto eliminado exitosamente.', + }, + + // 500 - Internal Server Error + RESPUESTA_ERROR_GENERAL: { + codigo: 500, + mensaje: 'Ocurrió un error al procesar la solicitud.', + }, + //LEER PRODUCTO + ERROR_LEER_PRODUCTO: { + codigo: 400, + mensaje: 'Ocurrió un error al obtener la informacion del producto.', + }, + LEER_PRODUCTO_EXITO: { + codigo: 200, + mensaje: 'Lista de productos consultada exitosamente.', + }, + ID_INVALIDO: { + codigo: 400, + mensaje: 'No se proporciono el id del producto.', + }, + ERROR_OBTENIENDO_INFORMACION: { + codigo: 400, + mensaje: 'Error obteniendo informacion del producto.', + }, + PRODUCTO_NO_ENCONTRADO: { + codigo: 400, + mensaje: 'El producto solicitado no existe.', + }, + ERROR_EXPORTACION: { + codigo: 400, + mensaje: 'Error al exportar la lista de productos.', + }, +}; diff --git a/Utilidades/Constantes/mensajesProveedores.js b/Utilidades/Constantes/mensajesProveedores.js new file mode 100644 index 00000000..20d8f3fa --- /dev/null +++ b/Utilidades/Constantes/mensajesProveedores.js @@ -0,0 +1,53 @@ +module.exports = { + // 200 - OK + PROVEEDOR_CREADO_EXITOSAMENTE: { + codigo: 200, + mensaje: 'El proveedor fue creado exitosamente.', + }, + CONSULTA_PROVEEDORES_EXITOSA: { + codigo: 200, + mensaje: 'Lista de proveedores obtenida exitosamente.', + }, + PROVEEDOR_OBTENIDO_EXITOSAMENTE: { + codigo: 200, + mensaje: 'Información del proveedor obtenida exitosamente.', + }, + + // 204 - No Content + LISTA_PROVEEDORES_VACIA: { + codigo: 204, + mensaje: 'No hay proveedores registrados actualmente.', + }, + + // 400 - Bad Request + DATOS_PROVEEDOR_INVALIDOS: { + codigo: 400, + mensaje: 'Los datos del proveedor proporcionados son inválidos o están incompletos.', + }, + + // 403 - Forbidden + ACCESO_NO_AUTORIZADO_PROVEEDORES: { + codigo: 403, + mensaje: 'No tiene permiso para consultar o modificar proveedores.', + }, + + // 404 - Not Found + PROVEEDOR_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró un proveedor con el ID proporcionado.', + }, + + // 500 - Internal Server Error + ERROR_CREAR_PROVEEDOR: { + codigo: 500, + mensaje: 'Ocurrió un error al intentar crear el proveedor.', + }, + ERROR_CONSULTAR_PROVEEDORES: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de proveedores.', + }, + ERROR_CONSULTAR_PROVEEDOR: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la información del proveedor.', + }, +}; diff --git a/Utilidades/Constantes/mensajesRoles.js b/Utilidades/Constantes/mensajesRoles.js new file mode 100644 index 00000000..d8ab3f05 --- /dev/null +++ b/Utilidades/Constantes/mensajesRoles.js @@ -0,0 +1,97 @@ +/** + * @file mensajesRoles.js + * @description + * Contiene las constantes de mensajes utilizadas para las respuestas HTTP + * relacionadas con la entidad "Rol" en el backend. + * Cada constante define un código de estado HTTP y un mensaje descriptivo, + * lo cual facilita respuestas consistentes y claras desde los controladores. + * + * @exports CONSULTA_EXITOSA, SIN_RESULTADOS, PARAMETROS_INVALIDOS, + * LIMITE_OFFSET_INVALIDOS, ERROR_CONSULTAR_ROLES + */ + +module.exports = { + /** + * Mensaje utilizado cuando la consulta de roles se realiza con éxito. + * + * @constant + * @type {{codigo: number, mensaje: string}} + */ + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de roles obtenida exitosamente.', + }, + + /** + * Mensaje utilizado cuando la consulta no devuelve ningún resultado. + * + * @constant + * @type {{codigo: number, mensaje: string}} + */ + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron roles registrados para el cliente.', + }, + + /** + * Mensaje utilizado cuando los parámetros de entrada son inválidos o faltan. + * + * @constant + * @type {{codigo: number, mensaje: string}} + */ + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos o están incompletos.', + }, + + /** + * Mensaje específico para errores relacionados con paginación. + * Generalmente usado si el límite o el desplazamiento (offset) son inválidos. + * + * @constant + * @type {{codigo: number, mensaje: string}} + */ + LIMITE_OFFSET_INVALIDOS: { + codigo: 400, + mensaje: 'Los valores de límite u offset deben ser números positivos.', + }, + + /** + * Mensaje de error general utilizado cuando ocurre un fallo inesperado + * durante la consulta de roles en el backend. + * + * @constant + * @type {{codigo: number, mensaje: string}} + */ + ERROR_CONSULTAR_ROLES: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de roles.', + }, + + ELIMINAR_ROL_EXITO: { + codigo: 200, + mensaje: 'Se elimino el rol correctamente', + }, + ELIMINAR_ROL_ERROR: { + codigo: 400, + mensaje: 'Ocurrió un error al eliminar rol', + mensaje_no_existe: 'Ocurrió un error, rol no existe', + mensaje_rol_asignado: 'No se puede eliminar el rol porque está asignado a uno o más usuarios.', + + }, + ACTUALIZAR_ROL: { + codigo: 200, + mensaje: 'Se actualizo correctamente el rol.', + }, + + NOMBRE_OBLIGATORIO: 'El nombre del rol es obligatorio.', + PERMISOS_OBLIGATORIOS: 'Debes seleccionar al menos un permiso.', + ROL_EXISTENTE: 'Ya existe un rol con ese nombre.', + ROL_CREADO: 'Rol creado exitosamente.', + ERROR_CREACION: 'Error al crear el rol.', + PERMISO_INVALIDO: (id) => `El permiso con ID "${id}" no existe.`, + FALTA_ID_CLIENTE: 'Falta el ID del cliente', + OPCIONES_OBTENIDAS: 'Permisos obtenidos correctamente', + ERROR_OBTENIENDO_OPCIONES: 'Error al obtener permisos', + ERROR_OBTENIENDO_PERMISOS: 'Error al obtener la lista de permisos.', +}; diff --git a/Utilidades/Constantes/mensajesSetsProductos.js b/Utilidades/Constantes/mensajesSetsProductos.js new file mode 100644 index 00000000..3d8291c9 --- /dev/null +++ b/Utilidades/Constantes/mensajesSetsProductos.js @@ -0,0 +1,95 @@ +module.exports = { + // 200 - OK + CONSULTA_EXITOSA: { + codigo: 200, + mensaje: 'Lista de sets de productos obtenida exitosamente.', + }, + SET_PRODUCTOS_ELIMINADO: { + codigo: 200, + mensaje: 'Set de productos eliminado correctamente.', + }, + SET_PRODUCTOS_ACTUALIZADO: { + codigo: 200, + mensaje: 'Set de productos actualizado correctamente.', + }, + + // 204 - No Content + SIN_RESULTADOS: { + codigo: 204, + mensaje: 'No se encontraron sets de productos registrados para el cliente.', + }, + + // 403 - Forbidden + PERMISO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para consultar sets de productos de este cliente.', + }, + + // 404 - No encontrado + SET_PRODUCTO_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'Set de productos no encontrado.', + }, + + // 500 - Internal Server Error + ERROR_CONSULTAR_SETS_PRODUCTOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de sets de productos.', + }, + ERROR_ELIMINAR_SET_PRODUCTOS: { + codigo: 500, + mensaje: 'Ocurrió un error al eliminar el set de productos.', + }, + DATOS_INVALIDOS_ERROR: { + codigo: 400, + mensaje: 'Formato de datos invalido.', + }, + CLIENTE_NO_SELECCIONADO: { + codigo: 400, + mensaje: 'Cliente no seleccionado.', + }, + SETS_PRODUCTOS_CREADO_EXITO: { + codigo: 200, + mensaje: 'Set de producto creado exitosamente.', + }, + ERROR_NOMBRE_DUPLICADO: { + codigo: 400, + mensaje: 'Nombre o nombre visible duplicado.', + }, + ERROR_NOMBRE_NORMAL_DUPLICADO: { + codigo: 400, + mensaje: 'Nombre duplicado.', + }, + ERROR_PRODUCTOS_INVALIDOS: { + codigo: 400, + mensaje: 'Uno o más productos no existen en este cliente.', + }, + ERROR_ACTUALIZAR_SET_PRODUCTOS: { + codigo: 500, + mensaje: 'Ocurrió un error al actualizar el set de productos', + }, + FORMATO_INVALIDO_DATOS: { + codigo: 400, + mensaje: 'Formato de datos inválido para actualizar el set de productos', + }, + SET_ACTUALIZADO: { + codigo: 200, + mensaje: 'Set de productos actualizado correctamente', + }, + ERROR_ACTUALIZAR_SET: { + codigo: 500, + mensaje: 'Error interno al actualizar el set de productos', + }, + SET_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'El set de productos no fue encontrado', + }, + PRODUCTOS_INVALIDOS: { + codigo: 400, + mensaje: 'La lista de productos contiene elementos inválidos', + }, + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Parámetros inválidos', + }, +}; diff --git a/Utilidades/Constantes/mensajesUsuarios.js b/Utilidades/Constantes/mensajesUsuarios.js new file mode 100644 index 00000000..898f47a2 --- /dev/null +++ b/Utilidades/Constantes/mensajesUsuarios.js @@ -0,0 +1,90 @@ +module.exports = { + // 201 - Creado + USUARIO_CREADO: { + codigo: 201, + mensaje: 'Usuario creado correctamente.', + }, + + // 200 - OK + USUARIO_OBTENIDO: { + codigo: 200, + mensaje: 'Información del usuario obtenida exitosamente.', + }, + LISTA_USUARIOS_OBTENIDA: { + codigo: 200, + mensaje: 'Lista de usuarios obtenida exitosamente.', + }, + + // 204 - Sin contenido + USUARIOS_NO_ENCONTRADOS: { + codigo: 204, + mensaje: 'No se encontraron usuarios registrados.', + }, + + // 400 - Bad Request + DATOS_INCOMPLETOS: { + codigo: 400, + mensaje: 'Faltan campos requeridos para crear el usuario.', + }, + CORREO_INVALIDO: { + codigo: 400, + mensaje: 'El correo electrónico proporcionado no es válido.', + }, + CONTRASENA_DEBIL: { + codigo: 400, + mensaje: + 'La contraseña debe tener al menos 8 caracteres y contener al menos un carácter especial.', + }, + TELEFONO_INVALIDO: { + codigo: 400, + mensaje: 'El número de teléfono debe contener exactamente 10 dígitos.', + }, + ROL_O_CLIENTE_INVALIDO: { + codigo: 400, + mensaje: 'El rol o el cliente especificado no es válido.', + }, + USUARIO_YA_EXISTE: { + codigo: 400, + mensaje: 'Ya existe un usuario con este correo electrónico.', + }, + PARAMETROS_INVALIDOS: { + codigo: 400, + mensaje: 'Los parámetros proporcionados no son válidos.', + }, + ERROR_OBTENER_USUARIO: { + codigo: 400, + mensaje: 'Ocurrió un error al obtener los datos del usuario.', + }, + + // 401 - sin autorizacion + CREDENCIALES_INVALIDAS: { + codigo: 401, + mensaje: 'Correo electrónico o contraseña incorrectos.', + }, + + // 403 - Denegado + ACCESO_DENEGADO: { + codigo: 403, + mensaje: 'No tiene permiso para realizar esta acción sobre usuarios.', + }, + + // 404 - No encontrado + USUARIO_NO_ENCONTRADO: { + codigo: 404, + mensaje: 'No se encontró un usuario con el ID proporcionado.', + }, + + // 500 - Server Error + ERROR_CREAR_USUARIO: { + codigo: 500, + mensaje: 'Ocurrió un error al intentar crear el usuario. Probablemente ya existe.', + }, + ERROR_OBTENER_USUARIOS: { + codigo: 500, + mensaje: 'Ocurrió un error al obtener la lista de usuarios.', + }, + ERROR_ELIMINAR_USUARIO: { + codigo: 500, + mensaje: 'Ocurrió un error al intentar eliminar el usuario.', + }, +}; diff --git a/Utilidades/Constantes/permisos.js b/Utilidades/Constantes/permisos.js new file mode 100644 index 00000000..417fa8d1 --- /dev/null +++ b/Utilidades/Constantes/permisos.js @@ -0,0 +1,120 @@ +module.exports = { + // Sistema + CONSULTAR_SISTEMA_ADMINISTRATIVO: 'Consultar Sistema Administrativo', + CONSULTAR_TIENDA: 'Consultar Tienda', + + // Usuario + CREAR_USUARIO: 'Crear Usuario', + CONSULTAR_USUARIOS: 'Consultar Lista de Usuarios', + LEER_USUARIO: 'Leer Usuario', + ACTUALIZAR_USUARIO: 'Actualizar Usuario', + ELIMINAR_USUARIOS: 'Eliminar Usuario', + ACTIVAR_2FA_SUPERADMIN: 'Activar 2FA para Superadmin', + VERIFICAR_2FA_SUPERADMIN: 'Verificar 2FA para Superadmin', + + // Rol + CREAR_ROL: 'Crear Rol', + CONSULTAR_ROLES: 'Consultar Lista de Roles', + LEER_ROL: 'Leer Rol', + ACTUALIZAR_ROL: 'Actualizar Rol', + ELIMINAR_ROL: 'Eliminar Rol', + + // Cliente + CREAR_CLIENTE: 'Crear Cliente', + CONSULTAR_CLIENTES: 'Consultar Lista de Clientes', + LEER_CLIENTE: 'Leer Cliente', + ACTUALIZAR_CLIENTE: 'Actualizar Cliente', + ELIMINAR_CLIENTE: 'Eliminar Cliente', + + // Empleado + CREAR_EMPLEADO: 'Crear Empleado', + CONSULTAR_EMPLEADOS: 'Consultar Lista de Empleados', + LEER_EMPLEADO: 'Leer Empleado', + ACTUALIZAR_EMPLEADO: 'Actualizar Empleado', + ELIMINAR_EMPLEADO: 'Eliminar Empleado', + + // Grupo de Empleados + CREAR_GRUPO_EMPLEADOS: 'Crear Grupo de Empleados', + CONSULTAR_GRUPOS_EMPLEADOS: 'Consultar Lista de Grupos de Empleados', + LEER_GRUPO_EMPLEADOS: 'Leer Grupo de Empleados', + ACTUALIZAR_GRUPO_EMPLEADOS: 'Actualizar Grupo de Empleados', + ELIMINAR_GRUPO_EMPLEADOS: 'Eliminar Grupo de Empleados', + + // Producto + CREAR_PRODUCTO: 'Crear Producto', + CONSULTAR_PRODUCTOS: 'Consultar Lista de Productos', + LEER_PRODUCTO: 'Leer Producto', + ACTUALIZAR_PRODUCTO: 'Actualizar Producto', + ELIMINAR_PRODUCTO: 'Eliminar Producto', + + // Set de Cuotas + CREAR_SET_CUOTAS: 'Crear Set de Cuotas', + CONSULTAR_SETS_CUOTAS: 'Consultar Lista de Sets de Cuotas', + LEER_SET_CUOTAS: 'Leer Set de Cuotas', + ACTUALIZAR_SET_CUOTAS: 'Actualizar Set de Cuotas', + ELIMINAR_SET_CUOTAS: 'Eliminar Set de Cuotas', + + // Evento + CREAR_EVENTO: 'Crear Evento', + CONSULTAR_EVENTO: 'Consultar Lista de Eventos', + LEER_EVENTO: 'Leer Evento', + ACTUALIZAR_EVENTO: 'Actualizar Evento', + ELIMINAR_EVENTO: 'Eliminar Evento', + + // Set de Productos + CREAR_SET_PRODUCTOS: 'Crear Set de Productos', + CONSULTAR_SETS_PRODUCTOS: 'Consultar Lista de Sets de Productos', + LEER_SET_PRODUCTOS: 'Leer Set de Productos', + ACTUALIZAR_SET_PRODUCTOS: 'Actualizar Set de Productos', + ELIMINAR_SET_PRODUCTOS: 'Eliminar Set de Productos', + + // Categoría de Productos + CREAR_CATEGORIA_PRODUCTOS: 'Crear Categoría de Productos', + CONSULTAR_CATEGORIAS_PRODUCTOS: 'Consultar Lista de Categorías de Productos', + LEER_CATEGORIA_PRODUCTOS: 'Leer Categoría de Productos', + ACTUALIZAR_CATEGORIA_PRODUCTOS: 'Actualizar Categoría de Productos', + ELIMINAR_CATEGORIA_PRODUCTOS: 'Eliminar Categoría de Productos', + + // Tipo de Pago + CREAR_TIPO_PAGO: 'Crear Tipo de Pago', + CONSULTAR_TIPOS_PAGO: 'Consultar Lista de Tipos de Pago', + LEER_TIPO_PAGO: 'Leer Tipo de Pago', + ACTUALIZAR_TIPO_PAGO: 'Actualizar Tipo de Pago', + ELIMINAR_TIPO_PAGO: 'Eliminar Tipo de Pago', + + // Importar / Exportar + IMPORTAR_PRODUCTOS: 'Importar Productos', + IMPORTAR_EMPLEADOS: 'Importar Empleados', + EXPORTAR_PRODUCTOS: 'Exportar Productos', + EXPORTAR_EMPLEADOS: 'Exportar Empleados', + + // Pedido + CONSULTAR_PEDIDOS: 'Consultar Lista de Pedidos', + LEER_PEDIDO: 'Leer Pedido', + ACTUALIZAR_PEDIDO: 'Actualizar Pedido', + ELIMINAR_PEDIDO: 'Eliminar Pedido', + + // Ayuda + ACCEDER_CENTRO_AYUDA: 'Acceder al Centro de Ayuda', + + // Carrito + LEER_CARRITO: 'Leer Carrito de Compras', + ACTUALIZAR_CARRITO: 'Actualizar Carrito de Compras', + ELIMINAR_PRODUCTO_CARRITO: 'Eliminar Productos del Carrito', + AGREGAR_PRODUCTO_CARRITO: 'Agregar Producto al Carrito', + + // Pedido Tienda + CREAR_PEDIDO: 'Crear Pedido', + LEER_PEDIDO_TIENDA: 'Leer Pedido Tienda', + FINALIZAR_PEDIDO: 'Finalizar Pedido', + CONSULTAR_PEDIDOS_TIENDA: 'Consultar Lista de Pedidos Tienda', + RECIBIR_NOTIFICACIONES_PEDIDO: 'Recibir Notificaciones de Estado del Pedido', + + // Producto Tienda + LEER_PRODUCTO_TIENDA: 'Leer Producto Tienda', + CONSULTAR_PRODUCTOS_TIENDA: 'Consultar Lista de Productos Tienda', + + // Balance / Pago + LEER_BALANCE: 'Leer Balance', + SELECCIONAR_TIPO_PAGO: 'Seleccionar Tipo de Pago', +}; diff --git a/Utilidades/Constantes/rutas.js b/Utilidades/Constantes/rutas.js new file mode 100644 index 00000000..c9ba52b7 --- /dev/null +++ b/Utilidades/Constantes/rutas.js @@ -0,0 +1,115 @@ +module.exports = { + RAIZ: '/', + API: '/api', + AUTENTICACION: { + BASE: '/autenticacion', + INICIO_SESION: '/iniciar-sesion', + REGISTRO: '/registro', + CERRAR_SESION: '/cerrar-sesion', + USUARIO_AUTENTICADO: '/autenticar', + ACTIVAR_2FA: '/activar-2fa', + VERIFICAR_2FA: '/verificar-2fa', + }, + USUARIOS: { + BASE: '/usuarios', + CONSULTAR_LISTA_USUARIOS: '/consultar-lista-usuarios', + CREAR: '/crear', + ELIMINAR_USUARIOS: '/eliminar-usuarios', + LEER: '/consultar-usuario', + }, + CATEGORIAS: { + BASE: '/categorias', + CONSULTAR_LISTA_CATEGORIAS: '/consultar-lista-categorias', + CREAR_CATEGORIA: '/crear-categoria', + ELIMINAR_CATEGORIA: '/eliminar', + CONSULTAR_LISTA_USUARIOS: '/consultar-lista-usuarios', + CREAR: '/crear', + ELIMINAR_USUARIOS: '/eliminar-usuarios', + LEER: '/leer', + ACTUALIZAR: '/actualizar', + }, + EVENTOS: { + BASE: '/eventos', + CREAR: '/crear', + ELIMINAR: '/eliminar', + EDITAR: '/editar', + CONSULTAR_LISTA_EVENTOS: '/consultar-lista-eventos', + CONSULTAR_EVENTO: '/consultar-evento', + }, + PRODUCTOS: { + BASE: '/productos', + CONSULTAR_LISTA: '/consultar-lista', + CREAR: '/crear', + ELIMINAR_PRODUCTO: '/eliminar', + IMPORTAR: '/importar', + LEER: '/leer-producto', + EXPORTAR_PRODUCTOS: '/exportar-productos', + }, + PROVEEDORES: { + BASE: '/proveedores', + CONSULTAR_LISTA: '/consultar-lista', + CREAR: '/crear', + }, + SETS_PRODUCTOS: { + BASE: '/sets-productos', + CONSULTAR_LISTA: '/consultar-lista', + CREAR: '/crear', + SUBIR_IMAGEN: '/subir-imagen', + ELIMINAR_SET_PRODUCTOS: '/eliminar', + ACTUALIZAR: '/actualizar', + }, + CLIENTES: { + BASE: '/clientes', + CONSULTAR_SISTEMA: '/consultar-sistema', + CONSULTAR_LISTA: '/consultar-lista', + ELIMINAR_CLIENTE: '/eliminar', + CREAR_CLIENTE: '/crear-cliente', + LEER: '/consultar-cliente', + ACTUALIZAR: `/actualizar-cliente`, + }, + EMPLEADOS: { + BASE: '/empleados', + CREAR: '/crear', + CONSULTAR_LISTA: '/consultar-lista', + CONSULTAR_GRUPO: '/consultar-grupo', + ELIMINAR_GRUPO: '/eliminar-grupo', + ELIMINAR_EMPLEADO: '/eliminar', + IMPORTAR_EMPLEADOS: '/importar-empleados', + EXPORTAR_EMPLEADOS: '/exportar-empleados', + LEER_GRUPO: '/leer-grupo', + CREAR_GRUPO: '/crear-grupo', + ACTUALIZAR: '/actualizar', + ACTUALIZAR_GRUPO_EMPLEADO: '/actualizar-grupo', + }, + CUOTAS: { + BASE: '/cuotas', + AGREGAR: '/crear-cuota', + OPCIONES: '/obtener-opciones', + CONSULTAR_LISTA: '/consultar-lista', + ELIMINAR_SET_CUOTAS: '/eliminar-set-cuotas', + LEER_SET_CUOTAS: '/leer-set-cuotas', + ACTUALIZAR_SET_CUOTAS: '/actualizar-set-cuotas', + }, + ROLES: { + BASE: '/roles', + CONSULTAR_LISTA: '/consultar-lista', + CREAR_ROL: '/crear-rol', + OBTENER_OPCIONES: '/obtener-opciones', + CONFIRMAR_CREACION: '/confirmar-creacion', + ELIMINAR_ROL: '/eliminar', + LEER_ROL: '/leer', + ACTUALIZAR: '/actualizar-rol', + }, + PEDIDOS: { + BASE: '/pedidos', + CONSULTAR_LISTA: '/consultar-lista', + ELIMINAR_PEDIDO: '/eliminar', + ACTUALIZAR_PEDIDO: '/actualizar-pedido', + }, + PAGOS: { + BASE: '/pagos', + CONSULTAR_LISTA: '/consultar-lista', + ACTUALIZAR: '/actualizar', + }, + API_DOCS: '/api-docs', +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarOpciones.js b/Utilidades/Intermediarios/Validaciones/validarOpciones.js new file mode 100644 index 00000000..8dc60a95 --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarOpciones.js @@ -0,0 +1,86 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida un conjunto de opciones para un producto. + * + * Esta función valida cada opción dentro del arreglo de opciones pasado como argumento. + * Se asegura de que los valores de las propiedades cumplan con los tipos y rangos especificados, + * y retorna un objeto de error si algún valor es inválido. + * + * @param {Array} opciones - Un arreglo de objetos que representan las opciones de un producto. + * @param {number} opciones[].cantidad - La cantidad de la opción, que debe ser un número entero positivo o cero. + * @param {string} opciones[].valorOpcion - El valor de la opción, que debe ser una cadena de texto de máximo 100 caracteres. + * @param {string} [opciones[].SKUautomatico] - El SKU automático de la opción, que debe ser una cadena de texto de máximo 50 caracteres, si se proporciona. + * @param {string} [opciones[].SKUcomercial] - El SKU comercial de la opción, que debe ser una cadena de texto de máximo 50 caracteres, si se proporciona. + * @param {number} opciones[].costoAdicional - El costo adicional de la opción, que debe ser un número positivo o cero. + * @param {number} opciones[].descuento - El descuento de la opción, que debe ser un número entre 0 y 100. + * @param {number} opciones[].estado - El estado de la opción, que debe ser 1 (activo) o 0 (inactivo). + * + * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todas las opciones son válidas. + * @example + * const opciones = [ + * { + * cantidad: 5, + * valorOpcion: 'Tamaño M', + * SKUautomatico: 'SKU123', + * SKUcomercial: 'S123', + * costoAdicional: 15.50, + * descuento: 10, + * estado: 1 + * } + * ]; + * + * const resultado = validarOpciones(opciones); + * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido + */ +module.exports = (opciones) => { + for (const opcion of opciones) { + // prettier-ignore + if ( + typeof opcion.cantidad !== 'number' + || opcion.cantidad < 0 + || !Number.isInteger(opcion.cantidad) + ) { + return { error: 'cantidad de la opción debe ser un número entero positivo o cero.' }; + } + + // prettier-ignore + if ( + !opcion.valorOpcion + || typeof opcion.valorOpcion !== 'string' + || opcion.valorOpcion.length > 100 + ) { + return { + error: 'valorOpcion es requerido y debe ser una cadena de texto de máximo 100 caracteres.', + }; + } + + // prettier-ignore + if ( + opcion.SKUautomatico + && (typeof opcion.SKUautomatico !== 'string' || opcion.SKUautomatico.length > 50) + ) { + return { error: 'SKUautomatico debe ser una cadena de texto de máximo 50 caracteres.' }; + } + // prettier-ignore + if ( + opcion.SKUcomercial + && (typeof opcion.SKUcomercial !== 'string' || opcion.SKUcomercial.length > 50) + ) { + return { error: 'SKUcomercial debe ser una cadena de texto de máximo 50 caracteres.' }; + } + + if (typeof opcion.costoAdicional !== 'number' || opcion.costoAdicional < 0) { + return { error: 'costoAdicional debe ser un número positivo o cero.' }; + } + + if (typeof opcion.descuento !== 'number' || opcion.descuento < 0 || opcion.descuento > 100) { + return { error: 'descuento debe ser un número entre 0 y 100.' }; + } + + if (typeof opcion.estado !== 'number' || (opcion.estado !== 0 && opcion.estado !== 1)) { + return { error: 'estado debe ser 1 (activo) o 0 (inactivo).' }; + } + } + + return null; +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarOpcionesImportar.js b/Utilidades/Intermediarios/Validaciones/validarOpcionesImportar.js new file mode 100644 index 00000000..69e7775e --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarOpcionesImportar.js @@ -0,0 +1,81 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida un conjunto de opciones para un producto. + * + * Esta función valida cada opción dentro del arreglo de opciones pasado como argumento. + * Se asegura de que los valores de las propiedades cumplan con los tipos y rangos especificados, + * y retorna un objeto de error si algún valor es inválido. + * + * @param {Array} opciones - Un arreglo de objetos que representan las opciones de un producto. + * @param {number} opciones[].cantidad - La cantidad de la opción, que debe ser un número entero positivo o cero. + * @param {string} opciones[].valorOpcion - El valor de la opción, que debe ser una cadena de texto de máximo 100 caracteres. + * @param {string} [opciones[].SKUautomatico] - El SKU automático de la opción, que debe ser una cadena de texto de máximo 50 caracteres, si se proporciona. + * @param {string} [opciones[].SKUcomercial] - El SKU comercial de la opción, que debe ser una cadena de texto de máximo 50 caracteres, si se proporciona. + * @param {number} opciones[].costoAdicional - El costo adicional de la opción, que debe ser un número positivo o cero. + * @param {number} opciones[].descuento - El descuento de la opción, que debe ser un número entre 0 y 100. + * @param {number} opciones[].estado - El estado de la opción, que debe ser 1 (activo) o 0 (inactivo). + * + * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todas las opciones son válidas. + * @example + * const opciones = [ + * { + * cantidad: 5, + * valorOpcion: 'Tamaño M', + * SKUautomatico: 'SKU123', + * SKUcomercial: 'S123', + * costoAdicional: 15.50, + * descuento: 10, + * estado: 1 + * } + * ]; + * + * const resultado = validarOpciones(opciones); + * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido + */ +module.exports = (opciones) => { + for (const opcion of opciones) { + // prettier-ignore + if ( + typeof opcion.cantidad !== 'number' + || opcion.cantidad < 0 + || !Number.isInteger(opcion.cantidad) + || opcion.cantidad % 1 !== 0 + ) { + return { error: 'cantidad de la opción debe ser un número entero positivo o cero.' }; + } + + // prettier-ignore + if ( + !opcion.valorOpcion + || typeof opcion.valorOpcion !== 'string' + || opcion.valorOpcion.length > 100 + ) { + return { + error: 'valorOpcion es requerido y debe ser una cadena de texto de máximo 100 caracteres.', + }; + } + + // prettier-ignore + if (!opcion.SKUcomercial + || opcion.SKUcomercial == null + || typeof opcion.SKUcomercial !== 'string' + || opcion.SKUcomercial.length > 50 + ) { + return { error: 'SKUcomercial debe ser una cadena de texto de máximo 50 caracteres.' }; + } + + if (typeof opcion.costoAdicional !== 'number' || opcion.costoAdicional < 0) { + return { error: 'costoAdicional debe ser un número positivo o cero.' }; + } + + if (typeof opcion.descuento !== 'number' || opcion.descuento < 0 || opcion.descuento > 100) { + return { error: 'descuento debe ser un número entre 0 y 100.' }; + } + + if (typeof opcion.estado !== 'number' || (opcion.estado !== 0 && opcion.estado !== 1)) { + return { error: 'estado debe ser 1 (activo) o 0 (inactivo).' }; + } + } + + return null; +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarProducto.js b/Utilidades/Intermediarios/Validaciones/validarProducto.js new file mode 100644 index 00000000..5738d072 --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarProducto.js @@ -0,0 +1,138 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida los campos de un producto. + * + * Esta función valida los diferentes campos de un objeto `producto` asegurándose de que cumplan con los tipos y restricciones especificados. Si algún campo no cumple con las condiciones, se devuelve un objeto de error con un mensaje específico. Si todos los campos son válidos, se retorna `null`. + * + * @param {object} producto - El objeto que representa un producto a validar. + * @param {number|null} producto.idProveedor - El ID del proveedor, debe ser un número entero positivo o `null`. + * @param {string} producto.nombreComun - El nombre común del producto, debe ser una cadena de texto de máximo 100 caracteres. + * @param {string|null} producto.nombreComercial - El nombre comercial del producto, debe ser una cadena de texto de máximo 150 caracteres o `null`. + * @param {string|null} producto.descripcion - Descripción del producto, debe ser una cadena de texto de máximo 1000 caracteres o `null`. + * @param {string|null} producto.marca - La marca del producto, debe ser una cadena de texto de máximo 100 caracteres o `null`. + * @param {string|null} producto.modelo - El modelo del producto, debe ser una cadena de texto de máximo 100 caracteres o `null`. + * @param {string|null} producto.tipoProducto - El tipo de producto, debe ser una cadena de texto de máximo 50 caracteres o `null`. + * @param {number|null} producto.precioPuntos - El precio en puntos, debe ser un número entero positivo o `null`. + * @param {number|null} producto.precioCliente - El precio para el cliente, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.precioVenta - El precio de venta, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.costo - El costo del producto, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.impuesto - El impuesto del producto, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.descuento - El descuento del producto, debe ser un número mayor o igual a cero o `null`. + * @param {number} producto.estado - El estado del producto, debe ser 0 (inactivo) o 1 (activo). + * @param {number} producto.envio - Indica si el envío está disponible, debe ser 0 (no disponible) o 1 (disponible). + * + * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todos los campos son válidos. + * @example + * const producto = { + * idProveedor: 1, + * nombreComun: 'Producto X', + * nombreComercial: 'Producto X Comercial', + * descripcion: 'Descripción del producto', + * marca: 'Marca X', + * modelo: 'Modelo 123', + * tipoProducto: 'Tipo A', + * precioPuntos: 100, + * precioCliente: 200.50, + * precioVenta: 250, + * costo: 150, + * impuesto: 25, + * descuento: 10, + * estado: 1, + * envio: 1, + * }; + * + * const resultado = validarProducto(producto); + * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido + */ +// prettier-ignore +module.exports = (producto) => { + if ( + producto.idProveedor !== null + && (typeof producto.idProveedor !== 'number' + || producto.idProveedor <= 0 + || !Number.isInteger(producto.idProveedor)) + ) { + return { error: 'idProveedor debe ser un número entero positivo o NULL.' }; + } + + if ( + !producto.nombreComun + || typeof producto.nombreComun !== 'string' + || producto.nombreComun.length > 100 + ) { + return { + error: 'nombreComun es requerido, debe ser una cadena de texto y no exceder 100 caracteres.', + }; + } + + if ( + producto.nombreComercial !== null + && (typeof producto.nombreComercial !== 'string' || producto.nombreComercial.length > 150) + ) { + return { + error: 'nombreComercial debe ser una cadena de texto o NULL y no exceder 150 caracteres.', + }; + } + + if ( + producto.descripcion !== null + && (typeof producto.descripcion !== 'string' || producto.descripcion.length > 1000) + ) { + return { + error: 'descripcion debe ser una cadena de texto o NULL y no exceder 1000 caracteres.', + }; + } + + if ( + producto.marca !== null + && (typeof producto.marca !== 'string' || producto.marca.length > 100) + ) { + return { error: 'marca debe ser una cadena de texto o NULL y no exceder 100 caracteres.' }; + } + + if ( + producto.modelo !== null + && (typeof producto.modelo !== 'string' || producto.modelo.length > 100) + ) { + return { error: 'modelo debe ser una cadena de texto o NULL y no exceder 100 caracteres.' }; + } + + if ( + producto.tipoProducto !== null + && (typeof producto.tipoProducto !== 'string' || producto.tipoProducto.length > 50) + ) { + return { + error: 'tipoProducto debe ser una cadena de texto o NULL y no exceder 50 caracteres.', + }; + } + + const camposNumericos = [ + 'precioPuntos', + 'precioCliente', + 'precioVenta', + 'costo', + 'impuesto', + 'descuento', + ]; + + for (const campo of camposNumericos) { + if ( + producto[campo] !== null + && (typeof producto[campo] !== 'number' + || (campo === 'precioPuntos' && !Number.isInteger(producto[campo])) + || (campo !== 'precioPuntos' && producto[campo] < 0)) + ) { + return { error: `${campo} debe ser un número válido y mayor o igual a cero.` }; + } + } + + if (producto.estado !== undefined && producto.estado !== 0 && producto.estado !== 1) { + return { error: 'estado debe ser 0 (inactivo) o 1 (activo).' }; + } + + if (producto.envio !== undefined && producto.envio !== 0 && producto.envio !== 1) { + return { error: 'envio debe ser 0 (no disponible) o 1 (disponible).' }; + } + + return null; +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarProductoImportado.js b/Utilidades/Intermediarios/Validaciones/validarProductoImportado.js new file mode 100644 index 00000000..1fd8e2b3 --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarProductoImportado.js @@ -0,0 +1,181 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida los campos de un producto. + * + * Esta función valida los diferentes campos de un objeto `producto` asegurándose de que cumplan con los tipos y restricciones especificados. Si algún campo no cumple con las condiciones, se devuelve un objeto de error con un mensaje específico. Si todos los campos son válidos, se retorna `null`. + * + * @param {object} producto - El objeto que representa un producto a validar. + * @param {number|null} producto.idProveedor - El ID del proveedor, debe ser un número entero positivo o `null`. + * @param {string} producto.nombreComun - El nombre común del producto, debe ser una cadena de texto de máximo 100 caracteres. + * @param {string|null} producto.nombreComercial - El nombre comercial del producto, debe ser una cadena de texto de máximo 150 caracteres o `null`. + * @param {string|null} producto.descripcion - Descripción del producto, debe ser una cadena de texto de máximo 1000 caracteres o `null`. + * @param {string|null} producto.marca - La marca del producto, debe ser una cadena de texto de máximo 100 caracteres o `null`. + * @param {string|null} producto.modelo - El modelo del producto, debe ser una cadena de texto de máximo 100 caracteres o `null`. + * @param {string|null} producto.tipoProducto - El tipo de producto, debe ser una cadena de texto de máximo 50 caracteres o `null`. + * @param {number|null} producto.precioPuntos - El precio en puntos, debe ser un número entero positivo o `null`. + * @param {number|null} producto.precioCliente - El precio para el cliente, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.precioVenta - El precio de venta, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.costo - El costo del producto, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.impuesto - El impuesto del producto, debe ser un número mayor o igual a cero o `null`. + * @param {number|null} producto.descuento - El descuento del producto, debe ser un número mayor o igual a cero o `null`. + * @param {number} producto.estado - El estado del producto, debe ser 0 (inactivo) o 1 (activo). + * @param {number} producto.envio - Indica si el envío está disponible, debe ser 0 (no disponible) o 1 (disponible). + * + * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todos los campos son válidos. + * @example + * const producto = { + * idProveedor: 1, + * nombreComun: 'Producto X', + * nombreComercial: 'Producto X Comercial', + * descripcion: 'Descripción del producto', + * marca: 'Marca X', + * modelo: 'Modelo 123', + * tipoProducto: 'Tipo A', + * precioPuntos: 100, + * precioCliente: 200.50, + * precioVenta: 250, + * costo: 150, + * impuesto: 25, + * descuento: 10, + * estado: 1, + * envio: 1, + * }; + * + * const resultado = validarProducto(producto); + * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido + */ +// prettier-ignore +module.exports = (producto) => { + if ( + producto.idProveedor !== null + && ( + typeof producto.idProveedor !== 'number' + || producto.idProveedor <= 0 + || producto.idProveedor % 1 !== 0 + ) + ) { + return { error: 'idProveedor debe ser un número entero positivo o NULL.' }; + } + + if ( + !producto.nombreComun + || typeof producto.nombreComun !== 'string' + || producto.nombreComun.length > 100 + ) { + return { + error: 'nombreProducto es requerido, debe ser una cadena de texto y no exceder 100 caracteres.', + }; + } + + if ( + !producto.nombreComercial + || typeof producto.nombreComercial !== 'string' + || producto.nombreComercial.length > 100 + ) { + return { + error: 'nombreComercial es requerido, debe ser una cadena de texto y no exceder 100 caracteres.', + }; + } + + if ( + producto.descripcion !== null + && (typeof producto.descripcion !== 'string' || producto.descripcion.length > 1000 || producto.descripcion.trim() === '') + ) { + return { + error: 'descripcion debe ser una cadena de texto y no exceder 1000 caracteres.', + }; + } + + if ( + producto.marca !== null + && (typeof producto.marca !== 'string' || producto.marca.length > 100 || producto.marca.trim() === '') + ) { + return { error: 'marca debe ser una cadena de texto y no exceder 100 caracteres.' }; + } + + if ( + producto.modelo !== null + && (typeof producto.modelo !== 'string' || producto.modelo.length > 100 || producto.modelo.trim() === '') + ) { + return { error: 'modelo debe ser una cadena de texto y no exceder 100 caracteres.' }; + } + + if ( + producto.tipoProducto !== null + && (typeof producto.tipoProducto !== 'string' || producto.tipoProducto.length > 50 || producto.tipoProducto.trim() === '') + ) { + return { + error: 'tipoProducto debe ser una cadena de texto y no exceder 50 caracteres.', + }; + } + + if ( + typeof producto.costo !== 'number' + || producto.costo < 0 + || Number.isNaN(producto.costo) + ) { + return { + error: 'costo debe ser un número mayor o igual a cero', + }; + } + + if ( + typeof producto.precioVenta !== 'number' + || producto.precioVenta < 0 + || Number.isNaN(producto.precioVenta) + ) { + return { + error: 'precioVenta debe ser un número mayor o igual a cero', + }; + } + + if ( + typeof producto.precioCliente !== 'number' + || producto.precioCliente < 0 + || Number.isNaN(producto.precioCliente) + ) { + return { + error: 'precioCliente debe ser un número mayor o igual a cero', + }; + } + + if ( + typeof producto.precioPuntos !== 'number' + || producto.precioPuntos < 0 + || Number.isNaN(producto.precioPuntos) + ) { + return { + error: 'precioPuntos debe ser un número mayor o igual a cero', + }; + } + + if ( + typeof producto.impuesto !== 'number' + || producto.impuesto < 0 + || Number.isNaN(producto.impuesto) + ) { + return { + error: 'impuesto debe ser un número mayor o igual a cero', + }; + } + + if ( + typeof producto.descuento !== 'number' + || producto.descuento < 0 || producto.descuento > 100 + || Number.isNaN(producto.descuento) + ) { + return { + error: 'descuento debe ser un número mayor o igual a cero y menor o igual a 100', + }; + } + + if (producto.estado !== undefined && producto.estado !== 0 && producto.estado !== 1) { + return { error: 'estado debe ser 0 (inactivo) o 1 (activo).' }; + } + + if (producto.envio !== undefined && producto.envio !== 0 && producto.envio !== 1) { + return { error: 'envio debe ser 0 (no disponible) o 1 (disponible).' }; + } + + return null; +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarProveedor.js b/Utilidades/Intermediarios/Validaciones/validarProveedor.js new file mode 100644 index 00000000..2618b3dc --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarProveedor.js @@ -0,0 +1,84 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida los campos de un proveedor. + * + * Esta función verifica que los campos del objeto `proveedor` cumplan con los requisitos de tipo, longitud y formato. + * Si algún campo no es válido, devuelve un objeto con un mensaje de error específico. + * Si todos los campos son válidos, retorna `null`. + * + * @param {object} proveedor - Objeto que representa al proveedor a validar. + * @param {string} proveedor.nombre - Nombre del proveedor (obligatorio, máximo 100 caracteres). + * @param {string} [proveedor.nombreCompania] - Nombre de la compañía (opcional, máximo 150 caracteres). + * @param {string} [proveedor.telefonoContacto] - Teléfono de contacto (opcional, máximo 20 caracteres). + * @param {string} [proveedor.correoContacto] - Correo electrónico de contacto (opcional, válido y máximo 100 caracteres). + * @param {string} [proveedor.direccion] - Dirección del proveedor (opcional, máximo 200 caracteres). + * @param {string} [proveedor.codigoPostal] - Código postal (opcional, máximo 20 caracteres). + * @param {string} [proveedor.pais] - País del proveedor (opcional, máximo 50 caracteres). + * @param {number} proveedor.estado - Estado del proveedor: 1 (activo) o 0 (inactivo). + * + * @returns {{ error: string } | null} Retorna un objeto con la propiedad `error` si hay un error de validación, o `null` si todo es válido. + */ +module.exports = (proveedor) => { + /** + * Verifica si un texto es válido (tipo string, no vacío, dentro del límite de caracteres). + * + * @param {string} valor - Texto a validar. + * @param {number} max - Longitud máxima permitida. + * @returns {boolean} `true` si es válido, `false` en caso contrario. + */ + // prettier-ignore + const esTextoValido = (valor, max) => typeof valor === 'string' && valor.trim().length > 0 && valor.trim().length <= max; + + if (!esTextoValido(proveedor.nombre, 100)) { + return { + error: 'nombre es obligatorio y debe ser una cadena de texto de máximo 100 caracteres.', + }; + } + + if (proveedor.nombreCompania != null && !esTextoValido(proveedor.nombreCompania, 150)) { + return { + error: 'nombreCompania debe ser una cadena de texto no vacía de máximo 150 caracteres.', + }; + } + + if (proveedor.telefonoContacto != null && !esTextoValido(proveedor.telefonoContacto, 20)) { + return { + error: 'telefonoContacto debe ser una cadena de texto no vacía de máximo 20 caracteres.', + }; + } + + // prettier-ignore + if (proveedor.correoContacto != null) { + const correo = proveedor.correoContacto.trim(); + const regexCorreo = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if ( + typeof correo !== 'string' + || correo.length === 0 + || correo.length > 100 + || !regexCorreo.test(correo) + ) { + return { + error: + 'correoContacto debe ser un correo electrónico válido y con un máximo de 100 caracteres.', + }; + } + } + + if (proveedor.direccion != null && !esTextoValido(proveedor.direccion, 200)) { + return { error: 'direccion debe ser una cadena de texto no vacía de máximo 200 caracteres.' }; + } + + if (proveedor.codigoPostal != null && !esTextoValido(proveedor.codigoPostal, 20)) { + return { error: 'codigoPostal debe ser una cadena de texto no vacía de máximo 20 caracteres.' }; + } + + if (proveedor.pais != null && !esTextoValido(proveedor.pais, 50)) { + return { error: 'pais debe ser una cadena de texto no vacía de máximo 50 caracteres.' }; + } + + if (typeof proveedor.estado !== 'number' || (proveedor.estado !== 1 && proveedor.estado !== 0)) { + return { error: 'estado debe ser 1 (activo) o 0 (inactivo).' }; + } + + return null; +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarVariante.js b/Utilidades/Intermediarios/Validaciones/validarVariante.js new file mode 100644 index 00000000..e5de5951 --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarVariante.js @@ -0,0 +1,44 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida los campos de una variante. + * + * Esta función valida los diferentes campos de un objeto `variante` asegurándose de que cumplan con los tipos y restricciones especificadas. Si algún campo no cumple con las condiciones, se devuelve un objeto de error con un mensaje específico. Si todos los campos son válidos, se retorna `null`. + * + * @param {object} variante - El objeto que representa una variante a validar. + * @param {string} variante.nombreVariante - El nombre de la variante, debe ser una cadena de texto de máximo 100 caracteres. + * @param {string|null} variante.descripcion - La descripción de la variante, debe ser una cadena de texto de máximo 1000 caracteres o `null`. + * + * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todos los campos son válidos. + * @example + * const variante = { + * nombreVariante: 'Tamaño L', + * descripcion: 'Variante para talla grande', + * }; + * + * const resultado = validarVariante(variante); + * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido + */ +// prettier-ignore +module.exports = (variante) => { + if ( + !variante.nombreVariante + || typeof variante.nombreVariante !== 'string' + || variante.nombreVariante.length > 100 + ) { + return { + error: + 'nombreVariante es requerido, debe ser una cadena de texto y no exceder 100 caracteres.', + }; + } + + if ( + variante.descripcion !== null + && (typeof variante.descripcion !== 'string' || variante.descripcion.length > 1000) + ) { + return { + error: 'descripcion debe ser una cadena de texto y no exceder 1000 caracteres.', + }; + } + + return null; +}; diff --git a/Utilidades/Intermediarios/Validaciones/validarVarianteImportar.js b/Utilidades/Intermediarios/Validaciones/validarVarianteImportar.js new file mode 100644 index 00000000..a65542f0 --- /dev/null +++ b/Utilidades/Intermediarios/Validaciones/validarVarianteImportar.js @@ -0,0 +1,44 @@ +//RF26 Crea Producto - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF26 +/** + * Valida los campos de una variante. + * + * Esta función valida los diferentes campos de un objeto `variante` asegurándose de que cumplan con los tipos y restricciones especificadas. Si algún campo no cumple con las condiciones, se devuelve un objeto de error con un mensaje específico. Si todos los campos son válidos, se retorna `null`. + * + * @param {object} variante - El objeto que representa una variante a validar. + * @param {string} variante.nombreVariante - El nombre de la variante, debe ser una cadena de texto de máximo 100 caracteres. + * @param {string|null} variante.descripcion - La descripción de la variante, debe ser una cadena de texto de máximo 1000 caracteres o `null`. + * + * @returns {object|null} Un objeto con una propiedad `error` si hay un error de validación, o `null` si todos los campos son válidos. + * @example + * const variante = { + * nombreVariante: 'Tamaño L', + * descripcion: 'Variante para talla grande', + * }; + * + * const resultado = validarVariante(variante); + * console.log(resultado); // null si todo está bien, o un objeto de error si algo es inválido + */ +// prettier-ignore +module.exports = (variante) => { + if ( + !variante.nombreVariante + || typeof variante.nombreVariante !== 'string' + || variante.nombreVariante.length > 100 + ) { + return { + error: + 'nombreVariante es requerido, debe ser una cadena de texto y no exceder 100 caracteres.', + }; + } + + if ( + variante.descripcion !== null + && (typeof variante.descripcion !== 'string' || variante.descripcion.length > 1000 || variante.descripcion.trim() === '') + ) { + return { + error: 'descripcionVariante debe ser una cadena de texto y no exceder 1000 caracteres.', + }; + } + + return null; +}; diff --git a/Utilidades/Intermediarios/autorizarToken.js b/Utilidades/Intermediarios/autorizarToken.js new file mode 100644 index 00000000..09b26ed4 --- /dev/null +++ b/Utilidades/Intermediarios/autorizarToken.js @@ -0,0 +1,44 @@ +const jwt = require('jsonwebtoken'); +const MENSAJES_AUTENTICACION = require('@altertex/util/const/mensajesAutenticacion'); + +/** + * Middleware que verifica la validez del token JWT en las cookies del cliente. + * + * Si el token no está presente, ha expirado o no es válido, se envía un error apropiado. + * + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @param {Express.NextFunction} next - Función para continuar con el siguiente middleware. + * @returns {Promise} Middleware de Express para autenticación por token. + */ +module.exports = async (req, res, next) => { + const token = req.cookies.token; + + if (!token) { + return res + .status(MENSAJES_AUTENTICACION.TOKEN_NO_PROPORCIONADO.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.TOKEN_NO_PROPORCIONADO.mensaje }); + } + + try { + const verificado = jwt.verify(token, process.env.JWT_SECRET); + req.user = verificado; + next(); + } catch (error) { + if (error.name === 'TokenExpiredError') { + return res + .status(MENSAJES_AUTENTICACION.TOKEN_EXPIRADO.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.TOKEN_EXPIRADO.mensaje }); + } + + if (error.name === 'JsonWebTokenError') { + return res + .status(MENSAJES_AUTENTICACION.TOKEN_INVALIDO.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.TOKEN_INVALIDO.mensaje }); + } + + return res + .status(MENSAJES_AUTENTICACION.ERROR_VALIDAR_TOKEN.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.ERROR_VALIDAR_TOKEN.mensaje }); + } +}; diff --git a/Utilidades/Intermediarios/generarSKUAuto.js b/Utilidades/Intermediarios/generarSKUAuto.js new file mode 100644 index 00000000..d8ffb993 --- /dev/null +++ b/Utilidades/Intermediarios/generarSKUAuto.js @@ -0,0 +1,66 @@ +/** + * Limpia el texto eliminando acentos, caracteres especiales y lo convierte a mayúsculas. + * @param {string} texto - El texto a limpiar. + * @returns {string} El texto limpio. + */ +const limpiarTexto = (texto) => + (typeof texto === 'string' ? texto : '') + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-zA-Z0-9\s]/g, '') + .toUpperCase(); + +/** + * Obtiene un código basado en el texto proporcionado, usando la primera palabra que cumpla la longitud mínima. + * @param {string} texto - El texto del que se extraerá el código. + * @param {number} [longitud=3] - La longitud mínima del código a extraer. + * @returns {string} El código generado a partir del texto. + */ +const obtenerCodigo = (texto, longitud = 3) => { + const palabras = limpiarTexto(texto).split(' '); + for (const palabra of palabras) { + if (palabra.length >= longitud) return palabra.substring(0, longitud); + } + return limpiarTexto(texto).substring(0, longitud); +}; + + +/** + * Genera un SKU basado en el nombre del producto, variante y valor de opción. + * @param {string} nombreProducto - El nombre del producto. + * @param {string} nombreVariante - El nombre de la variante. + * @param {string} valorOpcion - El valor de la opción. + * @returns {string} El SKU generado. + */ +const generarSKU = (nombreProducto, nombreVariante, valorOpcion) => { + try { + const prefijo = obtenerCodigo(nombreProducto); + const codigoVariante = obtenerCodigo(nombreVariante); + const codigoOpcion = obtenerCodigo(valorOpcion); + return `${prefijo}-${codigoVariante}-${codigoOpcion}`; + } catch { + return 'SKU-ERROR'; + } +}; + +/** + * Crea una función generadora de SKUs consecutivos basada en los parámetros dados. + * @returns {function(string, string, string): string} Función que genera un SKU único e incremental. + */ +const crearGeneradorSKUConsecutivo = () => { + const contadorSKU = new Map(); + + return (nombreProducto, nombreVariante, valorOpcion) => { + const base = generarSKU(nombreProducto, nombreVariante, valorOpcion); + const actual = contadorSKU.get(base) || 0; + const siguiente = actual + 1; + contadorSKU.set(base, siguiente); + + return `${base}-${String(siguiente).padStart(3, '0')}`; + }; +}; + +module.exports = { + generarSKU, // solo base + crearGeneradorSKUConsecutivo // base + numeración incremental +}; diff --git a/Utilidades/Intermediarios/limitePeticiones.js b/Utilidades/Intermediarios/limitePeticiones.js new file mode 100644 index 00000000..57ae1e1f --- /dev/null +++ b/Utilidades/Intermediarios/limitePeticiones.js @@ -0,0 +1,54 @@ +const redis = require('@altertex/config/clienteRedis'); + +/** + * Genera una llave única para rastrear las peticiones diarias de un usuario por correo. + * + * @param {string} correo - Correo electrónico del usuario autenticado. + * @returns {string} Llave única en formato `quota:usuario::`. + */ +const obtenerLlave = (correo) => { + const fecha = new Date().toISOString().split('T')[0]; + return `quota:usuario:${correo}:${fecha}`; +}; + +const PETICIONES_MAXIMAS_DIARIAS = 1500; + +/** + * Middleware que limita la cantidad de peticiones diarias que un usuario puede hacer, + * usando su correo electrónico como identificador. + * + * Este middleware asume que `req.user.correo` ha sido definido previamente, + * por ejemplo mediante un middleware de autenticación con JWT. + * + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @param {Express.NextFunction} next - Función para pasar al siguiente middleware. + * @returns {Promise} Retorna una promesa que se resuelve al continuar la cadena de middlewares, + * o responde con un error si se excede el límite o ocurre un fallo. + */ +const limitePeticionesDiarias = async (req, res, next) => { + try { + const correo = req.user?.correo; + if (!correo) { + return res.status(401).json({ mensaje: 'Correo del usuario no encontrado.' }); + } + + const llave = obtenerLlave(correo); + const contador = await redis.incr(llave); + + if (contador === 1) { + await redis.expire(llave, 86400); // 24 horas + } + + if (contador > PETICIONES_MAXIMAS_DIARIAS) { + return res.status(429).json({ mensaje: 'Límite diario de peticiones excedido para el usuario.' }); + } + + next(); + } catch (error) { + console.error('Error en middleware de límite de peticiones:', error); + res.status(500).json({ mensaje: 'Error en el servidor.' }); + } +}; + +module.exports = limitePeticionesDiarias; diff --git a/Utilidades/Intermediarios/revisarApiKey.js b/Utilidades/Intermediarios/revisarApiKey.js new file mode 100644 index 00000000..7c46813c --- /dev/null +++ b/Utilidades/Intermediarios/revisarApiKey.js @@ -0,0 +1,31 @@ +const MENSAJES_AUTENTICACION = require('@altertex/util/const/mensajesAutenticacion'); + +/** + * Middleware que valida una API key enviada en los headers de la solicitud. + * + * Si el header especificado no contiene una clave válida, se responde con un error 401. + * + * @param {string} [nombreHeader='x-api-key'] - Nombre del header que se debe verificar. + * @returns {function(Express.Request, Express.Response, Express.NextFunction): void} Middleware de Express que valida la API key. + */ +module.exports = (nombreHeader = 'x-api-key') => { + /** + * Middleware que compara la clave del header con `process.env.API_KEY`. + * + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @param {Express.NextFunction} next - Función para continuar con el siguiente middleware. + * @returns {void} + */ + return (req, res, next) => { + const valorHeader = req.get(nombreHeader); + + if (!valorHeader || valorHeader !== process.env.API_KEY) { + return res + .status(MENSAJES_AUTENTICACION.API_KEY_INVALIDA.codigo) + .json({ mensaje: MENSAJES_AUTENTICACION.API_KEY_INVALIDA.mensaje }); + } + + next(); + }; +}; diff --git a/Utilidades/Intermediarios/validarYSanitizar.js b/Utilidades/Intermediarios/validarYSanitizar.js new file mode 100644 index 00000000..0807b29a --- /dev/null +++ b/Utilidades/Intermediarios/validarYSanitizar.js @@ -0,0 +1,46 @@ +/** + * @file validarInyeccionSQL.js + * @description Middleware para detectar posibles intentos de inyección SQL en solicitudes POST/PUT. Si se detecta, responde con un mensaje genérico. + */ + +const patronSQL = /(\b(SELECT|INSERT|DELETE|UPDATE|DROP|UNION|--|;|'|"|`)\b|\bOR\b|\bAND\b)/i; + +/** + * Verifica si un objeto contiene posibles patrones de inyección SQL. + * + * @function contieneInyeccionSQL + * @param {any} obj - Valor a analizar. Puede ser un string, objeto o arreglo. + * @returns {boolean} Retorna `true` si se detecta un patrón sospechoso, `false` en caso contrario. + */ +function contieneInyeccionSQL(obj) { + if (typeof obj === 'string') { + return patronSQL.test(obj); + } else if (Array.isArray(obj)) { + return obj.some(contieneInyeccionSQL); + } else if (typeof obj === 'object' && obj !== null) { + return Object.values(obj).some(contieneInyeccionSQL); + } + return false; +} + +/** + * Middleware que analiza el contenido de `req.body` para detectar patrones de inyección SQL. + * + * @function validarInyeccionSQL + * @param {Express.Request} req - Objeto de solicitud de Express. + * @param {Express.Response} res - Objeto de respuesta de Express. + * @param {Express.NextFunction} next - Función para continuar con la siguiente capa de middleware. + * @returns {void} No retorna nada directamente, pero responde con un 400 si se detecta inyección. + */ +function validarInyeccionSQL(req, res, next) { + const cuerpo = req.body; + + if (contieneInyeccionSQL(cuerpo)) { + return res.status(400).json({ + mensaje: 'Entrada sospechosa detectada, por favor intente de nuevo.', + }); + } + next(); +} + +module.exports = validarInyeccionSQL; diff --git a/Utilidades/Intermediarios/validarYSanitizarImagen.js b/Utilidades/Intermediarios/validarYSanitizarImagen.js new file mode 100644 index 00000000..eb44b083 --- /dev/null +++ b/Utilidades/Intermediarios/validarYSanitizarImagen.js @@ -0,0 +1,50 @@ +/** + * @file validarYSanitizarImagen.js + * @description Middleware para detectar formatos no válidos de imagen. + */ + +/** + * Retorna un middleware que valida si el archivo recibido como imagen cumple con los requisitos: + * - El archivo debe ser una imagen. + * - El formato debe ser JPG o JPEG. + * - El tamaño debe ser menor a 5MB. + * + * @function validarFormatoImagen + * @returns {function(Express.Request, Express.Response, Express.NextFunction): void} + * Middleware de validación de imagen. + */ +function validarFormatoImagen() { + return function (req, res, next) { + const imagen = req.file; + + if (imagen) { + // Validar que el archivo corresponde a una imagen. + const tipo = imagen.mimetype.split('/')[0]; + if (!tipo.match('image.*')) { + return res.status(400).json({ + mensaje: 'El archivo no corresponde a una imagen.', + }); + } + + // Validar que el formato de la imagen es uno de los aceptados. + const extension = imagen.mimetype.split('/').pop(); + if (!['jpg', 'jpeg'].includes(extension)) { + return res.status(400).json({ + mensaje: 'Formato de imagen no válido. Formatos válidos: JPG', + }); + } + + // Validar que la imagen no pese más de 5Mb + const tamano = imagen.size; + if (tamano > 5500000) { + return res.status(400).json({ + mensaje: 'Archivo muy pesado. Tamaño máximo: 5Mb.', + }); + } + } + + next(); + }; +} + +module.exports = validarFormatoImagen; diff --git a/Utilidades/Intermediarios/verificarPermisos.js b/Utilidades/Intermediarios/verificarPermisos.js new file mode 100644 index 00000000..3ab09f09 --- /dev/null +++ b/Utilidades/Intermediarios/verificarPermisos.js @@ -0,0 +1,31 @@ +const MENSAJES_AUTENTICACION = require('@altertex/util/const/mensajesAutenticacion'); + +/** + * Middleware para verificar si el usuario autenticado posee todos los permisos requeridos. + * + * @param {...string} permisosRequeridos - Lista de permisos necesarios para acceder al recurso. + * @returns {function(Express.Request, Express.Response, Express.NextFunction): void} Middleware de Express que valida los permisos del usuario. + */ +module.exports = (...permisosRequeridos) => { + return (req, res, next) => { + const usuario = req.user; + + if (!usuario || !usuario.permisos) { + return res.status(MENSAJES_AUTENTICACION.USUARIO_NO_AUTENTICADO.codigo).json({ + mensaje: MENSAJES_AUTENTICACION.USUARIO_NO_AUTENTICADO.mensaje, + }); + } + + const permisosUsuario = usuario.permisos; + + const tienePermiso = permisosRequeridos.every((permiso) => permisosUsuario.includes(permiso)); + + if (!tienePermiso) { + return res.status(MENSAJES_AUTENTICACION.ACCESO_NO_AUTORIZADO.codigo).json({ + mensaje: MENSAJES_AUTENTICACION.ACCESO_NO_AUTORIZADO.mensaje, + }); + } + + next(); + }; +}; diff --git a/Utilidades/Servicios/correrQuery.js b/Utilidades/Servicios/correrQuery.js new file mode 100644 index 00000000..a2f5a55f --- /dev/null +++ b/Utilidades/Servicios/correrQuery.js @@ -0,0 +1,23 @@ +const conexion = require('@altertex/util/bd/db'); + +/** + * Ejecuta una consulta SQL utilizando la conexión a la base de datos. + * + * @async + * @function + * @param {string} query - Consulta SQL a ejecutar. + * @param {Array} [params=[]] - Parámetros para la consulta preparada. + * @returns {Promise} Promesa que se resuelve con los resultados de la consulta o se rechaza con un error. + * + * @example + * const resultados = await runQuery('SELECT * FROM usuarios WHERE id = ?', [1]); + */ +module.exports = async (query, params = []) => { + try { + const [results] = await conexion.query(query, params); + return results; + } catch (err) { + console.error('Error al ejecutar la consulta:', err); + throw err; + } +}; \ No newline at end of file diff --git a/Utilidades/Servicios/eliminarImagenS3.js b/Utilidades/Servicios/eliminarImagenS3.js new file mode 100644 index 00000000..533fc56b --- /dev/null +++ b/Utilidades/Servicios/eliminarImagenS3.js @@ -0,0 +1,34 @@ +// Importaciones específicas del AWS SDK v3 +const { S3Client, DeleteObjectCommand } = require('@aws-sdk/client-s3'); + +// Crear una instancia del cliente de S3 +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +/** + * Elimina una imagen de Amazon S3. + * + * @param {string} folder - Carpeta dentro del bucket (ej. "productos/"). + * @param {string} filename - Nombre del archivo a eliminar. + * @returns {Promise} Lanza un error si la eliminación falla. + * @throws {Error} Si ocurre un problema al eliminar el archivo de S3. + */ +const eliminarImagenS3 = async (folder, filename) => { + const params = { + Bucket: process.env.AWS_BUCKET_NAME, + Key: `${folder}${filename}`, + }; + + try { + await s3.send(new DeleteObjectCommand(params)); + } catch (error) { + throw new Error(`Error al eliminar la imagen "${folder}${filename}" de S3: ${error.message}`); + } +}; + +module.exports = eliminarImagenS3; diff --git a/Utilidades/Servicios/enviarS3.js b/Utilidades/Servicios/enviarS3.js new file mode 100644 index 00000000..3cf09461 --- /dev/null +++ b/Utilidades/Servicios/enviarS3.js @@ -0,0 +1,31 @@ +const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3'); + +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +/** + * Carga un archivo en un bucket de Amazon S3 y devuelve la URL pública del archivo cargado. + * Utiliza el cliente de AWS SDK para enviar el archivo al bucket S3 configurado. + * + * @async + * @function subirArchivoS3 + * @param {object} parametros - Los parámetros necesarios para cargar el archivo en S3. + * @param {string} parametros.Bucket - El nombre del bucket S3 donde se almacenará el archivo. + * @param {string} parametros.Key - El nombre del archivo que se almacenará en S3. + * @param {Buffer|Uint8Array|Blob|string} parametros.Body - El contenido del archivo a cargar. + * @param {string} [parametros.ContentType] - El tipo de contenido del archivo (opcional). + * + * @returns {Promise} La URL pública del archivo cargado en S3. + * + * @throws {Error} - Si ocurre un error al cargar el archivo en S3 o al obtener la URL. + */ +module.exports = async (parametros) => { + await s3.send(new PutObjectCommand(parametros)); + const nombreArchivo = parametros.Key; + return nombreArchivo; +}; diff --git a/Utilidades/Servicios/extraerNombreArchivoS3.js b/Utilidades/Servicios/extraerNombreArchivoS3.js new file mode 100644 index 00000000..1149bcbc --- /dev/null +++ b/Utilidades/Servicios/extraerNombreArchivoS3.js @@ -0,0 +1,12 @@ +/** + * Extrae el nombre real del archivo desde una URL firmada de S3. + * @param {string} url - URL completa del archivo en S3. + * @returns {string} Nombre del archivo con extensión, sin parámetros. + */ +const extraerNombreArchivoS3 = (url) => { + const partes = url.split('/'); + const ultimaParte = partes[partes.length - 1]; + return ultimaParte.split('?')[0]; // Elimina query params + }; + + module.exports = extraerNombreArchivoS3; \ No newline at end of file diff --git a/Utilidades/Servicios/generarNombreUnico.js b/Utilidades/Servicios/generarNombreUnico.js new file mode 100644 index 00000000..81e64dc4 --- /dev/null +++ b/Utilidades/Servicios/generarNombreUnico.js @@ -0,0 +1,22 @@ +const crypto = require('crypto'); +const path = require('path'); + +/** + * Genera un nombre de archivo único basado en la fecha actual y un hash aleatorio. + * + * Esta función utiliza la fecha actual (en milisegundos desde la época Unix) y una cadena + * aleatoria generada con `crypto.randomBytes`, preservando la extensión original del archivo. + * + * @function generarNombreArchivo + * @param {string} [nombreOriginal=""] - El nombre original del archivo, utilizado para conservar la extensión. + * + * @returns {string} Un nuevo nombre de archivo único, con la misma extensión que el original. + * + * @example + * const nombre = generarNombreArchivo("foto.png"); + */ +module.exports = (nombreOriginal = '') => { + const ext = path.extname(nombreOriginal); + const randomBytes = crypto.randomBytes(16).toString('hex'); + return `${Date.now()}-${randomBytes}${ext}`; +}; diff --git a/Utilidades/Servicios/obtenerImagenCliente.js b/Utilidades/Servicios/obtenerImagenCliente.js new file mode 100644 index 00000000..0d8947a3 --- /dev/null +++ b/Utilidades/Servicios/obtenerImagenCliente.js @@ -0,0 +1,49 @@ +const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); +const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); + +const s3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +/** + * Obtiene URL firmadas temporalmente para acceder a una imagen almacenada en S3. + * + * Esta función toma un nombre de archivo, busca las imágenes + * dentro de esa carpeta indicadas por su `urlImagen`, y reemplaza la ruta por una URL + * firmada y válida por una hora, generadas con AWS S3. + * + * @async + * @function obtenerImagenCliente + * @param {string} nombreImagen - Nombre de la imagen para obtener URL de S3. + * + * @returns {string} imagenUrl + * + * @throws {Error} - Si ocurre un error al obtener la imagen desde S3. + */ +async function obtenerImagenCliente(nombreImagen) { + if (!nombreImagen) { + return null; // O lanzar un error si prefieres + } + + try { + const parametrosImagen = { + Bucket: process.env.AWS_BUCKET_NAME, + Key: `clientes/${nombreImagen}`, + }; + + // El tiempo de expiración en segundos (ejemplo: 1 hora) + const imagenUrl = await getSignedUrl(s3, new GetObjectCommand(parametrosImagen), { + expiresIn: 3600, + }); + + return imagenUrl; + } catch { + throw new Error('Error fetching user image from S3'); + } +} + +module.exports = obtenerImagenCliente; diff --git a/Utilidades/Servicios/obtenerImagenFolder.js b/Utilidades/Servicios/obtenerImagenFolder.js new file mode 100644 index 00000000..e05b9077 --- /dev/null +++ b/Utilidades/Servicios/obtenerImagenFolder.js @@ -0,0 +1,62 @@ +const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3'); +const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); + +const clienteS3 = new S3Client({ + region: process.env.AWS_REGION, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID, + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, + }, +}); + +/** + * Obtiene URLs firmadas temporalmente para acceder a imágenes almacenadas en S3. + * + * Esta función toma un objeto `request` y un nombre de carpeta, busca las imágenes + * dentro de esa carpeta indicadas por su `urlImagen`, y reemplaza esas rutas por URLs + * firmadas válidas por una hora, generadas con AWS S3. + * + * @async + * @function obtenerImagenFolder + * @param {object} request - Objeto que contiene los datos con las rutas de imágenes a firmar. + * @param {string} nombreFolder - Nombre del campo dentro del objeto `request` que contiene el arreglo con las rutas de imágenes. El último carácter (`/`) será eliminado. + * + * @returns {Promise>} Un array con los mismos objetos del array original, pero con la propiedad `urlImagen` reemplazada por la URL firmada o `null`. + * + * @throws {Error} - Si los datos del `request` no son válidos o si ocurre un error al obtener la imagen desde S3. + */ +async function obtenerImagenFolder(request, nombreFolder) { + nombreFolder = nombreFolder.slice(0, -1); + const Json = request[nombreFolder]; + + if (!Json || !Array.isArray(Json)) { + throw new Error('Invalid request data'); + } + + try { + const jsonActualizado = await Promise.all( + Json.map(async (folder) => { + if (folder.urlImagen) { + const comando = new GetObjectCommand({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: `${nombreFolder}/${folder.urlImagen}`, + }); + + folder.urlImagen = await getSignedUrl(clienteS3, comando, { + expiresIn: 60 * 60, + }); + } else { + folder.urlImagen = null; + } + + return folder; + }) + ); + + return jsonActualizado; + } catch { + throw new Error('Error obteniendo imagen de S3'); + } +} + +module.exports = obtenerImagenFolder; diff --git a/Utilidades/Servicios/subirImagen.js b/Utilidades/Servicios/subirImagen.js new file mode 100644 index 00000000..49b1bb6b --- /dev/null +++ b/Utilidades/Servicios/subirImagen.js @@ -0,0 +1,29 @@ +const subirArchivo = require('@altertex/util/ser/enviarS3'); + +/** + * Sube un archivo al bucket de AWS S3 con un nombre y ruta específicos. + * + * @async + * @function + * @param {object} file - Archivo a subir. Debe contener las propiedades `buffer` y `mimetype`. + * @param {string} route - Ruta dentro del bucket donde se almacenará la imagen (por ejemplo, 'clientes'). + * @param {string} name - Nombre base del archivo (sin extensión). Se le añadirá `.jpg` automáticamente. + * @returns {Promise} Retorna el resultado de la operación de subida a S3. + * @throws {Error} Lanza un error si ocurre un fallo al subir el archivo. + */ +module.exports = async (file, route, name) => { + if (file) { + const fileName = `${route}/${name}.jpg`; + try { + return await subirArchivo({ + Bucket: process.env.AWS_BUCKET_NAME, + Key: fileName, + Body: file.buffer, + ContentType: file.mimetype, + }); + } catch (error) { + console.error('Error al subir imagen: ', error); + throw new Error('Error al subir imagen'); + } + } +}; diff --git a/Utilidades/Servicios/verificarCodigo2FA.servicio.js b/Utilidades/Servicios/verificarCodigo2FA.servicio.js new file mode 100644 index 00000000..31829286 --- /dev/null +++ b/Utilidades/Servicios/verificarCodigo2FA.servicio.js @@ -0,0 +1,32 @@ +// @file verificarCodigo2FA.servicio.js + +const speakeasy = require('speakeasy'); +const db = require('@altertex/util/bd/db'); + +/** + * Verifica un código TOTP contra el secreto almacenado de un usuario. + * @param {number} idUsuario - ID del usuario que ejecuta la acción + * @param {string} codigo - Código TOTP ingresado (6 dígitos) + * @returns {Promise} true si el código es válido + */ +const verificarCodigo2FA = async (idUsuario, codigo) => { + const [resultado] = await db.query( + 'SELECT secret2FA FROM usuarios_2fa WHERE idUsuario = ? AND tiene2FA = true', + [idUsuario] + ); + + if (!resultado || resultado.length === 0) { + return false; + } + + const { secret2FA } = resultado[0]; + + return speakeasy.totp.verify({ + secret: secret2FA, + encoding: 'base32', + token: codigo, + window: 1 + }); +}; + +module.exports = { verificarCodigo2FA }; \ No newline at end of file diff --git a/_tests_/Categorias/consultarListaCategorias.controller.test.js b/_tests_/Categorias/consultarListaCategorias.controller.test.js new file mode 100644 index 00000000..c6e4b246 --- /dev/null +++ b/_tests_/Categorias/consultarListaCategorias.controller.test.js @@ -0,0 +1,89 @@ +/** + * RF[47] Consulta Lista de Categorías - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF47 + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/cat/repos/repositorioConsultarListaCategorias', () => ({ + consultarListaCategorias: jest.fn(), +})); + +// Importar después del mock +const controlador = require('@altertex/cat/ctrl/consultarListaCategorias.controller'); +const repositorio = require('@altertex/cat/repos/repositorioConsultarListaCategorias'); +const MENSAJES_CATEGORIAS = require('@altertex/util/const/mensajesCategorias'); + +describe('Controlador consultarListaCategorias', () => { + let req; + let res; + + beforeEach(() => { + jest.clearAllMocks(); + + req = { + user: { + clienteSeleccionado: '3', + }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + jest.spyOn(console, 'error').mockImplementation(() => {}); // Evita logs + }); + + // Escenario 1: No se encuentran categorías + test('Debe retornar mensaje cuando no hay categorías encontradas', async () => { + repositorio.consultarListaCategorias.mockResolvedValue([]); + + await controlador.consultarListaCategorias(req, res); + + expect(repositorio.consultarListaCategorias).toHaveBeenCalledWith(3); + expect(res.status).toHaveBeenCalledWith(MENSAJES_CATEGORIAS.CATEGORIAS_NO_ENCONTRADAS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CATEGORIAS.CATEGORIAS_NO_ENCONTRADAS.mensaje, + }); + }); + + // Escenario 2: Lista de categorías obtenida correctamente + test('Debe retornar la lista de categorías cuando se obtienen con éxito', async () => { + const mockCategorias = [ + { + idCategoria: 1, + nombre: 'Camisas', + productos: ['Camisa A', 'Camisa B'], + }, + { + idCategoria: 2, + nombre: 'Pantalones', + productos: ['Pantalón X'], + }, + ]; + + repositorio.consultarListaCategorias.mockResolvedValue(mockCategorias); + + await controlador.consultarListaCategorias(req, res); + + expect(repositorio.consultarListaCategorias).toHaveBeenCalledWith(3); + expect(res.status).toHaveBeenCalledWith(MENSAJES_CATEGORIAS.LISTA_CATEGORIAS_OBTENIDA.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CATEGORIAS.LISTA_CATEGORIAS_OBTENIDA.mensaje, + listaCategoria: mockCategorias, + }); + }); + + // Escenario 3: Error inesperado en el repositorio + test('Debe manejar errores si falla el repositorio', async () => { + repositorio.consultarListaCategorias.mockRejectedValue( + new Error('Error de base de datos') + ); + + await controlador.consultarListaCategorias(req, res); + + expect(repositorio.consultarListaCategorias).toHaveBeenCalledWith(3); + expect(res.status).toHaveBeenCalledWith(MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIAS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CATEGORIAS.ERROR_OBTENER_CATEGORIAS.mensaje, + }); + }); +}); \ No newline at end of file diff --git a/_tests_/Clientes/eliminarCliente.controller.test.js b/_tests_/Clientes/eliminarCliente.controller.test.js new file mode 100644 index 00000000..22b47d57 --- /dev/null +++ b/_tests_/Clientes/eliminarCliente.controller.test.js @@ -0,0 +1,92 @@ +/** + * RF[15] Eliminar Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF15 + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/cli/repos/repositorioEliminarCliente', () => ({ + eliminarClientePorId: jest.fn(), +})); +jest.mock('@altertex/util/ser/eliminarImagenS3', () => jest.fn()); +jest.mock('@altertex/util/ser/extraerNombreArchivoS3', () => jest.fn()); +jest.mock('@altertex/util/ser/correrQuery', () => jest.fn()); + +const controlador = require('@altertex/cli/ctrl/eliminarCliente.controller'); +const repositorio = require('@altertex/cli/repos/repositorioEliminarCliente'); +const eliminarImagenS3 = require('@altertex/util/ser/eliminarImagenS3'); +const extraerNombreArchivoS3 = require('@altertex/util/ser/extraerNombreArchivoS3'); +const correrQuery = require('@altertex/util/ser/correrQuery'); +const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes'); + +describe('Controlador eliminarCliente', () => { + let req; + let res; + + beforeEach(() => { + jest.clearAllMocks(); + + req = { + body: { idCliente: '3' }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + // Escenario 1: ID inválido + test('Debe retornar error si el ID de cliente es inválido', async () => { + req.body.idCliente = ' '; + + await controlador.eliminarCliente(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_INVALIDO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.CLIENTE_INVALIDO.mensaje, + }); + }); + + // Escenario 2: Cliente no encontrado + test('Debe retornar error si el cliente no existe', async () => { + correrQuery.mockResolvedValue([]); + repositorio.eliminarClientePorId.mockResolvedValue({ affectedRows: 0 }); + + await controlador.eliminarCliente(req, res); + + expect(repositorio.eliminarClientePorId).toHaveBeenCalledWith(3); + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.mensaje, + }); + }); + + // Escenario 3: Cliente eliminado correctamente con imagen + test('Debe eliminar al cliente y su imagen si existe', async () => { + correrQuery.mockResolvedValue([{ urlImagen: 'https://bucket.s3/cliente123.jpg' }]); + extraerNombreArchivoS3.mockReturnValue('cliente123.jpg'); + repositorio.eliminarClientePorId.mockResolvedValue({ affectedRows: 1 }); + + await controlador.eliminarCliente(req, res); + + expect(correrQuery).toHaveBeenCalled(); + expect(extraerNombreArchivoS3).toHaveBeenCalledWith('https://bucket.s3/cliente123.jpg'); + expect(eliminarImagenS3).toHaveBeenCalledWith('clientes/', 'cliente123.jpg'); + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_ELIMINADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.CLIENTE_ELIMINADO.mensaje, + }); + }); + + // Escenario 4: Error inesperado + test('Debe manejar errores inesperados en el try/catch', async () => { + correrQuery.mockRejectedValue(new Error('DB error')); + + await controlador.eliminarCliente(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.ERROR_ELIMINAR_CLIENTE.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.ERROR_ELIMINAR_CLIENTE.mensaje, + }); + }); +}); diff --git a/_tests_/Clientes/leerCliente.controller.test.js b/_tests_/Clientes/leerCliente.controller.test.js new file mode 100644 index 00000000..953b7318 --- /dev/null +++ b/_tests_/Clientes/leerCliente.controller.test.js @@ -0,0 +1,124 @@ +/** + * RF[13] Leer Cliente - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/rf13/ + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/cli/repos/repositorioLeerCliente', () => ({ + obtenerClientePorId: jest.fn(), +})); +jest.mock('@altertex/util/ser/obtenerImagenCliente', () => jest.fn()); + +const controlador = require('@altertex/cli/ctrl/leerCliente.controller'); +const repositorio = require('@altertex/cli/repos/repositorioLeerCliente'); +const obtenerImagenCliente = require('@altertex/util/ser/obtenerImagenCliente'); +const MENSAJES_CLIENTES = require('@altertex/util/const/mensajesClientes'); + +describe('Controlador leerCliente', () => { + let req; + let res; + + beforeEach(() => { + jest.clearAllMocks(); + + req = { + body: { idCliente: '7' }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + // Escenario 1: ID inválido + test('Debe retornar error si el ID es inválido', async () => { + req.body.idCliente = ' '; + + await controlador.leerCliente(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.PARAMETROS_INVALIDOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.PARAMETROS_INVALIDOS.mensaje, + }); + }); + + // Escenario 2: Cliente no encontrado + test('Debe retornar error si el cliente no existe', async () => { + repositorio.obtenerClientePorId.mockResolvedValue(null); + + await controlador.leerCliente(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.CLIENTE_NO_ENCONTRADO.mensaje, + }); + }); + + // Escenario 3: Cliente encontrado con imagen exitosa + test('Debe retornar cliente completo con imagen si todo funciona bien', async () => { + const mockCliente = { + idCliente: 7, + nombreLegal: 'Tech S.A.', + nombreVisible: 'Tech Store', + empleados: [], + usuariosAsignados: [], + numeroEmpleados: 10, + urlImagen: 'https://bucket.s3/cliente.jpg', + }; + + repositorio.obtenerClientePorId.mockResolvedValue(mockCliente); + obtenerImagenCliente.mockResolvedValue('https://signed-url.com/cliente.jpg'); + + await controlador.leerCliente(req, res); + + expect(obtenerImagenCliente).toHaveBeenCalledWith(mockCliente.urlImagen); + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.CONSULTA_EXITOSA.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.CONSULTA_EXITOSA.mensaje, + cliente: { + ...mockCliente, + imagenCliente: 'https://signed-url.com/cliente.jpg', + }, + }); + }); + + // Escenario 4: Cliente encontrado, pero falla obtenerImagenCliente + test('Debe retornar imagen placeholder si obtenerImagenCliente falla', async () => { + const mockCliente = { + idCliente: 7, + nombreLegal: 'Tech S.A.', + nombreVisible: 'Tech Store', + empleados: [], + usuariosAsignados: [], + numeroEmpleados: 10, + urlImagen: 'https://bucket.s3/cliente.jpg', + }; + + repositorio.obtenerClientePorId.mockResolvedValue(mockCliente); + obtenerImagenCliente.mockRejectedValue(new Error('Fallo S3')); + + await controlador.leerCliente(req, res); + + expect(obtenerImagenCliente).toHaveBeenCalledWith(mockCliente.urlImagen); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.CONSULTA_EXITOSA.mensaje, + cliente: { + ...mockCliente, + imagenCliente: '/placeholder.png', + }, + }); + }); + + // Escenario 5: Error inesperado + test('Debe manejar error inesperado del repositorio', async () => { + repositorio.obtenerClientePorId.mockRejectedValue(new Error('DB error')); + + await controlador.leerCliente(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_CLIENTES.ERROR_CONSULTAR_CLIENTE.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_CLIENTES.ERROR_CONSULTAR_CLIENTE.mensaje, + }); + }); +}); \ No newline at end of file diff --git a/_tests_/Cuotas/Controladores/crearCuota.controller.test.js b/_tests_/Cuotas/Controladores/crearCuota.controller.test.js new file mode 100644 index 00000000..5104d937 --- /dev/null +++ b/_tests_/Cuotas/Controladores/crearCuota.controller.test.js @@ -0,0 +1,103 @@ +/** + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/cuota/ctrl/validarCuotaSet', () => ({ + validarCuotaSet: jest.fn(), +})); +jest.mock('@altertex/cuota/repos/crearCuotaRepositorio', () => ({ + crearCuota: jest.fn(), +})); + +// Importar después de los mocks +const controladorCrearCuota = require('@altertex/cuota/ctrl/crearCuota.controller'); +const { validarCuotaSet } = require('@altertex/cuota/ctrl/validarCuotaSet'); +const { crearCuota } = require('@altertex/cuota/repos/crearCuotaRepositorio'); + +describe('Controlador de Crear Cuota', () => { + let req; + let res; + let originalDate; + + const dataMock = { + nombre: 'Plan Básico', + descripcion: 'Plan básico para clientes nuevos', + periodoRenovacion: 'mensual', + renovacionHabilitada: true, + productosYLimite: [ + { idProducto: 1, limite: 100, limiteActual: 100 }, + { idProducto: 2, limite: 50, limiteActual: 50 }, + ], + idCliente: 102, + }; + + beforeEach(() => { + jest.clearAllMocks(); + + // Guardar la Date original + originalDate = global.Date; + + // Mock de fecha + const mockDate = new Date('2023-05-15T00:00:00Z'); + global.Date = class extends Date { + constructor() { + super(); + return mockDate; + } + }; + + // Mock de req y res + req = { + body: dataMock, + user: { + clienteSeleccionado: 102, // o el valor necesario para el controlador + }, + }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + }); + + afterEach(() => { + // Restaurar Date original + global.Date = originalDate; + }); + + test('Debe crear un cuota set exitosamente', async () => { + const cuotaSetIdMock = 123; + crearCuota.mockResolvedValue(cuotaSetIdMock); + + await controladorCrearCuota.crearCuota(req, res); + + expect(validarCuotaSet).toHaveBeenCalledWith(dataMock.nombre, dataMock.productosYLimite, res); + + expect(crearCuota).toHaveBeenCalledWith({ + ...dataMock, + ultimaActualizacion: '2023-05-15', + }); + + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith({ + exito: 'Cuota set creado exitosamente', + }); + }); + + test('Debe manejar errores de validación', async () => { + // Simula que validarCuotaSet lanza un error + validarCuotaSet.mockImplementation(() => { + throw new Error('Error de validación'); + }); + + await controladorCrearCuota.crearCuota(req, res); + + expect(validarCuotaSet).toHaveBeenCalled(); + expect(crearCuota).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ + error: 'Error creando cuota set', + detalle: expect.stringContaining('Error de validación'), + }) + ); + }); +}); diff --git a/_tests_/Empleados/exportarEmpleados.controller.test.js b/_tests_/Empleados/exportarEmpleados.controller.test.js new file mode 100644 index 00000000..3dd61936 --- /dev/null +++ b/_tests_/Empleados/exportarEmpleados.controller.test.js @@ -0,0 +1,106 @@ +/** + * RF[59] Exportar Empleados - https://codeandco-wiki.netlify.app/docs/next/proyectos/textiles/documentacion/requisitos/RF59 + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/emp/repos/repositorioExportarEmpleado', () => ({ + obtenerEmpleadosExportacion: jest.fn(), +})); + +const controlador = require('@altertex/emp/ctrl/exportarEmpleados.controller'); +const repositorio = require('@altertex/emp/repos/repositorioExportarEmpleado'); +const MENSAJES_EMPLEADOS = require('@altertex/util/const/mensajesEmpleados'); + +describe('Controlador exportarEmpleados', () => { + let req; + let res; + + beforeEach(() => { + jest.clearAllMocks(); + + req = { + user: { clienteSeleccionado: '5' }, + body: { idsEmpleado: [1, 2] }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + // Escenario 1: No se envían IDs + test('Debe retornar error si no se envían empleados a exportar', async () => { + req.body.idsEmpleado = []; + + await controlador.exportarEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.PARAMETROS_INVALIDOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: 'Debes seleccionar al menos un empleado para exportar.', + }); + }); + + // Escenario 2: No se encuentran empleados en la base + test('Debe retornar mensaje si no hay empleados encontrados', async () => { + repositorio.obtenerEmpleadosExportacion.mockResolvedValue([]); + + await controlador.exportarEmpleados(req, res); + + expect(repositorio.obtenerEmpleadosExportacion).toHaveBeenCalledWith(5, [1, 2]); + expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_EMPLEADOS.EMPLEADOS_NO_ENCONTRADOS.mensaje, + csv: '', + }); + }); + + // Escenario 3: Exportación exitosa + test('Debe retornar CSV con empleados exportados correctamente', async () => { + const empleadosMock = [ + { + idEmpleado: 1, + nombreCompleto: 'Juan Pérez', + correoElectronico: 'juan@example.com', + numeroTelefono: '1234567890', + direccion: 'Calle Falsa 123', + fechaNacimiento: '1990-01-01', + genero: 'M', + estatus: 'Activo', + numeroEmergencia: '0987654321', + areaTrabajo: 'Ventas', + posicion: 'Ejecutivo', + cantidadPuntos: 200, + antiguedad: '2020-01-01', + }, + ]; + + repositorio.obtenerEmpleadosExportacion.mockResolvedValue(empleadosMock); + + await controlador.exportarEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.codigo); + const csvLlamado = res.json.mock.calls[0][0].csv; + + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_EMPLEADOS.LISTA_EMPLEADOS_EXPORTADA.mensaje, + csv: expect.stringContaining('Juan Pérez'), // Verifica contenido parcial + }); + + expect(csvLlamado.startsWith('\uFEFF')).toBe(true); // CSV debe tener BOM + }); + + // Escenario 4: Error inesperado + test('Debe manejar errores del repositorio', async () => { + repositorio.obtenerEmpleadosExportacion.mockRejectedValue(new Error('Error de DB')); + + await controlador.exportarEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_EMPLEADOS.ERROR_EXPORTAR_EMPLEADOS.mensaje, + csv: '', + }); + }); +}); \ No newline at end of file diff --git a/_tests_/Empleados/leerGrupoEmpleados.controller.test.js b/_tests_/Empleados/leerGrupoEmpleados.controller.test.js new file mode 100644 index 00000000..d9e6f33b --- /dev/null +++ b/_tests_/Empleados/leerGrupoEmpleados.controller.test.js @@ -0,0 +1,98 @@ +/** + * RF[23] Lee grupo de empleados - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF23 + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/emp/repos/repositorioLeerGrupoDeEmpleados', () => ({ + obtenerGrupoEmpleadosPorId: jest.fn(), +})); + +// Importar después de los mocks +const controladorLeerGrupoEmpleados = require('@altertex/emp/ctrl/leerGrupoEmpleados.controller'); +const repositorio = require('@altertex/emp/repos/repositorioLeerGrupoDeEmpleados'); +const MENSAJES_GRUPO_EMPLEADOS = require('@altertex/util/const/mensajesGrupoEmpleados'); + +describe('Controlador de Leer Grupo de Empleados', () => { + let req; + let res; + + beforeEach(() => { + jest.clearAllMocks(); + + // Mock de req y res + req = { + body: { + idGrupo: 5, + }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + // Evitar mostrar errores en consola durante las pruebas + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + // Escenario 1: ID de grupo inválido + test('Debe retornar error cuando el ID de grupo es inválido', async () => { + req.body.idGrupo = 'invalido'; + + await controladorLeerGrupoEmpleados.leerGrupoEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_GRUPO_EMPLEADOS.PARAMETROS_INVALIDOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.PARAMETROS_INVALIDOS.mensaje, + }); + }); + + // Escenario 2: Grupo no encontrado en la base de datos + test('Debe retornar error cuando el grupo no es encontrado', async () => { + repositorio.obtenerGrupoEmpleadosPorId.mockResolvedValue(null); + + await controladorLeerGrupoEmpleados.leerGrupoEmpleados(req, res); + + expect(repositorio.obtenerGrupoEmpleadosPorId).toHaveBeenCalledWith(5); + expect(res.status).toHaveBeenCalledWith(MENSAJES_GRUPO_EMPLEADOS.GRUPO_NO_ENCONTRADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.GRUPO_NO_ENCONTRADO.mensaje, + }); + }); + + // Escenario 3: Grupo encontrado exitosamente + test('Debe retornar el grupo cuando es encontrado exitosamente', async () => { + const mockGrupo = { + idGrupo: 5, + nombre: 'Grupo de Ventas', + descripcion: 'Equipo de ventas regional', + setsProductos: ['Producto A', 'Producto B'], + empleados: ['Juan Pérez', 'Ana García'], + }; + + repositorio.obtenerGrupoEmpleadosPorId.mockResolvedValue(mockGrupo); + + await controladorLeerGrupoEmpleados.leerGrupoEmpleados(req, res); + + expect(repositorio.obtenerGrupoEmpleadosPorId).toHaveBeenCalledWith(5); + expect(res.status).toHaveBeenCalledWith(MENSAJES_GRUPO_EMPLEADOS.GRUPO_OBTENIDO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.GRUPO_OBTENIDO.mensaje, + grupoEmpleados: mockGrupo, + }); + }); + + // Escenario 4: Error al obtener el grupo (excepción en el repositorio) + test('Debe manejar errores en la consulta al repositorio', async () => { + repositorio.obtenerGrupoEmpleadosPorId.mockRejectedValue( + new Error('Error en la base de datos') + ); + + await controladorLeerGrupoEmpleados.leerGrupoEmpleados(req, res); + + expect(repositorio.obtenerGrupoEmpleadosPorId).toHaveBeenCalledWith(5); + expect(res.status).toHaveBeenCalledWith(MENSAJES_GRUPO_EMPLEADOS.ERROR_OBTENER_GRUPO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_GRUPO_EMPLEADOS.ERROR_OBTENER_GRUPO.mensaje, + }); + }); +}); diff --git a/_tests_/Eventos/Controladores/crearEvento.controller.test.js b/_tests_/Eventos/Controladores/crearEvento.controller.test.js new file mode 100644 index 00000000..68c0613d --- /dev/null +++ b/_tests_/Eventos/Controladores/crearEvento.controller.test.js @@ -0,0 +1,185 @@ +// Mock del repositorio +jest.mock('@altertex/eve/repos/repositorioCrearEvento', () => ({ + crearEvento: jest.fn(), +})); + +// Función a probar +const { crearEvento } = require('@altertex/eve/ctrl/crearEvento.controller'); +const repositorio = require('@altertex/eve/repos/repositorioCrearEvento'); // Mock del repositorio +const MENSAJES_EVENTOS = require('@altertex/util/const/mensajesEventos'); + +// Pruebas +describe('Controlador de Crear Evento', () => { + let req; + let res; + + beforeEach(() => { + // Reset de los mocks + jest.clearAllMocks(); + + // Mock de req y res + req = { + body: {}, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + cookie: jest.fn(), + }; + }); + + test('Campos requeridos vacíos', async () => { + // Arrange - Solo faltan los campos requeridos + req.body = { + idCliente: '', + nombre: '', + descripcion: '', // Opcional - está bien que esté vacío + puntos: '', + multiplicador: '', + periodoRenovacion: '', // Opcional - está bien que esté vacío + renovacion: false, + }; + + // Act + await crearEvento(req, res); + + // Assert + expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo, + mensaje: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.mensaje, + }); + }); + + test('Evento con campos opcionales vacíos', async () => { + // Arrange - Campos requeridos llenos, opcionales vacíos + req.body = { + idCliente: '1', + nombre: 'Evento de prueba', + descripcion: '', // Campo opcional vacío + puntos: '100', + multiplicador: '1.5', + periodoRenovacion: '', // Campo opcional vacío + renovacion: true, + }; + + const eventoCreado = { + id: 1, + nombre: 'Evento de prueba' + }; + + repositorio.crearEvento.mockResolvedValue({ evento: eventoCreado }); + + // Act + await crearEvento(req, res); + + // Assert + expect(repositorio.crearEvento).toHaveBeenCalledWith({ + idCliente: 1, + nombre: 'Evento de prueba', + descripcion: null, // Se convierte a null cuando está vacío + puntos: 100, + multiplicador: 1.5, + periodoRenovacion: null, // Se convierte a null cuando está vacío + renovacion: 1, + }); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.EVENTO_CREADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + codigo: MENSAJES_EVENTOS.EVENTO_CREADO.codigo, + mensaje: MENSAJES_EVENTOS.EVENTO_CREADO.mensaje, + evento: eventoCreado, + }); + }); + + test('Campos inválidos', async () => { + // Arrange + req.body = { + idCliente: 'abc', + nombre: 'Evento de prueba', + descripcion: 'Descripción del evento', + puntos: 'cien', + multiplicador: 'uno punto cinco', + periodoRenovacion: 'mensual', + renovacion: 'sí', + }; + + // Act + await crearEvento(req, res); + + // Assert + expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + codigo: MENSAJES_EVENTOS.PARAMETROS_INVALIDOS.codigo, + mensaje: 'El ID del cliente debe ser un número válido mayor a 0.', + }); + }); + + test('Cliente inexistente (error del repositorio)', async () => { + // Arrange + req.body = { + idCliente: '999', + nombre: 'Evento de prueba', + descripcion: 'Descripción del evento', + puntos: '100', + multiplicador: '1.5', + periodoRenovacion: 'mensual', + renovacion: true, + }; + + // Simulamos un error específico del repositorio + repositorio.crearEvento.mockRejectedValue(new Error(MENSAJES_EVENTOS.ERROR_CLIENTE_NO_EXISTE.mensaje)); + + // Act + await crearEvento(req, res); + + // Assert + expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo); + expect(res.json).toHaveBeenCalledWith({ + codigo: MENSAJES_EVENTOS.ERROR_CREAR_EVENTO.codigo, + mensaje: MENSAJES_EVENTOS.ERROR_CLIENTE_NO_EXISTE.mensaje, + }); + }); + + test('Evento creado exitosamente', async () => { + // Arrange + req.body = { + idCliente: '1', + nombre: 'Evento de prueba', + descripcion: 'Descripción del evento', + puntos: '100', + multiplicador: '1.5', + periodoRenovacion: 'mensual', + renovacion: true, + }; + + const eventoCreado = { + id: 1, + nombre: 'Evento de prueba' + }; + + repositorio.crearEvento.mockResolvedValue({ evento: eventoCreado }); + + // Act + await crearEvento(req, res); + + // Assert + expect(repositorio.crearEvento).toHaveBeenCalledWith({ + idCliente: 1, + nombre: 'Evento de prueba', + descripcion: 'Descripción del evento', + puntos: 100, + multiplicador: 1.5, + periodoRenovacion: 'mensual', + renovacion: 1, + }); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_EVENTOS.EVENTO_CREADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + codigo: MENSAJES_EVENTOS.EVENTO_CREADO.codigo, + mensaje: MENSAJES_EVENTOS.EVENTO_CREADO.mensaje, + evento: eventoCreado, + }); + }); +}); diff --git a/_tests_/GrupoEmpleados/Controladores/crearGrupoEmpleados.controller.test.js b/_tests_/GrupoEmpleados/Controladores/crearGrupoEmpleados.controller.test.js new file mode 100644 index 00000000..2636907c --- /dev/null +++ b/_tests_/GrupoEmpleados/Controladores/crearGrupoEmpleados.controller.test.js @@ -0,0 +1,99 @@ +/** + * Mocks antes de importar el controlador + */ +jest.mock('@altertex/emp/repos/repositorioCrearGrupo', () => ({ + crearGrupoYAsignarEmpleados: jest.fn(), + existeGrupoConNombre: jest.fn(), +})); + +const repositorio = require('@altertex/emp/repos/repositorioCrearGrupo'); +const controlador = require('@altertex/emp/ctrl/crearGrupoEmpleados.controller'); +const MENSAJES = require('@altertex/util/const/mensajesEmpleados'); + +describe('Controlador de Crear Grupo de Empleados', () => { + let req; + let res; + + const datosMock = { + nombreGrupo: 'Grupo A', + descripcion: 'Descripción de prueba', + idCliente: 1, + listaEmpleados: [10, 20, 30], + }; + + beforeEach(() => { + jest.clearAllMocks(); + req = { + body: { ...datosMock }, + user: { + clienteSeleccionado: datosMock.idCliente, + }, + }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + }); + + test('Debe crear un grupo exitosamente', async () => { + const idGrupoMock = 999; + + repositorio.existeGrupoConNombre.mockResolvedValue(false); // evitar conflicto de nombre repetido + repositorio.crearGrupoYAsignarEmpleados.mockResolvedValue({ idGrupo: idGrupoMock }); + + await controlador.crearGrupoEmpleados(req, res); + + expect(repositorio.existeGrupoConNombre).toHaveBeenCalledWith( + datosMock.nombreGrupo, + datosMock.idCliente + ); + expect(repositorio.crearGrupoYAsignarEmpleados).toHaveBeenCalledWith( + datosMock.nombreGrupo, + datosMock.descripcion, + datosMock.idCliente, + datosMock.listaEmpleados + ); + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.GRUPO_CREADO.mensaje, + idGrupo: idGrupoMock, + }); + }); + + test('Debe manejar error por datos incompletos', async () => { + req.body = {}; // Datos faltantes + req.user = { clienteSeleccionado: 1 }; + + await controlador.crearGrupoEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.DATOS_INCOMPLETOS.mensaje, + }); + expect(repositorio.crearGrupoYAsignarEmpleados).not.toHaveBeenCalled(); + }); + + test('Debe manejar error interno del servidor', async () => { + repositorio.existeGrupoConNombre.mockResolvedValue(false); + repositorio.crearGrupoYAsignarEmpleados.mockRejectedValue(new Error('Fallo en DB')); + + await controlador.crearGrupoEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.ERROR_CREAR_GRUPO.mensaje, + }); + }); + + test('Debe manejar error de nombre duplicado', async () => { + repositorio.existeGrupoConNombre.mockResolvedValue(true); // nombre ya existe + + await controlador.crearGrupoEmpleados(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.GRUPO_NOMBRE_REPETIDO.mensaje, + }); + expect(repositorio.crearGrupoYAsignarEmpleados).not.toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/_tests_/SetsProductos/actualizarSetsProductos.controller.test.js b/_tests_/SetsProductos/actualizarSetsProductos.controller.test.js new file mode 100644 index 00000000..3aea3b8d --- /dev/null +++ b/_tests_/SetsProductos/actualizarSetsProductos.controller.test.js @@ -0,0 +1,83 @@ +const request = require('supertest'); +const express = require('express'); +const bodyParser = require('body-parser'); +const MENSAJES = require('@altertex/util/const/mensajesSetsProductos'); + +jest.mock('@altertex/setspro/repos/repositorioActualizarSetsProductos', () => ({ + actualizarSetProductos: jest.fn() +})); + +const repositorio = require('@altertex/setspro/repos/repositorioActualizarSetsProductos'); +const controller = require('@altertex/setspro/ctrl/actualizarSetsProductos.controller'); + +const app = express(); +app.use(bodyParser.json()); + +// 👇 Middleware para simular usuario con cliente seleccionado +app.use((req, res, next) => { + req.user = { clienteSeleccionado: 1 }; + next(); +}); + +app.put('/sets-productos', controller.actualizarSetProductos); + +describe('Controlador actualizarSetProductos', () => { + // Silenciar console.error durante las pruebas para evitar ruido en la salida + beforeAll(() => { + jest.spyOn(console, 'error').mockImplementation(() => {}); + }); + + // Restaurar el comportamiento original de console.error + afterAll(() => { + console.error.mockRestore(); + }); + + // Limpia los mocks después de cada test + afterEach(() => jest.clearAllMocks()); + + it('debe devolver 400 si faltan campos requeridos', async () => { + const res = await request(app).put('/sets-productos').send({ + nombre: 'Set incompleto' + }); + + expect(res.statusCode).toBe(MENSAJES.FORMATO_INVALIDO_DATOS.codigo); + expect(res.body.mensaje).toBe(MENSAJES.FORMATO_INVALIDO_DATOS.mensaje); + expect(res.body.detalles).toMatch(/nombre y lista de productos/i); + }); + + it('debe devolver 200 si la actualización es exitosa', async () => { + repositorio.actualizarSetProductos.mockResolvedValue(); + + const datos = { + idSetProducto: 1, + nombre: 'Set Actualizado', + descripcion: 'Nueva descripción', + activo: true, + productos: [101, 102] + }; + + const res = await request(app).put('/sets-productos').send(datos); + + expect(res.statusCode).toBe(MENSAJES.SET_ACTUALIZADO.codigo); + expect(res.body.mensaje).toBe(MENSAJES.SET_ACTUALIZADO.mensaje); + expect(res.body.datos).toEqual(datos); + expect(repositorio.actualizarSetProductos).toHaveBeenCalledWith(1, datos); + }); + + it('debe devolver 500 si el repositorio lanza un error', async () => { + repositorio.actualizarSetProductos.mockRejectedValue(new Error('Fallo DB')); + + const datos = { + idSetProducto: 2, + nombre: 'Set con error', + descripcion: 'desc', + activo: false, + productos: [] + }; + + const res = await request(app).put('/sets-productos').send(datos); + + expect(res.statusCode).toBe(MENSAJES.ERROR_ACTUALIZAR_SET.codigo); + expect(res.body.mensaje).toBe('Fallo DB'); + }); +}); diff --git a/_tests_/SetsProductos/consultarListaSetsProductos.controller.test.js b/_tests_/SetsProductos/consultarListaSetsProductos.controller.test.js new file mode 100644 index 00000000..3c4d609a --- /dev/null +++ b/_tests_/SetsProductos/consultarListaSetsProductos.controller.test.js @@ -0,0 +1,81 @@ +// Mocks antes de importar el controlador +jest.mock('@altertex/setspro/repos/repositorioConsultarSetsProductos', () => ({ + obtenerSetsProductos: jest.fn(), +})); + +// Importaciones +const controlador = require('@altertex/setspro/ctrl/consultarSetsProductos.controller'); +const repositorio = require('@altertex/setspro/repos/repositorioConsultarSetsProductos'); +const MENSAJES = require('@altertex/util/const/mensajesSetsProductos'); + +describe('Controlador consultarLista (sets de productos)', () => { + let req; + let res; + + beforeEach(() => { + jest.clearAllMocks(); + + req = { + user: { + clienteSeleccionado: '101', + }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + jest.spyOn(console, 'error').mockImplementation(() => {}); // Silenciar errores + }); + + // Escenario 1: ID de cliente inválido + test('Debe retornar error si el ID del cliente no es válido', async () => { + req.user.clienteSeleccionado = undefined; + + await controlador.consultarLista(req, res); + expect(res.status).toHaveBeenCalledWith(MENSAJES.PARAMETROS_INVALIDOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.PARAMETROS_INVALIDOS.mensaje, + }); + }); + + // Escenario 2: Lista de sets obtenida exitosamente + test('Debe retornar la lista de sets si la consulta es exitosa', async () => { + const mockSets = [ + { + idSet: 1, + nombre: 'Set A', + productos: ['Producto 1', 'Producto 2'], + }, + { + idSet: 2, + nombre: 'Set B', + productos: ['Producto 3'], + }, + ]; + + repositorio.obtenerSetsProductos.mockResolvedValue(mockSets); + + await controlador.consultarLista(req, res); + + expect(repositorio.obtenerSetsProductos).toHaveBeenCalledWith(101); + expect(res.status).toHaveBeenCalledWith(MENSAJES.CONSULTA_EXITOSA.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.CONSULTA_EXITOSA.mensaje, + setsProductos: mockSets, + }); + }); + + // Escenario 3: Error inesperado en el repositorio + test('Debe manejar errores inesperados del repositorio', async () => { + repositorio.obtenerSetsProductos.mockRejectedValue(new Error('Error')); + + await controlador.consultarLista(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES.ERROR_CONSULTAR_SETS_PRODUCTOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES.ERROR_CONSULTAR_SETS_PRODUCTOS.mensaje, + }); + }); +}); diff --git a/_tests_/Usuarios/crearUsuario.controller.test.js b/_tests_/Usuarios/crearUsuario.controller.test.js new file mode 100644 index 00000000..9e4496ed --- /dev/null +++ b/_tests_/Usuarios/crearUsuario.controller.test.js @@ -0,0 +1,100 @@ +jest.mock('@altertex/usu/repos/repositorioCrearUsuario', () => ({ + crearUsuarioConAsociaciones: jest.fn(), +})); + +const repositorio = require('@altertex/usu/repos/repositorioCrearUsuario'); +const controlador = require('@altertex/usu/ctrl/crearUsuario.controller'); +const MENSAJES_USUARIOS = require('@altertex/util/const/mensajesUsuarios'); + +describe('Controlador de Crear Usuario', () => { + let req; + let res; + + const datosMock = { + nombreCompleto: 'María López', + correoElectronico: 'maria@correo.com', + contrasenia: 'Contrasenia1.', + numeroTelefono: '5512345678', + direccion: 'Calle Falsa 123', + fechaNacimiento: '1990-01-01', + genero: 'Femenino', + estatus: true, + idRol: 2, + idCliente: [1], + }; + + beforeEach(() => { + jest.clearAllMocks(); + req = { body: { ...datosMock } }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + }); + + test('Debe crear un usuario exitosamente', async () => { + repositorio.crearUsuarioConAsociaciones.mockResolvedValue({ idUsuario: 101 }); + + await controlador.crearUsuario(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.USUARIO_CREADO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_USUARIOS.USUARIO_CREADO.mensaje, + idUsuario: 101, + }); + expect(repositorio.crearUsuarioConAsociaciones).toHaveBeenCalled(); + }); + + test('Debe rechazar si faltan campos requeridos', async () => { + req.body = { ...datosMock, nombreCompleto: undefined }; + + await controlador.crearUsuario(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(res.json).toHaveBeenCalledWith({ mensaje: 'Faltan campos requeridos' }); + }); + + test('Debe rechazar correo inválido', async () => { + req.body.correoElectronico = 'correo-no-valido'; + + await controlador.crearUsuario(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.CORREO_INVALIDO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_USUARIOS.CORREO_INVALIDO.mensaje, + }); + }); + + test('Debe rechazar contraseña débil (sin mayúscula)', async () => { + req.body.contrasenia = 'contrasenia$1'; + + await controlador.crearUsuario(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.CONTRASENA_DEBIL.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: 'La contraseña debe contener al menos una letra mayúscula.', + }); + }); + + test('Debe rechazar número telefónico inválido', async () => { + req.body.numeroTelefono = 'abc123'; + + await controlador.crearUsuario(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.TELEFONO_INVALIDO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_USUARIOS.TELEFONO_INVALIDO.mensaje, + }); + }); + + test('Debe manejar error del repositorio', async () => { + repositorio.crearUsuarioConAsociaciones.mockRejectedValue(new Error('Fallo')); + + await controlador.crearUsuario(req, res); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_USUARIOS.ERROR_CREAR_USUARIO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_USUARIOS.ERROR_CREAR_USUARIO.mensaje, + }); + }); +}); diff --git a/_tests_/autenticacion/Controladores/inicioSesion.controller.test.js b/_tests_/autenticacion/Controladores/inicioSesion.controller.test.js new file mode 100644 index 00000000..f66cca03 --- /dev/null +++ b/_tests_/autenticacion/Controladores/inicioSesion.controller.test.js @@ -0,0 +1,192 @@ +/** + * + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + * + * Primero configuramos los mocks antes de importar el controlador + */ +jest.mock('@altertex/aut/repos/repositorioInicioSesion', () => ({ + obtenerUsuario: jest.fn(), +})); +jest.mock('bcryptjs', () => ({ + compare: jest.fn(), +})); +jest.mock('jsonwebtoken', () => ({ + sign: jest.fn(), +})); + +// Mock del módulo de mensajes con los valores reales +jest.mock('@altertex/util/const/mensajesAutenticacion', () => ({ + INICIO_SESION_EXITOSO: { + codigo: 200, + mensaje: 'Inicio de sesión exitoso.', + }, + CAMPOS_OBLIGATORIOS: { + codigo: 400, + mensaje: 'Se necesita ingresar correo y contraseña.', + }, + FORMATO_CORREO_INVALIDO: { + codigo: 400, + mensaje: 'El formato del correo electrónico no es válido.', + }, + CREDENCIALES_INVALIDAS: { + codigo: 401, + mensaje: 'Usuario o contraseña incorrectos.', + }, + ERROR_SERVIDOR: { + codigo: 500, + mensaje: 'Ocurrió un error inesperado. Intente de nuevo más tarde.', + }, +})); + +// Importamos los módulos después de configurar los mocks +const controladorInicioSesion = require('@altertex/aut/ctrl/inicioSesion.controller'); +const repositorio = require('@altertex/aut/repos/repositorioInicioSesion'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const MENSAJES_AUTENTICACION = require('@altertex/util/const/mensajesAutenticacion'); + +// Mock para process.env +process.env.JWT_SECRET = 'secret_test_key'; + +describe('Controlador de Inicio de Sesión', () => { + let req; + let res; + + beforeEach(() => { + // Reset de los mocks + jest.clearAllMocks(); + + // Mock de req y res + req = { + body: {}, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + cookie: jest.fn(), + }; + }); + + test('Debe retornar error 400 cuando faltan campos requeridos', async () => { + // Arrange + req.body = { correo: '', contrasenia: '' }; + + // Act + await controladorInicioSesion.inicioSesion(req, res); + + // Assert + expect(res.status).toHaveBeenCalledWith(MENSAJES_AUTENTICACION.CAMPOS_OBLIGATORIOS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_AUTENTICACION.CAMPOS_OBLIGATORIOS.mensaje, + }); + }); + + test('Debe retornar error 400 cuando el formato de correo es inválido', async () => { + // Arrange + req.body = { correo: 'correo-invalido', contrasenia: 'password123' }; + + // Act + await controladorInicioSesion.inicioSesion(req, res); + + // Assert + expect(res.status).toHaveBeenCalledWith(MENSAJES_AUTENTICACION.FORMATO_CORREO_INVALIDO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_AUTENTICACION.FORMATO_CORREO_INVALIDO.mensaje, + }); + }); + + test('Debe retornar error 401 cuando el usuario no existe', async () => { + // Arrange + req.body = { correo: 'usuario@ejemplo.com', contrasenia: 'password123' }; + + repositorio.obtenerUsuario.mockResolvedValue({ + infoUsuario: [], + permisos: [], + clientesAsociados: [], + }); + + // Act + await controladorInicioSesion.inicioSesion(req, res); + + // Assert + expect(repositorio.obtenerUsuario).toHaveBeenCalledWith('usuario@ejemplo.com'); + expect(res.status).toHaveBeenCalledWith(MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.mensaje, + }); + }); + + test('Debe retornar error 401 cuando la contraseña es incorrecta', async () => { + // Arrange + req.body = { correo: 'usuario@ejemplo.com', contrasenia: 'password123' }; + + repositorio.obtenerUsuario.mockResolvedValue({ + infoUsuario: [ + { + correoElectronico: 'usuario@ejemplo.com', + contrasenia: 'hashed_password', + }, + ], + permisos: [], + clientesAsociados: [], + }); + + bcrypt.compare.mockResolvedValue(false); + + // Act + await controladorInicioSesion.inicioSesion(req, res); + + // Assert + expect(bcrypt.compare).toHaveBeenCalledWith('password123', 'hashed_password'); + expect(res.status).toHaveBeenCalledWith(MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_AUTENTICACION.CREDENCIALES_INVALIDAS.mensaje, + }); + }); + + test('Debe retornar status 200 y generar token cuando las credenciales son correctas', async () => { + // Arrange + req.body = { correo: 'usuario@ejemplo.com', contrasenia: 'password123' }; + const mockUsuario = { + correoElectronico: 'usuario@ejemplo.com', + contrasenia: 'hashed_password', + }; + const mockPermisos = ['permiso1', 'permiso2']; + const mockClientesAsociados = [1, 2, 3]; + + repositorio.obtenerUsuario.mockResolvedValue({ + infoUsuario: [mockUsuario], + permisos: mockPermisos, + clientesAsociados: mockClientesAsociados, + }); + + bcrypt.compare.mockResolvedValue(true); + jwt.sign.mockReturnValue('token_jwt_generado'); + + // Act + await controladorInicioSesion.inicioSesion(req, res); + + // Assert + expect(jwt.sign).toHaveBeenCalledWith( + { + correo: 'usuario@ejemplo.com', + permisos: mockPermisos, + clientesAsociados: mockClientesAsociados, + }, + 'secret_test_key', + { expiresIn: '8h' } + ); + + expect(res.cookie).toHaveBeenCalledWith('token', 'token_jwt_generado', { + httpOnly: true, + secure: true, + sameSite: 'None', + }); + + expect(res.status).toHaveBeenCalledWith(MENSAJES_AUTENTICACION.INICIO_SESION_EXITOSO.codigo); + expect(res.json).toHaveBeenCalledWith({ + mensaje: MENSAJES_AUTENTICACION.INICIO_SESION_EXITOSO.mensaje, + }); + }); +}); diff --git a/_tests_/autenticacion/Repositorios/repositorioInicioSesion.test.js b/_tests_/autenticacion/Repositorios/repositorioInicioSesion.test.js new file mode 100644 index 00000000..433b892d --- /dev/null +++ b/_tests_/autenticacion/Repositorios/repositorioInicioSesion.test.js @@ -0,0 +1,91 @@ +/** + * RF78 - Iniciar Sesion - https://codeandco-wiki.netlify.app/docs/proyectos/textiles/documentacion/requisitos/RF78 + * + * Primero configuramos los mocks + */ +jest.mock("@altertex/util/ser/correrQuery", () => jest.fn()); + +jest.mock("@altertex/util/const/consultasUsuarios", () => ({ + OBTENER_USUARIO: "SELECT * FROM usuarios WHERE correoElectronico = ?", + OBTENER_PERMISOS: "SELECT nombre FROM permisos WHERE correoElectronico = ?", + OBTENER_CLIENTES_ASOCIADOS: + "SELECT idCliente FROM clientes_usuarios WHERE correoElectronico = ?", +})); + +// Importamos después de configurar los mocks +const repositorio = require("@altertex/aut/repos/repositorioInicioSesion"); +const correrQuery = require("@altertex/util/ser/correrQuery"); +const CONSULTAS_USUARIOS = require("@altertex/util/const/consultasUsuarios"); + +describe("Repositorio de Inicio de Sesión", () => { + beforeEach(() => { + // Limpiar los mocks antes de cada prueba + jest.clearAllMocks(); + }); + + test("obtenerUsuario debe retornar información del usuario, permisos y clientes asociados", async () => { + // Arrange + const correoElectronico = "usuario@ejemplo.com"; + const mockUsuario = [ + { + id: 1, + nombre: "Usuario Test", + correoElectronico, + contrasenia: "hashed_password", + }, + ]; + const mockPermisos = [{ nombre: "admin" }, { nombre: "usuario" }]; + const mockClientesAsociados = [{ idCliente: 1 }, { idCliente: 2 }]; + + // Configuramos el comportamiento del mock para cada llamada + correrQuery + .mockResolvedValueOnce(mockUsuario) + .mockResolvedValueOnce(mockPermisos) + .mockResolvedValueOnce(mockClientesAsociados); + + // Act + const resultado = await repositorio.obtenerUsuario(correoElectronico); + + // Assert + expect(correrQuery).toHaveBeenCalledTimes(3); + expect(correrQuery).toHaveBeenNthCalledWith( + 1, + CONSULTAS_USUARIOS.OBTENER_USUARIO, + [correoElectronico] + ); + expect(correrQuery).toHaveBeenNthCalledWith( + 2, + CONSULTAS_USUARIOS.OBTENER_PERMISOS, + [correoElectronico] + ); + expect(correrQuery).toHaveBeenNthCalledWith( + 3, + CONSULTAS_USUARIOS.OBTENER_CLIENTES_ASOCIADOS, + [correoElectronico] + ); + + expect(resultado).toEqual({ + infoUsuario: mockUsuario, + permisos: ["admin", "usuario"], + clientesAsociados: [1, 2], + }); + }); + + test("obtenerUsuario debe manejar errores correctamente", async () => { + // Arrange + const correoElectronico = "usuario@ejemplo.com"; + const errorMessage = "Error en la base de datos"; + + correrQuery.mockRejectedValue(new Error(errorMessage)); + + // Act + const resultado = await repositorio.obtenerUsuario(correoElectronico); + + // Assert + expect(correrQuery).toHaveBeenCalledWith( + CONSULTAS_USUARIOS.OBTENER_USUARIO, + [correoElectronico] + ); + expect(resultado).toBe(`Error obteniendo usuario`); + }); +}); diff --git a/app.js b/app.js index e77687f7..c2ded97d 100644 --- a/app.js +++ b/app.js @@ -1,38 +1,89 @@ -const dotenv = require("dotenv"); -const envFile = `.env.${process.env.NODE_ENV || "staging"}`; // Defaults to 'development' if NODE_ENV is not set -dotenv.config({ path: envFile }); +require('module-alias/register'); +require('@altertex/config/dotenv'); -const cors = require("cors"); -const express = require("express"); -const cookieParser = require("cookie-parser"); -const revisarApiKey = require("./util/middlewares/revisarApiKey"); +//Importaciones de librerias +const express = require('express'); +const cors = require('cors'); +const cookieParser = require('cookie-parser'); +const swaggerJSDoc = require('swagger-jsdoc'); +const csrf = require('csurf') -const app = express(); +//Importaciones de configuracion +const corsOptions = require('@altertex/config/corsOptions'); +const opcionesSwagger = require('@altertex/config/swagger'); +const swaggerUI = require('swagger-ui-express'); -app.use(express.json()); +//Importaciones de rutas +const rutasAutenticacion = require('@altertex/aut/rutas/indexAutenticacion.routes'); +const rutasUsuarios = require('@altertex/usu/rutas/indexUsuarios.routes'); +const rutasCategorias = require('@altertex/cat/rutas/indexCategorias.routes'); +const rutasProductos = require('@altertex/pro/rutas/indexProductos.routes'); +const rutasProveedores = require('@altertex/prove/rutas/indexProveedores.routes'); +const rutasSetsProductos = require('@altertex/setspro/rutas/indexSetsProductos.routes'); +const rutasEmpleados = require('@altertex/emp/rutas/indexEmpleados.routes'); +const rutasClientes = require('@altertex/cli/rutas/indexClientes.routes'); +const rutasRoles = require('@altertex/rol/rutas/indexRoles.routes'); +const rutasCuotas = require('@altertex/cuota/rutas/indexCuotas.routes'); +const rutasPedidos = require('@altertex/pedidos/rutas/indexPedidos.routes'); +const rutasEventos = require('@altertex/eve/rutas/indexEventos.routes'); +const rutasPagos = require('@altertex/pago/rutas/indexPagos.routes'); +const RUTAS = require('@altertex/util/const/rutas'); -app.use( - cors({ - origin: [process.env.LOCAL_URL, process.env.DEPLOYED_URL], - methods: ["GET", "POST", "PUT", "DELETE"], - credentials: true, // ✅ Allow cookies - }) -); +//Importaciones de CRON jobs +const cronCuotas = require('@altertex/CRON/ctrl/actualizarCuotaSet.controller'); -app.use(revisarApiKey("x-api-key", "Api key invalida")); +const puerto = process.env.PORT || 5000; +//Configuracion de aplicacion express +const app = express(); +app.use(express.json({limit: '5mb'})); +app.use(express.urlencoded({limit: '5mb', extended: true})); app.use(cookieParser()); +app.use(cors(corsOptions)); +const proteccionCsrf = csrf({ + cookie: { + httpOnly: true, + secure: false, + sameSite: 'strict' + } +}) + +cronCuotas.start(); -const ambiente = process.env.NODE_ENV; +//Usar las rutas para que esten disponibles en la aplicacion +app.use(RUTAS.API, rutasAutenticacion); +app.use(RUTAS.API, rutasUsuarios); +app.use(RUTAS.API, rutasProductos); +app.use(RUTAS.API, rutasProveedores); +app.use(RUTAS.API, rutasSetsProductos); +app.use(RUTAS.API, rutasEmpleados); +app.use(RUTAS.API, rutasClientes); +app.use(RUTAS.API, rutasRoles); +app.use(RUTAS.API, rutasCuotas); +app.use(RUTAS.API, rutasCategorias); +app.use(RUTAS.API, rutasPedidos); +app.use(RUTAS.API, rutasEventos); +app.use(RUTAS.API, rutasPagos); -app.get("/", async (req, res) => { - res.status(201).json({ message: `Proyecto TEXT&LINES ${ambiente}` }); +app.use((req, res, next) => { + if (req.method === 'GET' && req.path === '/api/csrf-token') { + return next() + } + proteccionCsrf(req, res, next) +}) + +app.get('/api/csrf-token', proteccionCsrf, (req, res) => { + res.json({csrfToken: req.csrfToken()}); }); -const port = process.env.PORT || 5000; +app.get('/', async (req, res) => { + return res + .status(200) + .json({mensaje: `Ruta por default Proyecto Text&Lines en ambiente: ${process.env.NODE_ENV}`}); +}); -app.listen(port, () => - console.log( - `Server corriendo en puerto: ${port} ${port} en ambiente de ${process.env.NODE_ENV}.` - ) -); +//Configuracion de swaggerUI +const swaggerSpec = swaggerJSDoc(opcionesSwagger); +app.use(RUTAS.API_DOCS, swaggerUI.serve, swaggerUI.setup(swaggerSpec)); +app.listen(puerto, () => + console.log(`Servidor corriendo en puerto ${puerto} [${process.env.NODE_ENV}]`)); diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 00000000..fa5c9e72 Binary files /dev/null and b/dump.rdb differ diff --git a/ecosystem-production.config.js b/ecosystem-production.config.js new file mode 100644 index 00000000..a83e4fee --- /dev/null +++ b/ecosystem-production.config.js @@ -0,0 +1,15 @@ +module.exports = { + apps: [ + { + name: 'app-production', + script: './app.js', + instances: 'max', + exec_mode: 'cluster', + watch: false, + env: { + NODE_ENV: 'production', + PORT: 3000, + }, + }, + ], +}; diff --git a/ecosystem-staging.config.js b/ecosystem-staging.config.js new file mode 100644 index 00000000..9bbc443c --- /dev/null +++ b/ecosystem-staging.config.js @@ -0,0 +1,15 @@ +module.exports = { + apps: [ + { + name: 'app-staging', + script: './app.js', + instances: 'max', + exec_mode: 'cluster', + watch: false, + env: { + NODE_ENV: 'staging', + PORT: 4000, + }, + }, + ], +}; diff --git a/ecosystem.config.js b/ecosystem.config.js deleted file mode 100644 index 75dcd038..00000000 --- a/ecosystem.config.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - apps: [ - { - name: "app-production", // Name of your app - script: "./app.js", // Path to your entry file (e.g., app.js, server.js, etc.) - instances: "max", // Run maximum instances of your app based on available CPU cores - exec_mode: "cluster", // Use cluster mode for load balancing (optional) - watch: false, // Disable file watching (set to true if you want PM2 to restart on file changes) - env: { - NODE_ENV: "production", // Set the environment variable to "production" - PORT: 3000, - }, - }, - { - name: "app-staging", // Name of your app - script: "./app.js", // Path to your entry file (e.g., app.js, server.js, etc.) - instances: "max", // Run maximum instances of your app based on available CPU cores - exec_mode: "cluster", // Use cluster mode for load balancing (optional) - watch: false, // Disable file watching (set to true if you want PM2 to restart on file changes) - env: { - NODE_ENV: "staging", // Set environment variable for production explicitly (optional) - PORT: 4000, // Define the port your app runs on (optional) - }, - }, - ], -}; diff --git a/eslint.config.mjs b/eslint.config.mjs index 3d5fe7eb..ebc27158 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,67 +1,139 @@ -import js from "@eslint/js"; -import globals from "globals"; +import js from '@eslint/js'; +import globals from 'globals'; +import jsdoc from 'eslint-plugin-jsdoc'; export default [ - { ignores: ["dist", "node_modules"] }, + { ignores: ['dist', 'node_modules'] }, + + // JSDoc rules for target files - controllers and utility/data folders (excluding Constantes) { - files: ["**/*.{js,mjs,cjs}"], + plugins: { + jsdoc, + }, + files: ['**/Utilidades/**/*.js', '**/Datos/**/*.js', '**/*.controller.js'], + ignores: [ + '**/Utilidades/**/Constantes/**/*.js', // Exclude Constantes folders + ], languageOptions: { - ecmaVersion: "latest", - globals: globals.node, // Set Node.js globals + ecmaVersion: 'latest', + globals: globals.node, parserOptions: { ecmaFeatures: { jsx: false }, - sourceType: "module", + sourceType: 'module', + }, + }, + settings: { + jsdoc: { + mode: 'jsdoc', // Classic JS mode, not TypeScript }, }, rules: { - ...js.configs.recommended.rules, + // Enforce JSDoc presence - Highest priority + 'jsdoc/require-jsdoc': [ + 'error', // Changed from "warn" to "error" to enforce more strictly + { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + ArrowFunctionExpression: true, // Changed to true to require JSDoc for arrow functions + FunctionExpression: true, // Changed to true to require JSDoc for function expressions + }, + exemptEmptyFunctions: false, // Require JSDoc even for empty functions + }, + ], + + // Enforce JSDoc content quality - With warnings + 'jsdoc/require-param': 'error', + 'jsdoc/require-param-name': 'error', + 'jsdoc/require-param-type': 'error', + 'jsdoc/require-returns': 'error', + 'jsdoc/require-returns-type': 'error', + 'jsdoc/valid-types': 'error', + 'jsdoc/check-param-names': 'error', + 'jsdoc/check-tag-names': 'error', + 'jsdoc/check-types': 'error', - // ESLint Rules for Backend - "object-shorthand": "error", - "no-new-object": "error", - "default-param-last": "error", - "no-new-func": "error", - "function-paren-newline": ["error", "consistent"], - "no-duplicate-imports": "error", - "object-curly-newline": ["error", { consistent: true }], - "no-undef": "error", - "prefer-const": "error", - "one-var": ["error", "never"], - "no-multi-assign": "error", - "no-plusplus": "error", - "operator-linebreak": ["error", "before"], - "new-cap": [ - "error", + // Optional rules based on your preference + 'jsdoc/require-description': 'warn', // Added: Require general descriptions + 'jsdoc/require-param-description': 'off', // Kept off as per your preference + 'jsdoc/require-returns-description': 'off', // Kept off as per your preference + }, + }, + + // General rules for all JavaScript files + { + files: ['**/*.{js,mjs,cjs}'], + languageOptions: { + ecmaVersion: 'latest', + globals: globals.node, + parserOptions: { + ecmaFeatures: { jsx: false }, + sourceType: 'module', + }, + }, + rules: { + ...js.configs.recommended.rules, + 'object-shorthand': 'error', + 'no-new-object': 'error', + 'default-param-last': 'error', + 'no-new-func': 'error', + 'function-paren-newline': ['error', 'consistent'], + 'no-duplicate-imports': 'error', + 'object-curly-newline': ['error', { consistent: true }], + 'no-undef': 'error', + 'prefer-const': 'error', + 'one-var': ['error', 'never'], + 'no-multi-assign': 'error', + 'no-plusplus': 'error', + 'operator-linebreak': ['error', 'before'], + 'new-cap': [ + 'error', { - newIsCap: true, // Enforce capitalization for constructors - capIsNew: false, // Allow capitalized functions that aren't constructors - capIsNewExceptions: ["Router"], // Allow express.Router() without error + newIsCap: true, + capIsNew: false, + capIsNewExceptions: ['Router'], properties: false, }, ], camelcase: [ - "error", + 'error', { - properties: "never", // Ignore object properties - allow: ["exec_mode"], // Allow specific exceptions + properties: 'never', + allow: ['exec_mode'], }, ], - "id-length": ["error", { min: 2 }], - "nonblock-statement-body-position": ["error", "beside"], - "brace-style": ["error", "1tbs", { allowSingleLine: true }], - "no-iterator": "error", - "no-restricted-syntax": "error", - "prefer-arrow-callback": "error", - "arrow-spacing": "error", - "no-array-constructor": "error", - "template-curly-spacing": ["error", "never"], - "prefer-template": "error", - "no-eval": "error", - "no-useless-constructor": "error", - "no-dupe-class-members": "error", - "class-methods-use-this": "error", - "dot-notation": "error", - "prefer-exponentiation-operator": "error", + 'id-length': ['error', { min: 2 }], + 'nonblock-statement-body-position': ['error', 'beside'], + 'brace-style': ['error', '1tbs', { allowSingleLine: true }], + 'no-iterator': 'error', + 'no-restricted-syntax': 'error', + 'prefer-arrow-callback': 'error', + 'arrow-spacing': 'error', + 'no-array-constructor': 'error', + 'template-curly-spacing': ['error', 'never'], + 'prefer-template': 'error', + 'no-eval': 'error', + 'no-useless-constructor': 'error', + 'no-dupe-class-members': 'error', + 'class-methods-use-this': 'error', + 'dot-notation': 'error', + 'prefer-exponentiation-operator': 'error', + }, + }, + + // Special configuration for test files + { + files: ['**/*.test.js', '**/*.spec.js', 'jest.setup.js'], + languageOptions: { + ecmaVersion: 'latest', + globals: { + ...globals.node, + ...globals.jest, + }, + parserOptions: { + sourceType: 'module', + }, }, }, ]; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..9162afb7 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,103 @@ +module.exports = { + // La raíz del directorio que Jest usará para buscar los archivos + rootDir: '.', + + // Rutas a directorios que Jest debe ignorar durante las pruebas + testPathIgnorePatterns: ['/node_modules/'], + + // Patrón para encontrar archivos de prueba + testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'], + + // Entorno de prueba + testEnvironment: 'node', + + // Cobertura de código + collectCoverage: true, + coverageDirectory: 'coverage', + + // Módulos que deben ser transformados + // (Si usas babel o typescript necesitarías configurar esto) + transform: {}, + moduleFileExtensions: ['js', 'json'], + moduleNameMapper: { + '^@altertex/root(.*)$': '$1', + + // Autenticacion module mappings + '^@altertex/aut/ctrl/(.*)$': '/Autenticacion/Controladores/$1', + '^@altertex/aut/repos/(.*)$': '/Autenticacion/Datos/Repositorios/$1', + '^@altertex/aut/rutasInd/(.*)$': '/Autenticacion/Rutas/RutasIndividuales/$1', + '^@altertex/aut/rutas/(.*)$': '/Autenticacion/Rutas/$1', + '^@altertex/aut/datos/(.*)$': '/Autenticacion/Datos/$1', + '^@altertex/aut/(.*)$': '/Autenticacion/$1', + + // Categorias module mappings + "^@altertex/cat/ctrl/(.*)$": "/Categorias/Controladores/$1", + "^@altertex/cat/repos/(.*)$": "/Categorias/Datos/Repositorios/$1", + "^@altertex/cat/rutasInd/(.*)$": "/Categorias/Rutas/RutasIndividuales/$1", + "^@altertex/cat/rutas/(.*)$": "/Categorias/Rutas/$1", + "^@altertex/cat/datos/(.*)$": "/Categorias/Datos/$1", + "^@altertex/cat/(.*)$": "/Categorias/$1", + + // Clientes module mappings + "^@altertex/cli/ctrl/(.*)$": "/Clientes/Controladores/$1", + "^@altertex/cli/repos/(.*)$": "/Clientes/Datos/Repositorios/$1", + "^@altertex/cli/rutasInd/(.*)$": "/Clientes/Rutas/RutasIndividuales/$1", + "^@altertex/cli/rutas/(.*)$": "/Clientes/Rutas/$1", + "^@altertex/cli/datos/(.*)$": "/Clientes/Datos/$1", + "^@altertex/cli/(.*)$": "/Clientes/$1", + + // Cuotas module mappings (added) + '^@altertex/cuota/ctrl/(.*)$': '/Cuotas/Controladores/$1', + '^@altertex/cuota/repos/(.*)$': '/Cuotas/Datos/Repositorios/$1', + '^@altertex/cuota/rutasInd/(.*)$': '/Cuotas/Rutas/RutasIndividuales/$1', + '^@altertex/cuota/rutas/(.*)$': '/Cuotas/Rutas/$1', + '^@altertex/cuota/datos/(.*)$': '/Cuotas/Datos/$1', + '^@altertex/cuota/(.*)$': '/Cuotas/$1', + + // CRON jobs module mappings (added) + '^@altertex/CRON/ctrl/(.*)$': '/CRON_JOBS/Controladores/$1', + '^@altertex/CRON/datos/(.*)$': '/CRON_JOBS/Datos/$1', + '^@altertex/CRON/repos/(.*)$': '/CRON_JOBS/Datos/Repositorios/$1', + '^@altertex/CRON/(.*)$': '/CRON_JOBS/$1', + + // Configuration and utilities mappings + '^@altertex/config/(.*)$': '/Configuracion/$1', + '^@altertex/util/ser/(.*)$': '/Utilidades/Servicios/$1', + '^@altertex/util/inter/(.*)$': '/Utilidades/Intermediarios/$1', + '^@altertex/util/const/(.*)$': '/Utilidades/Constantes/$1', + '^@altertex/util/bd/(.*)$': '/Utilidades/BaseDeDatos/$1', + '^@altertex/util/(.*)$': '/Utilidades/$1', + + // GrupoEmpleados module mappings + "^@altertex/emp/ctrl/(.*)$": "/Empleados/Controladores/$1", + "^@altertex/emp/repos/(.*)$": "/Empleados/Datos/Repositorios/$1", + "^@altertex/emp/rutasInd/(.*)$": "/Empleados/Rutas/RutasIndividuales/$1", + "^@altertex/emp/rutas/(.*)$": "/Empleados/Rutas/$1", + "^@altertex/emp/datos/(.*)$": "/Empleados/Datos/$1", + "^@altertex/emp/(.*)$": "/Empleados/$1", + + // Eventos module mappings + "^@altertex/eve/ctrl/(.*)$": "/Eventos/Controladores/$1", + "^@altertex/eve/repos/(.*)$": "/Eventos/Datos/Repositorios/$1", + "^@altertex/eve/rutasInd/(.*)$": "/Eventos/Rutas/RutasIndividuales/$1", + "^@altertex/eve/rutas/(.*)$": "/Eventos/Rutas/$1", + "^@altertex/eve/datos/(.*)$": "/Eventos/Datos/$1", + "^@altertex/eve/(.*)$": "/Eventos/$1", + + // Usuarios module mappings + '^@altertex/usu/ctrl/(.*)$': '/Usuarios/Controladores/$1', + '^@altertex/usu/repos/(.*)$': '/Usuarios/Datos/Repositorios/$1', + '^@altertex/usu/rutasInd/(.*)$': '/Usuarios/Rutas/RutasIndividuales/$1', + '^@altertex/usu/rutas/(.*)$': '/Usuarios/Rutas/$1', + '^@altertex/usu/datos/(.*)$': '/Usuarios/Datos/$1', + '^@altertex/usu/(.*)$': '/Usuarios/$1', + + // Sets de productos + "^@altertex/setspro/ctrl/(.*)$": "/SetsProductos/Controladores/$1", + "^@altertex/setspro/repos/(.*)$": "/SetsProductos/Datos/Repositorios/$1", + + // Generic mapping as fallback + '^@altertex/(.*)$': '/$1', + + }, +}; diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 00000000..0fb8877d --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,2 @@ +// Configuración global para Jest +jest.setTimeout(10000); // Timeout de 10 segundos para las pruebas diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 00000000..9e8820a1 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,97 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@altertex/*": ["./*"], + "@altertex/aut/*": ["Autenticacion/*"], + "@altertex/aut/ctrl/*": ["Autenticacion/Controladores/*"], + "@altertex/aut/datos/*": ["Autenticacion/Datos/*"], + "@altertex/aut/repos/*": ["Autenticacion/Datos/Repositorios/*"], + "@altertex/aut/rutas/*": ["Autenticacion/Rutas/*"], + "@altertex/aut/rutasInd/*": ["Autenticacion/Rutas/RutasIndividuales/*"], + "@altertex/usu/*": ["Usuarios/*"], + "@altertex/usu/ctrl/*": ["Usuarios/Controladores/*"], + "@altertex/usu/datos/*": ["Usuarios/Datos/*"], + "@altertex/usu/repos/*": ["Usuarios/Datos/Repositorios/*"], + "@altertex/usu/rutas/*": ["Usuarios/Rutas/*"], + "@altertex/usu/rutasInd/*": ["Usuarios/Rutas/RutasIndividuales/*"], + "@altertex/emp/*": ["Empleados/*"], + "@altertex/emp/ctrl/*": ["Empleados/Controladores/*"], + "@altertex/emp/datos/*": ["Empleados/Datos/*"], + "@altertex/emp/repos/*": ["Empleados/Datos/Repositorios/*"], + "@altertex/emp/rutas/*": ["Empleados/Rutas/*"], + "@altertex/emp/rutasInd/*": ["Empleados/Rutas/RutasIndividuales/*"], + "@altertex/cli/*": ["Clientes/*"], + "@altertex/cli/ctrl/*": ["Clientes/Controladores/*"], + "@altertex/cli/datos/*": ["Clientes/Datos/*"], + "@altertex/cli/repos/*": ["Clientes/Datos/Repositorios/*"], + "@altertex/cli/rutas/*": ["Clientes/Rutas/*"], + "@altertex/cli/rutasInd/*": ["Clientes/Rutas/RutasIndividuales/*"], + "@altertex/config/*": ["Configuracion/*"], + "@altertex/util/*": ["Utilidades/*"], + "@altertex/util/bd/*": ["Utilidades/BaseDeDatos/*"], + "@altertex/util/const/*": ["Utilidades/Constantes/*"], + "@altertex/util/inter/*": ["Utilidades/Intermediarios/*"], + "@altertex/util/vali/*": ["Utilidades/Intermediarios/Validaciones/*"], + "@altertex/util/ser/*": ["Utilidades/Servicios/*"], + "@altertex/rol/*": ["Roles/*"], + "@altertex/rol/ctrl/*": ["Roles/Controladores/*"], + "@altertex/rol/datos/*": ["Roles/Datos/*"], + "@altertex/rol/repos/*": ["Roles/Datos/Repositorios/*"], + "@altertex/rol/rutas/*": ["Roles/Rutas/*"], + "@altertex/rol/rutasInd/*": ["Roles/Rutas/RutasIndividuales/*"], + "@altertex/pro/*": ["Productos/*"], + "@altertex/pro/ctrl/*": ["Productos/Controladores/*"], + "@altertex/pro/datos/*": ["Productos/Datos/*"], + "@altertex/pro/repos/*": ["Productos/Datos/Repositorios/*"], + "@altertex/pro/rutas/*": ["Productos/Rutas/*"], + "@altertex/pro/rutasInd/*": ["Productos/Rutas/RutasIndividuales/*"], + "@altertex/prove/*": ["Proveedores/*"], + "@altertex/prove/ctrl/*": ["Proveedores/Controladores/*"], + "@altertex/prove/datos/*": ["Proveedores/Datos/*"], + "@altertex/prove/repos/*": ["Proveedores/Datos/Repositorios/*"], + "@altertex/prove/rutas/*": ["Proveedores/Rutas/*"], + "@altertex/prove/rutasInd/*": ["Proveedores/Rutas/RutasIndividuales/*"], + "@altertex/cuota/*": ["Cuotas/*"], + "@altertex/cuota/ctrl/*": ["Cuotas/Controladores/*"], + "@altertex/cuota/datos/*": ["Cuotas/Datos/*"], + "@altertex/cuota/repos/*": ["Cuotas/Datos/Repositorios/*"], + "@altertex/cuota/rutas/*": ["Cuotas/Rutas/*"], + "@altertex/cuota/rutasInd/*": ["Cuotas/Rutas/RutasIndividuales/*"], + "@altertex/cat/*": ["Categorias/*"], + "@altertex/cat/ctrl/*": ["Categorias/Controladores/*"], + "@altertex/cat/datos/*": ["Categorias/Datos/*"], + "@altertex/cat/repos/*": ["Categorias/Datos/Repositorios/*"], + "@altertex/cat/rutas/*": ["Categorias/Rutas/*"], + "@altertex/cat/rutasInd/*": ["Categorias/Rutas/RutasIndividuales/*"], + "@altertex/setspro/*": ["SetsProductos/*"], + "@altertex/setspro/ctrl/*": ["SetsProductos/Controladores/*"], + "@altertex/setspro/datos/*": ["SetsProductos/Datos/*"], + "@altertex/setspro/repos/*": ["SetsProductos/Datos/Repositorios/*"], + "@altertex/setspro/rutas/*": ["SetsProductos/Rutas/*"], + "@altertex/setspro/rutasInd/*": ["SetsProductos/Rutas/RutasIndividuales/*"], + "@altertex/CRON/*": ["CRON_JOBS/*"], + "@altertex/CRON/ctrl/*": ["CRON_JOBS/Controladores/*"], + "@altertex/CRON/datos/*": ["CRON_JOBS/Datos/*"], + "@altertex/CRON/repos/*": ["CRON_JOBS/Datos/Repositorios/*"], + "@altertex/eve/*": ["Eventos/*"], + "@altertex/eve/ctrl/*": ["Eventos/Controladores/*"], + "@altertex/eve/datos/*": ["Eventos/Datos/*"], + "@altertex/eve/repos/*": ["Eventos/Datos/Repositorios/*"], + "@altertex/eve/rutas/*": ["Eventos/Rutas/*"], + "@altertex/eve/rutasInd/*": ["Eventos/Rutas/RutasIndividuales/*"], + "@altertex/pedidos/*": ["Pedidos/*"], + "@altertex/pedidos/ctrl/*": ["Pedidos/Controladores/*"], + "@altertex/pedidos/datos/*": ["Pedidos/Datos/*"], + "@altertex/pedidos/repos/*": ["Pedidos/Datos/Repositorios/*"], + "@altertex/pedidos/rutas/*": ["Pedidos/Rutas/*"], + "@altertex/pedidos/rutasInd/*": ["Pedidos/Rutas/RutasIndividuales/*"], + "@altertex/pago/ctrl/*": ["Pagos/Controladores/*"], + "@altertex/pago/datos/*": ["Pagos/Datos/*"], + "@altertex/pago/repos/*": ["Pagos/Datos/Repositorios/*"], + "@altertex/pago/rutas/*": ["Pagos/Rutas/*"], + "@altertex/pago/rutas/rutasInd/*": ["Pagos/Rutas/RutasIndividuales/*"] + } + }, + "include": ["**/*.js"] +} diff --git a/package-lock.json b/package-lock.json index e5e6c8ff..8474434f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,41 +1,74 @@ { - "name": "prueba-arquitectura-backend-textiles", + "name": "backend-textiles", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "prueba-arquitectura-backend-textiles", + "name": "backend-textiles", "version": "1.0.0", "license": "ISC", "dependencies": { "@aws-sdk/client-dynamodb": "^3.751.0", - "@aws-sdk/client-s3": "^3.750.0", + "@aws-sdk/client-s3": "^3.808.0", "@aws-sdk/lib-dynamodb": "^3.751.0", + "@aws-sdk/s3-request-presigner": "^3.787.0", + "@aws-sdk/types": "^3.775.0", "aws-sdk": "^2.1692.0", "bcryptjs": "^3.0.0", "body-parser": "^1.20.3", "cookie-parser": "^1.4.7", "cors": "^2.8.5", + "csurf": "^1.11.0", + "date-fns": "^4.1.0", "dotenv": "^16.4.7", + "exceljs": "^4.4.0", "express": "^4.21.2", + "helmet": "^8.1.0", + "i": "^0.3.7", + "json2csv": "^6.0.0-alpha.2", "jsonwebtoken": "^9.0.2", + "module-alias": "^2.2.3", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", + "mysql2": "^3.14.0", + "node-cron": "^3.0.3", + "nodemon": "^3.1.9", "pm2": "^5.4.3", + "qrcode": "^1.5.4", + "redis": "^5.1.0", + "speakeasy": "^2.0.0", + "supertest": "^7.1.0", "swagger-jsdoc": "^6.2.8", - "swagger-ui": "^5.20.0", + "swagger-ui": "^5.21.0", "swagger-ui-express": "^5.0.1" }, "devDependencies": { "@eslint/js": "^9.23.0", - "eslint": "^9.22.0" + "eslint": "^9.22.0", + "eslint-plugin-jsdoc": "^50.6.11", + "jest": "^29.7.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "license": "MIT", "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.6", @@ -47,6 +80,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "license": "MIT", "engines": { "node": ">=10" } @@ -54,12 +88,14 @@ "node_modules/@apidevtools/swagger-methods": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "license": "MIT" }, "node_modules/@apidevtools/swagger-parser": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.6", "@apidevtools/openapi-schemas": "^2.0.4", @@ -76,6 +112,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -89,6 +126,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -99,6 +137,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", @@ -112,6 +151,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -123,6 +163,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -135,6 +176,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -147,6 +189,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -161,6 +204,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -172,6 +216,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -184,6 +229,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -196,6 +242,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -209,6 +256,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" } @@ -217,6 +265,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -227,6 +276,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -238,6 +288,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -250,6 +301,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -259,50 +311,51 @@ } }, "node_modules/@aws-sdk/client-dynamodb": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.758.0.tgz", - "integrity": "sha512-ZdVVCvmQ4wlV22HgYZKndIYNKkFfTLi8PIOF5rOkqthgYRTfVzKajrVbYebCs5jMDTk73LPLl2Ze/EYBEHKlBA==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.808.0.tgz", + "integrity": "sha512-n1y4b+X/GQSGbVfegeoeii8rujBb1bjwdHmyKG5P2HILoVTpyLrO/sqQBDPCokjakZAPmJ2oe8ptqmOSw0Tv9Q==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.758.0", - "@aws-sdk/middleware-endpoint-discovery": "3.734.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/credential-provider-node": "3.808.0", + "@aws-sdk/middleware-endpoint-discovery": "3.808.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.2", + "@smithy/util-waiter": "^4.0.3", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" @@ -312,65 +365,66 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.758.0.tgz", - "integrity": "sha512-f8SlhU9/93OC/WEI6xVJf/x/GoQFj9a/xXK6QCtr5fvCjfSLgMVFmKTiIl/tgtDRzxUDc8YS6EGtbHjJ3Y/atg==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.808.0.tgz", + "integrity": "sha512-8RY3Jsm84twmYfiqnMkxznuY6pBX7y2GiuEJVdW1ZJLXRDOiCPkTBHsO6jUwppfMua7HRhO2OTAdWr7aSBAdPw==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-node": "3.758.0", - "@aws-sdk/middleware-bucket-endpoint": "3.734.0", - "@aws-sdk/middleware-expect-continue": "3.734.0", - "@aws-sdk/middleware-flexible-checksums": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-location-constraint": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-sdk-s3": "3.758.0", - "@aws-sdk/middleware-ssec": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/signature-v4-multi-region": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@aws-sdk/xml-builder": "3.734.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/eventstream-serde-browser": "^4.0.1", - "@smithy/eventstream-serde-config-resolver": "^4.0.1", - "@smithy/eventstream-serde-node": "^4.0.1", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-blob-browser": "^4.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/hash-stream-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/md5-js": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/credential-provider-node": "3.808.0", + "@aws-sdk/middleware-bucket-endpoint": "3.808.0", + "@aws-sdk/middleware-expect-continue": "3.804.0", + "@aws-sdk/middleware-flexible-checksums": "3.808.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-location-constraint": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-sdk-s3": "3.808.0", + "@aws-sdk/middleware-ssec": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/signature-v4-multi-region": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@aws-sdk/xml-builder": "3.804.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", + "@smithy/eventstream-serde-browser": "^4.0.2", + "@smithy/eventstream-serde-config-resolver": "^4.1.0", + "@smithy/eventstream-serde-node": "^4.0.2", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-blob-browser": "^4.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/hash-stream-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/md5-js": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "@smithy/util-stream": "^4.1.2", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.3", + "@smithy/util-stream": "^4.2.0", "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.2", + "@smithy/util-waiter": "^4.0.3", "tslib": "^2.6.2" }, "engines": { @@ -378,46 +432,47 @@ } }, "node_modules/@aws-sdk/client-sso": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.758.0.tgz", - "integrity": "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.808.0.tgz", + "integrity": "sha512-NxGomD0x9q30LPOXf4x7haOm6l2BJdLEzpiC/bPEXUkf2+4XudMQumMA/hDfErY5hCE19mFAouoO465m3Gl3JQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -426,19 +481,20 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.758.0.tgz", - "integrity": "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/core": "^3.1.5", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/signature-v4": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.808.0.tgz", + "integrity": "sha512-+nTmxJVIPtAarGq9Fd/uU2qU/Ngfb9EntT0/kwXdKKMI0wU9fQNWi10xSTVeqOtzWERbQpOJgBAdta+v3W7cng==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/core": "^3.3.1", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/property-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" }, @@ -447,14 +503,15 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.758.0.tgz", - "integrity": "sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.808.0.tgz", + "integrity": "sha512-snPRQnwG9PV4kYHQimo1tenf7P974RcdxkHUThzWSxPEV7HpjxTFYNWGlKbOKBhL4AcgeCVeiZ/j+zveF2lEPA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -462,19 +519,20 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.758.0.tgz", - "integrity": "sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/property-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.808.0.tgz", + "integrity": "sha512-gNXjlx3BIUeX7QpVqxbjBxG6zm45lC39QvUIo92WzEJd2OTPcR8TU0OTTsgq/lpn2FrKcISj5qXvhWykd41+CA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/property-provider": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", + "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -482,22 +540,23 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.758.0.tgz", - "integrity": "sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/credential-provider-env": "3.758.0", - "@aws-sdk/credential-provider-http": "3.758.0", - "@aws-sdk/credential-provider-process": "3.758.0", - "@aws-sdk/credential-provider-sso": "3.758.0", - "@aws-sdk/credential-provider-web-identity": "3.758.0", - "@aws-sdk/nested-clients": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/credential-provider-imds": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.808.0.tgz", + "integrity": "sha512-Y53CW0pCvFQQEvtVFwExCCMbTg+6NOl8b3YOuZVzPmVmDoW7M1JIn9IScesqoGERXL3VoXny6nYTsZj+vfpp7Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/credential-provider-env": "3.808.0", + "@aws-sdk/credential-provider-http": "3.808.0", + "@aws-sdk/credential-provider-process": "3.808.0", + "@aws-sdk/credential-provider-sso": "3.808.0", + "@aws-sdk/credential-provider-web-identity": "3.808.0", + "@aws-sdk/nested-clients": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/credential-provider-imds": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -505,21 +564,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.758.0.tgz", - "integrity": "sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ==", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.758.0", - "@aws-sdk/credential-provider-http": "3.758.0", - "@aws-sdk/credential-provider-ini": "3.758.0", - "@aws-sdk/credential-provider-process": "3.758.0", - "@aws-sdk/credential-provider-sso": "3.758.0", - "@aws-sdk/credential-provider-web-identity": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/credential-provider-imds": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.808.0.tgz", + "integrity": "sha512-lASHlXJ6U5Cpnt9Gs+mWaaSmWcEibr1AFGhp+5UNvfyd+UU2Oiwgbo7rYXygmaVDGkbfXEiTkgYtoNOBSddnWQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.808.0", + "@aws-sdk/credential-provider-http": "3.808.0", + "@aws-sdk/credential-provider-ini": "3.808.0", + "@aws-sdk/credential-provider-process": "3.808.0", + "@aws-sdk/credential-provider-sso": "3.808.0", + "@aws-sdk/credential-provider-web-identity": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/credential-provider-imds": "^4.0.2", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -527,15 +587,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.758.0.tgz", - "integrity": "sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.808.0.tgz", + "integrity": "sha512-ZLqp+xsQUatoo8pMozcfLwf/pwfXeIk0w3n0Lo/rWBgT3RcdECmmPCRcnkYBqxHQyE66aS9HiJezZUwMYPqh6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -543,17 +604,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.758.0.tgz", - "integrity": "sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ==", - "dependencies": { - "@aws-sdk/client-sso": "3.758.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/token-providers": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.808.0.tgz", + "integrity": "sha512-gWZByAokHX+aps1+syIW/hbKUBrjE2RpPRd/RGQvrBbVVgwsJzsHKsW0zy1B6mgARPG6IahmSUMjNkBCVsiAgw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.808.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/token-providers": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -561,15 +623,16 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.758.0.tgz", - "integrity": "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/nested-clients": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.808.0.tgz", + "integrity": "sha512-SsGa1Gfa05aJM/qYOtHmfg0OKKW6Fl6kyMCcai63jWDVDYy0QSHcesnqRayJolISkdsVK6bqoWoFcPxiopcFcg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/nested-clients": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -577,9 +640,10 @@ } }, "node_modules/@aws-sdk/endpoint-cache": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.723.0.tgz", - "integrity": "sha512-2+a4WXRc+07uiPR+zJiPGKSOWaNJQNqitkks+6Hhm/haTLJqNVTgY2OWDh2PXvwMNpKB+AlGdhE65Oy6NzUgXg==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.804.0.tgz", + "integrity": "sha512-TQVDkA/lV6ua75ELZaichMzlp6x7tDa1bqdy/+0ZftmODPtKXuOOEcJxmdN7Ui/YRo1gkRz2D9txYy7IlNg1Og==", + "license": "Apache-2.0", "dependencies": { "mnemonist": "0.38.3", "tslib": "^2.6.2" @@ -589,32 +653,34 @@ } }, "node_modules/@aws-sdk/lib-dynamodb": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.758.0.tgz", - "integrity": "sha512-lkxh7nkFMHY2zbPxhGQz7hVA43yRPu+ERrSiRu7I11arAOz/MJlt7MjHmt0B8x7x6isF1utNixkU28HKh9hgWQ==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/util-dynamodb": "3.758.0", - "@smithy/core": "^3.1.5", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.808.0.tgz", + "integrity": "sha512-okdntX2StMqWHvUqEN/X8HA4B51T19iUWzZ0HTpbUCi4Zo6PEZvmNBSgCFzoGQO3BeYDD0E1OVCGkdTm/PM6Sg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/util-dynamodb": "3.808.0", + "@smithy/core": "^3.3.1", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.758.0" + "@aws-sdk/client-dynamodb": "^3.808.0" } }, "node_modules/@aws-sdk/lib-storage": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.758.0.tgz", - "integrity": "sha512-g07y7rA505zaTJNPTmvW4zYJA3gThFDE1be7kBUKhTKAdwv8jVSbOiAy2AhClXs2evSUoQiFFtD1xWxLRXPPRQ==", - "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/smithy-client": "^4.1.6", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.808.0.tgz", + "integrity": "sha512-gEdiBuqPjmMA0Z2RtppIdcMQH1KDeRM//+DWcVC9gU9pMJ5S/LEwCmWEVh3/C2n/Sehv71b7x0GKGxHO/yBrwA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/smithy-client": "^4.2.4", "buffer": "5.6.0", "events": "3.3.0", "stream-browserify": "3.0.0", @@ -624,13 +690,14 @@ "node": ">=18.0.0" }, "peerDependencies": { - "@aws-sdk/client-s3": "^3.758.0" + "@aws-sdk/client-s3": "^3.808.0" } }, "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "license": "MIT", "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -640,20 +707,22 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", "engines": { "node": ">=0.8.x" } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.734.0.tgz", - "integrity": "sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-arn-parser": "3.723.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.808.0.tgz", + "integrity": "sha512-wEPlNcs8dir9lXbuviEGtSzYSxG/NRKQrJk5ybOc7OpPGHovsN+QhDOdY3lcjOFdwMTiMIG9foUkPz3zBpLB1A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", "tslib": "^2.6.2" }, @@ -662,15 +731,16 @@ } }, "node_modules/@aws-sdk/middleware-endpoint-discovery": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.734.0.tgz", - "integrity": "sha512-hE3x9Sbqy64g/lcFIq7BF9IS1tSOyfBCyHf1xBgevWeFIDTWh647URuCNWoEwtw4HMEhO2MDUQcKf1PFh1dNDA==", - "dependencies": { - "@aws-sdk/endpoint-cache": "3.723.0", - "@aws-sdk/types": "3.734.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.808.0.tgz", + "integrity": "sha512-h8LAIO6tuA0JAahrg+oSIVZpb6rhJOFVDDqYNQVp6ZdawlIzpZcc1sa+XVZvarBnThNKqvLTSGK7boSRmaLAwg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/endpoint-cache": "3.804.0", + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -678,13 +748,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.734.0.tgz", - "integrity": "sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.804.0.tgz", + "integrity": "sha512-YW1hySBolALMII6C8y7Z0CRG2UX1dGJjLEBNFeefhO/xP7ZuE1dvnmfJGaEuBMnvc3wkRS63VZ3aqX6sevM1CA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -692,21 +763,22 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.758.0.tgz", - "integrity": "sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.808.0.tgz", + "integrity": "sha512-NW1yoTYDH2h8ycqMPNkvW3d1XT2vEeXfXclagL2tv82P7Qt7vPXYcObs/YtETvNZ7hdnmOftJ/IJv7YrFC8vtQ==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -715,13 +787,14 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz", - "integrity": "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.804.0.tgz", + "integrity": "sha512-bum1hLVBrn2lJCi423Z2fMUYtsbkGI2s4N+2RI2WSjvbaVyMSv/WcejIrjkqiiMR+2Y7m5exgoKeg4/TODLDPQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -729,12 +802,13 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.734.0.tgz", - "integrity": "sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.804.0.tgz", + "integrity": "sha512-AMtKnllIWKgoo7hiJfphLYotEwTERfjVMO2+cKAncz9w1g+bnYhHxiVhJJoR94y047c06X4PU5MsTxvdQ73Znw==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.804.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -742,12 +816,13 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.734.0.tgz", - "integrity": "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.804.0.tgz", + "integrity": "sha512-w/qLwL3iq0KOPQNat0Kb7sKndl9BtceigINwBU7SpkYWX9L/Lem6f8NPEKrC9Tl4wDBht3Yztub4oRTy/horJA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.804.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -755,13 +830,14 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz", - "integrity": "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.804.0.tgz", + "integrity": "sha512-zqHOrvLRdsUdN/ehYfZ9Tf8svhbiLLz5VaWUz22YndFv6m9qaAcijkpAOlKexsv3nLBMJdSdJ6GUTAeIy3BZzw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -769,22 +845,23 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.758.0.tgz", - "integrity": "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-arn-parser": "3.723.0", - "@smithy/core": "^3.1.5", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/signature-v4": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.808.0.tgz", + "integrity": "sha512-qvyJTDf0HIsPpZzBUqhNQm5g8stAn2EOwVsaAolsOHuBsdaBAE/s/NgPzazDlSXwdF0ITvsIouUVDCn4fJGJqQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-arn-parser": "3.804.0", + "@smithy/core": "^3.3.1", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -793,12 +870,13 @@ } }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.734.0.tgz", - "integrity": "sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.804.0.tgz", + "integrity": "sha512-Tk8jK0gOIUBvEPTz/wwSlP1V70zVQ3QYqsLPAjQRMO6zfOK9ax31dln3MgKvFDJxBydS2tS3wsn53v+brxDxTA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.804.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -806,16 +884,17 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.758.0.tgz", - "integrity": "sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg==", - "dependencies": { - "@aws-sdk/core": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@smithy/core": "^3.1.5", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.808.0.tgz", + "integrity": "sha512-VckV6l5cf/rL3EtgzSHVTTD4mI0gd8UxDDWbKJsxbQ2bpNPDQG2L1wWGLaolTSzjEJ5f3ijDwQrNDbY9l85Mmg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@smithy/core": "^3.3.1", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -823,46 +902,47 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.758.0.tgz", - "integrity": "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.808.0.tgz", + "integrity": "sha512-NparPojwoBul7XPCasy4psFMJbw7Ys4bz8lVB93ljEUD4VV7mM7zwK27Uhz20B8mBFGmFEoAprPsVymJcK9Vcw==", + "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.758.0", - "@aws-sdk/middleware-host-header": "3.734.0", - "@aws-sdk/middleware-logger": "3.734.0", - "@aws-sdk/middleware-recursion-detection": "3.734.0", - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/region-config-resolver": "3.734.0", - "@aws-sdk/types": "3.734.0", - "@aws-sdk/util-endpoints": "3.743.0", - "@aws-sdk/util-user-agent-browser": "3.734.0", - "@aws-sdk/util-user-agent-node": "3.758.0", - "@smithy/config-resolver": "^4.0.1", - "@smithy/core": "^3.1.5", - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/hash-node": "^4.0.1", - "@smithy/invalid-dependency": "^4.0.1", - "@smithy/middleware-content-length": "^4.0.1", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-retry": "^4.0.7", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/protocol-http": "^5.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", + "@aws-sdk/core": "3.808.0", + "@aws-sdk/middleware-host-header": "3.804.0", + "@aws-sdk/middleware-logger": "3.804.0", + "@aws-sdk/middleware-recursion-detection": "3.804.0", + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/region-config-resolver": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-endpoints": "3.808.0", + "@aws-sdk/util-user-agent-browser": "3.804.0", + "@aws-sdk/util-user-agent-node": "3.808.0", + "@smithy/config-resolver": "^4.1.2", + "@smithy/core": "^3.3.1", + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/hash-node": "^4.0.2", + "@smithy/invalid-dependency": "^4.0.2", + "@smithy/middleware-content-length": "^4.0.2", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/middleware-retry": "^4.1.5", + "@smithy/middleware-serde": "^4.0.3", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.7", - "@smithy/util-defaults-mode-node": "^4.0.7", - "@smithy/util-endpoints": "^3.0.1", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", + "@smithy/util-defaults-mode-browser": "^4.0.12", + "@smithy/util-defaults-mode-node": "^4.0.12", + "@smithy/util-endpoints": "^3.0.4", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.3", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" }, @@ -871,15 +951,35 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.734.0.tgz", - "integrity": "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==", - "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.808.0.tgz", + "integrity": "sha512-9x2QWfphkARZY5OGkl9dJxZlSlYM2l5inFeo2bKntGuwg4A4YUe5h7d5yJ6sZbam9h43eBrkOdumx03DAkQF9A==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/types": "^4.2.0", "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", + "@smithy/util-middleware": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/s3-request-presigner": { + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.808.0.tgz", + "integrity": "sha512-viKWGrb7ZTN2rDkjX5rSkPeyKEVFDiyJS8HWVLBUJGwOLb65wluX+Rr/9fuCFyhCbBzhzpXFdzczZ0kCHOM6Qw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/signature-v4-multi-region": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@aws-sdk/util-format-url": "3.804.0", + "@smithy/middleware-endpoint": "^4.1.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/smithy-client": "^4.2.4", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -887,15 +987,16 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.758.0.tgz", - "integrity": "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/signature-v4": "^5.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.808.0.tgz", + "integrity": "sha512-lQuEB6JK81eKV7fdiktmRq06Y1KCcJbx9fLf7b19nSfYUbJSn/kfSpHPv/tOkJK2HKnN61JsfG19YU8k4SOU8Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/signature-v4": "^5.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -903,15 +1004,16 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.758.0.tgz", - "integrity": "sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w==", - "dependencies": { - "@aws-sdk/nested-clients": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.808.0.tgz", + "integrity": "sha512-PsfKanHmnyO7FxowXqxbLQ+QjURCdSGxyhUiSdZbfvlvme/wqaMyIoMV/i4jppndksoSdPbW2kZXjzOqhQF+ew==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/nested-clients": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -919,11 +1021,12 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.734.0.tgz", - "integrity": "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.804.0.tgz", + "integrity": "sha512-A9qnsy9zQ8G89vrPPlNG9d1d8QcKRGqJKqwyGgS0dclJpwy6d1EWgQLIolKPl6vcFpLoe6avLOLxr+h8ur5wpg==", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -931,9 +1034,10 @@ } }, "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.723.0.tgz", - "integrity": "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", + "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -942,9 +1046,10 @@ } }, "node_modules/@aws-sdk/util-dynamodb": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.758.0.tgz", - "integrity": "sha512-JjBbhJLajilyMWJ/z82bYgIMj9XGISZ/QMYSpNBdzGFRmL1AL9s6NwLB6FuquRvpY9Lo3Y5vwEbedqdZPIrRFg==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.808.0.tgz", + "integrity": "sha512-ydmqhKaT5rb2NClClbGRSjrW1kAJhgEMIP7B+drvhSBggMmTyTLrJCd9Be3Be9ce1oEXn03NbY1rcwtTusfdoA==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -952,17 +1057,33 @@ "node": ">=18.0.0" }, "peerDependencies": { - "@aws-sdk/client-dynamodb": "^3.758.0" + "@aws-sdk/client-dynamodb": "^3.808.0" } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.743.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.743.0.tgz", - "integrity": "sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.808.0.tgz", + "integrity": "sha512-N6Lic98uc4ADB7fLWlzx+1uVnq04VgVjngZvwHoujcRg9YDhIg9dUDiTzD5VZv13g1BrPYmvYP1HhsildpGV6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.804.0", + "@smithy/types": "^4.2.0", + "@smithy/util-endpoints": "^3.0.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-format-url": { + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.804.0.tgz", + "integrity": "sha512-1nOwSg7B0bj5LFGor0udF/HSdvDuSCxP+NC0IuSOJ5RgJ2AphFo03pLtK2UwArHY5WWZaejAEz5VBND6xxOEhA==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", - "@smithy/util-endpoints": "^3.0.1", + "@aws-sdk/types": "3.804.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -970,9 +1091,10 @@ } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.723.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.723.0.tgz", - "integrity": "sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", + "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", + "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" }, @@ -981,25 +1103,27 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.734.0.tgz", - "integrity": "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.804.0.tgz", + "integrity": "sha512-KfW6T6nQHHM/vZBBdGn6fMyG/MgX5lq82TDdX4HRQRRuHKLgBWGpKXqqvBwqIaCdXwWHgDrg2VQups6GqOWW2A==", + "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.734.0", - "@smithy/types": "^4.1.0", + "@aws-sdk/types": "3.804.0", + "@smithy/types": "^4.2.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.758.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.758.0.tgz", - "integrity": "sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw==", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.758.0", - "@aws-sdk/types": "3.734.0", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", + "version": "3.808.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.808.0.tgz", + "integrity": "sha512-5UmB6u7RBSinXZAVP2iDgqyeVA/odO2SLEcrXaeTCw8ICXEoqF0K+GL36T4iDbzCBOAIugOZ6OcQX5vH3ck5UA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.808.0", + "@aws-sdk/types": "3.804.0", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -1015,98 +1139,80 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.734.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.734.0.tgz", - "integrity": "sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==", + "version": "3.804.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.804.0.tgz", + "integrity": "sha512-JbGWp36IG9dgxtvC6+YXwt5WDZYfuamWFtVfK6fQpnmL96dx+GUPOXPKRWdw67WLKf2comHY28iX2d3z35I53Q==", + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", + "@smithy/types": "^4.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", "dependencies": { - "regenerator-runtime": "^0.14.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/runtime-corejs3": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.26.9.tgz", - "integrity": "sha512-5EVjbTegqN7RSJle6hMWYxO4voo4rI+9krITk+DWR+diJgGrjZjrIBnJhjrHYYQsFgI7j1w1QnrvV7YSKBfYGg==", - "dependencies": { - "core-js-pure": "^3.30.2", - "regenerator-runtime": "^0.14.0" - }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=6.9.0" }, "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@eslint/config-array/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -1119,2728 +1225,6309 @@ } } }, - "node_modules/@eslint/config-array/node_modules/ms": { + "node_modules/@babel/core/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/@eslint/config-helpers": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", - "integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", "dev": true, + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", - "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { - "node": ">=6.0" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, - "dependencies": { - "@eslint/core": "^0.12.0", - "levn": "^0.4.1" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6.9.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=18.18.0" + "node": ">=6.9.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, + "license": "MIT", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { - "node": ">=18.18.0" + "node": ">=6.9.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", "dev": true, - "engines": { - "node": ">=18.18" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "engines": { - "node": ">=12.22" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "engines": { - "node": ">=18.18" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" - }, - "node_modules/@pm2/agent": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.4.tgz", - "integrity": "sha512-n7WYvvTJhHLS2oBb1PjOtgLpMhgImOq8sXkPBw6smeg9LJBWZjiEgPKOpR8mn9UJZsB5P3W4V/MyvNnp31LKeA==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", "dependencies": { - "async": "~3.2.0", - "chalk": "~3.0.0", - "dayjs": "~1.8.24", - "debug": "~4.3.1", - "eventemitter2": "~5.0.1", - "fast-json-patch": "^3.0.0-1", - "fclone": "~1.0.11", - "nssocket": "0.6.0", - "pm2-axon": "~4.0.1", - "pm2-axon-rpc": "~0.7.0", - "proxy-agent": "~6.3.0", - "semver": "~7.5.0", - "ws": "~7.5.10" + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/agent/node_modules/dayjs": { - "version": "1.8.36", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", - "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==" - }, - "node_modules/@pm2/agent/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=6.0" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/agent/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/@pm2/agent/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/io": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.0.1.tgz", - "integrity": "sha512-KiA+shC6sULQAr9mGZ1pg+6KVW9MF8NpG99x26Lf/082/Qy8qsTCtnJy+HQReW1A9Rdf0C/404cz0RZGZro+IA==", + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", "dependencies": { - "async": "~2.6.1", - "debug": "~4.3.1", - "eventemitter2": "^6.3.1", - "require-in-the-middle": "^5.0.0", - "semver": "~7.5.4", - "shimmer": "^1.2.0", - "signal-exit": "^3.0.3", - "tslib": "1.9.3" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/@pm2/io/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dependencies": { - "lodash": "^4.17.14" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/io/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=6.0" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/io/node_modules/eventemitter2": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", - "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" - }, - "node_modules/@pm2/io/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/io/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/@pm2/io/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/io/node_modules/tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" - }, - "node_modules/@pm2/js-api": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.8.0.tgz", - "integrity": "sha512-nmWzrA/BQZik3VBz+npRcNIu01kdBhWL0mxKmP1ciF/gTcujPTQqt027N9fc1pK9ERM8RipFhymw7RcmCyOEYA==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", "dependencies": { - "async": "^2.6.3", - "debug": "~4.3.1", - "eventemitter2": "^6.3.1", - "extrareqp2": "^1.0.0", - "ws": "^7.0.0" + "@babel/helper-plugin-utils": "^7.10.4" }, - "engines": { - "node": ">=4.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/js-api/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", "dependencies": { - "lodash": "^4.17.14" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/js-api/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/js-api/node_modules/eventemitter2": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", - "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" - }, - "node_modules/@pm2/js-api/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/@pm2/pm2-version-check": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", - "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.3.1" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/pm2-version-check/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=6.0" + "node": ">=6.9.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@pm2/pm2-version-check/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/@scarf/scarf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true - }, - "node_modules/@smithy/abort-controller": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.1.tgz", - "integrity": "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", - "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", - "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", - "dependencies": { - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" } }, - "node_modules/@smithy/config-resolver": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.0.1.tgz", - "integrity": "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==", + "node_modules/@babel/runtime-corejs3": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.1.tgz", + "integrity": "sha512-909rVuj3phpjW6y0MCXAZ5iNeORePa6ldJvp2baWGcTjwqbBDDz6xoS5JHJ7lS88NlwLYj07ImL/8IUMtDZzTA==", + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "tslib": "^2.6.2" + "core-js-pure": "^3.30.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" } }, - "node_modules/@smithy/core": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.1.5.tgz", - "integrity": "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA==", + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/middleware-serde": "^4.0.2", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-stream": "^4.1.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" } }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.1.tgz", - "integrity": "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==", + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "tslib": "^2.6.2" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" } }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.1.tgz", - "integrity": "sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==", + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.1.0", - "@smithy/util-hex-encoding": "^4.0.0", - "tslib": "^2.6.2" + "ms": "^2.1.3" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.1.tgz", - "integrity": "sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=4" } }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.0.1.tgz", - "integrity": "sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==", + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.9.0" } }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.1.tgz", - "integrity": "sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.49.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz", + "integrity": "sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=16" } }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.1.tgz", - "integrity": "sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/eventstream-codec": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.1.tgz", - "integrity": "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==", - "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.1.tgz", - "integrity": "sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw==", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.0.0", - "@smithy/chunked-blob-reader-native": "^4.0.0", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@smithy/hash-node": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.1.tgz", - "integrity": "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==", + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.1.tgz", - "integrity": "sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw==", + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "ms": "^2.1.3" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.1.tgz", - "integrity": "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==", - "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "tslib": "^2.6.2" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@smithy/md5-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.1.tgz", - "integrity": "sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.1.tgz", - "integrity": "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==", + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "ms": "^2.1.3" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.0.6.tgz", - "integrity": "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg==", - "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-serde": "^4.0.2", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "@smithy/url-parser": "^4.0.1", - "@smithy/util-middleware": "^4.0.1", - "tslib": "^2.6.2" - }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.26.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz", + "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@smithy/middleware-retry": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.0.7.tgz", - "integrity": "sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ==", - "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/service-error-classification": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-retry": "^4.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@smithy/middleware-serde": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.2.tgz", - "integrity": "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==", + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" }, "engines": { - "node": ">=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@smithy/middleware-stack": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.1.tgz", - "integrity": "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==", + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" } }, - "node_modules/@smithy/node-config-provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.0.1.tgz", - "integrity": "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==", + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "license": "MIT", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/shared-ini-file-loader": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=18.18.0" } }, - "node_modules/@smithy/node-http-handler": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.3.tgz", - "integrity": "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA==", + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/querystring-builder": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=18.18.0" } }, - "node_modules/@smithy/property-provider": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.1.tgz", - "integrity": "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==", - "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@smithy/protocol-http": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.0.1.tgz", - "integrity": "sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==", - "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@smithy/querystring-builder": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.1.tgz", - "integrity": "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", "dependencies": { - "@smithy/types": "^4.1.0", - "@smithy/util-uri-escape": "^4.0.0", - "tslib": "^2.6.2" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@smithy/querystring-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.1.tgz", - "integrity": "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" + "sprintf-js": "~1.0.2" } }, - "node_modules/@smithy/service-error-classification": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.1.tgz", - "integrity": "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.1.tgz", - "integrity": "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=18.0.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@smithy/signature-v4": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.0.1.tgz", - "integrity": "sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.1", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@smithy/smithy-client": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.1.6.tgz", - "integrity": "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw==", - "dependencies": { - "@smithy/core": "^3.1.5", - "@smithy/middleware-endpoint": "^4.0.6", - "@smithy/middleware-stack": "^4.0.1", - "@smithy/protocol-http": "^5.0.1", - "@smithy/types": "^4.1.0", - "@smithy/util-stream": "^4.1.2", - "tslib": "^2.6.2" + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@smithy/types": { + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.1.0.tgz", - "integrity": "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@smithy/url-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.1.tgz", - "integrity": "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==", - "dependencies": { - "@smithy/querystring-parser": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" - }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=8" } }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", - "dependencies": { - "tslib": "^2.6.2" + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "engines": { - "node": ">=18.0.0" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "tslib": "^2.6.2" + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.7.tgz", - "integrity": "sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q==", + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" + "jest-get-type": "^29.6.3" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.7.tgz", - "integrity": "sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ==", - "dependencies": { - "@smithy/config-resolver": "^4.0.1", - "@smithy/credential-provider-imds": "^4.0.1", - "@smithy/node-config-provider": "^4.0.1", - "@smithy/property-provider": "^4.0.1", - "@smithy/smithy-client": "^4.1.6", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-endpoints": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.1.tgz", - "integrity": "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/node-config-provider": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-middleware": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.1.tgz", - "integrity": "sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==", + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-retry": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.1.tgz", - "integrity": "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/service-error-classification": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-stream": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.1.2.tgz", - "integrity": "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/fetch-http-handler": "^5.0.1", - "@smithy/node-http-handler": "^4.0.3", - "@smithy/types": "^4.1.0", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "tslib": "^2.6.2" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@smithy/util-waiter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.2.tgz", - "integrity": "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", "dependencies": { - "@smithy/abort-controller": "^4.0.1", - "@smithy/types": "^4.1.0", - "tslib": "^2.6.2" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": ">=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@swagger-api/apidom-ast": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.12.tgz", - "integrity": "sha512-KdJ+8PyYvfnHgpqrC0WWDRJLVx6+YkmYgAGpsdOa8S/p6btJdCUozeqpcXawmGqwAX/9jCXbmKdia3v3fUrP0w==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "unraw": "^3.0.0" + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@swagger-api/apidom-core": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.12.tgz", - "integrity": "sha512-CAr6aSk9l9ZJUneHpmwk4Br0NZhFLy2QRHoPmr2pWMlAn+0YC4eRYtwOEB8PVsCmP83D4MiXU5zi6cOZyV/cVw==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "minim": "~0.23.8", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "short-unique-id": "^5.0.2", - "ts-mixer": "^6.0.3" + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@swagger-api/apidom-error": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.12.tgz", - "integrity": "sha512-p74a/8GgitGIYvjD5WmROEHv2bGCnDKug3QpJvC5+g36ErZQp428+fK5hhfKQuCo0rjD2fZvs27S17Zh8y0zFw==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@swagger-api/apidom-json-pointer": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.12.tgz", - "integrity": "sha512-JuCqMVfDSWJ7JcdPjYgGjNlqjmKQwxuQh7uKKBLTpNccmXYT+x7WemPuzcWjVVHDd5plw8yQ0YvaU0HlqjS1mA==", + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.2.tgz", + "integrity": "sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.3", + "eventsource": "^3.0.2", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@swagger-api/apidom-ns-api-design-systems": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-beta.12.tgz", - "integrity": "sha512-D4MAnm1Jiame1KfxkboYU/gRsvlDaplFE3SGjdg/dG3vTOHWXzm5ta8pEf3naPuo8+fXt0rcMxf2edaFHnPLWA==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@swagger-api/apidom-ns-asyncapi-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-beta.12.tgz", - "integrity": "sha512-3R1AdZdUNo2rw9PudkWfP0f556DFTjUn9mBdbLHQPhcmdIRTJQAMDNy2FhN6ZiEg4ggG31Hyk2AY/97CAxHd6A==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.3" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@swagger-api/apidom-ns-json-schema-2019-09": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.12.tgz", - "integrity": "sha512-mrcWwAfCcUDiPrGymowZqnrOpOk7hUNDkW9WjsMe3bFiTrCm4EsQYvGtyWAtB/0yo7hNBMGXYEtDWfGBsw8AyA==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.4" + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@swagger-api/apidom-ns-json-schema-2020-12": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.12.tgz", - "integrity": "sha512-SW0Jtty3o12OwpTAVJEewurvTSIhxJ72TZlMSk5L36jvekzqKfLL7aBYRCEE9QkV3rxTjxOf0WK/tYLRMKUbzw==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-2019-09": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.4" + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" } }, - "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.12.tgz", - "integrity": "sha512-Z3PnEEdkGnr6zomFAgmkkDGrwlj3bbbEJBfXsshxRuXf3i5RymiURFy42CfKa5Tmx3rw8rSw393p0TkHqS0NIg==", + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-beta.12", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.4" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.12.tgz", - "integrity": "sha512-QvubeYZvRd19Q8VVP4xGGYTuSVgLQqEp/epe8LXcrFJvgF6A9CTUxkfKVxL4+Q5a9DFaKTZKNYwkRaPzisvnWQ==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.4" + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.12.tgz", - "integrity": "sha512-UIU/vY5xBhYeBEykmXMvQRaIXqWWNWc/RPG5L8LrfILoZhzZbjqcdRMf5w4wQWqteQxXxkpDdkcHVBsJxcQtJg==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.4" + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@swagger-api/apidom-ns-openapi-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-beta.12.tgz", - "integrity": "sha512-61I3NcH2agyPmNXW7JOoxshjVr7YVekHnEaYfl3VYTc0mT2KcRhcDWM0cufQdGeIJPR9SdFcSZ01aRQUUTj3fQ==", - "optional": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.3" + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/@swagger-api/apidom-ns-openapi-3-0": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.12.tgz", - "integrity": "sha512-6TWUagR1/Y9HB8t75/vrkHHDV5c5K0S72Wywx7PoDyNgQ1Jxy3p6iwuSHfTwJYH+/hAxg3f91i6HXXyrHB5RAg==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.3" + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/@swagger-api/apidom-ns-openapi-3-1": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.12.tgz", - "integrity": "sha512-IayaLSawWo5rAyM2nRY6faTfK8cJQ+mGGR94NOmsjcUQw9IljY9uX7PXj3izOdFlXFYjgR1P+mIhuuXyDuw4qg==", - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-beta.12", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-json-pointer": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.3" + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/@swagger-api/apidom-ns-workflows-1": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-workflows-1/-/apidom-ns-workflows-1-1.0.0-beta.12.tgz", - "integrity": "sha512-ALQbORmsql7HJjlCWMzOfTIqc0O0gCJbp3je+uzp2Y3Cu2BlQgu7aZAGly+GdM1rWNJosm0ZOGG1KTfgJaTZxw==", - "optional": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "ts-mixer": "^6.0.3" + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-beta.12.tgz", - "integrity": "sha512-DjFZmSmoMmSu9gHWcpWGuaZd5o2eD5xkhHwL2QjvFvH7UXBxxhrx89RwNmHt1Hy5De4fV+zlB/7TsL7FsV4i8Q==", - "optional": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-beta.12.tgz", - "integrity": "sha512-bWJ0KylVPNeAqI/KPqaT1PfmIlWFx7fY5MBsIccn9iSB880oUSB+XLmIRpFBOSh5iPM7Dn6GTg3gdnVJRk5fNA==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-beta.12.tgz", - "integrity": "sha512-UAbPIKHNYUy4MOWGyPSkafgipX0zwndSidqG9AUzeDe4t5yldnBRPnCTnUHecSqktIzq5Tz6mViNTc1/uY9lOg==", - "optional": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-beta.12.tgz", - "integrity": "sha512-gT6Z2ReDxELPE6ZzDxf/wQM+AcG13eXGLDcYTOOKacBruWsh8Aa/iF9ZW0DlJckE+vlDgvbhlkxsiHIExOY41g==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@swagger-api/apidom-parser-adapter-json": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-beta.12.tgz", - "integrity": "sha512-Bt7oCylNzf49MRsnnWayIqh2QBIVRGq35k/dcmb0J8QP94GDLfbOCXn0kvuRJvQIK/aJFlBFVMVn47GKQibqfg==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-beta.12", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "tree-sitter": "=0.22.1", - "tree-sitter-json": "=0.24.8", - "web-tree-sitter": "=0.24.5" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@swagger-api/apidom-parser-adapter-json/node_modules/tree-sitter": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.1.tgz", - "integrity": "sha512-gRO+jk2ljxZlIn20QRskIvpLCMtzuLl5T0BY6L9uvPYD17uUrxlxWkvYCiVqED2q2q7CVtY52Uex4WcYo2FEXw==", - "hasInstallScript": true, - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "license": "MIT", "dependencies": { - "node-addon-api": "^8.2.1", - "node-gyp-build": "^4.8.2" + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-beta.12.tgz", - "integrity": "sha512-zMrLeDvDOCGgMNYMW9iuAlOtA+mCa4msBM70tgVdg/89SdS4K5MxVptmpRHQAODdv1oErm2ChVmzFcuPHH38qw==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" } }, - "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-beta.12.tgz", - "integrity": "sha512-tJznOQ+8iEOfKU01hLt6FHLgsRfd5zugnNFuNTvS7oJt6xtQ9vqFS/uKajMSOq6p+irAF6dWI+C5f+1AdDOvnw==", - "optional": true, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-beta.12.tgz", - "integrity": "sha512-HLToO8Jqo06p70h3MWA2FkkNSfRi2M9fjNW3V94nCb6ECMIfgppgw+FDwawskvBNH6RfZqN7OBgq19Vly/sgbw==", - "optional": true, - "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-beta.12.tgz", - "integrity": "sha512-mdg1/80lkoMVla3rvH7GeIuyj70YONJ3CnnBKJ/FIsFjgAViiC3mT5UnP6HmNQ+ZhAl1IvTmkdeI4GQsNtuW/g==", - "optional": true, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "@noble/hashes": "^1.1.5" } }, - "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-beta.12.tgz", - "integrity": "sha512-vUgsJjoItuL+6yOxAFzuMEdPsL3qzwvqZnlwXSPXyCdnzrChzfmWM083LvxyyuQQaBRAhzoYcxSsavZq9MQuUg==", - "optional": true, + "node_modules/@pm2/agent": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.4.tgz", + "integrity": "sha512-n7WYvvTJhHLS2oBb1PjOtgLpMhgImOq8sXkPBw6smeg9LJBWZjiEgPKOpR8mn9UJZsB5P3W4V/MyvNnp31LKeA==", + "license": "AGPL-3.0", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "async": "~3.2.0", + "chalk": "~3.0.0", + "dayjs": "~1.8.24", + "debug": "~4.3.1", + "eventemitter2": "~5.0.1", + "fast-json-patch": "^3.0.0-1", + "fclone": "~1.0.11", + "nssocket": "0.6.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.0", + "proxy-agent": "~6.3.0", + "semver": "~7.5.0", + "ws": "~7.5.10" } }, - "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-beta.12.tgz", - "integrity": "sha512-HHKxKrs99UZmymMScnyEz8VYwicJj78H0iLsuYjIJDggtvKx/kHxTM16/vAe9et7q/uP+BqP/hyUKNeS7n23Kw==", - "optional": true, + "node_modules/@pm2/agent/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@swagger-api/apidom-parser-adapter-workflows-json-1": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-json-1/-/apidom-parser-adapter-workflows-json-1-1.0.0-beta.12.tgz", - "integrity": "sha512-soKD4N7JUvgiPRdsWGJ53itp5mcueoSvb6ikcMneEOu9wxL3y40aCK5Vb76UuVKRZmqWRXpgs3kl5oL34Bno9Q==", - "optional": true, + "node_modules/@pm2/agent/node_modules/dayjs": { + "version": "1.8.36", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", + "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==", + "license": "MIT" + }, + "node_modules/@pm2/agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-workflows-1": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/@swagger-api/apidom-parser-adapter-workflows-yaml-1": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-workflows-yaml-1/-/apidom-parser-adapter-workflows-yaml-1-1.0.0-beta.12.tgz", - "integrity": "sha512-+1GZknZH3shdViUubKTCOolZzday+h3Cxp9PQDb8LgGJcxu40HHf44YZdZNsmkDLXqd2t7+NGbt2EXum7CTgtA==", - "optional": true, + "node_modules/@pm2/agent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-ns-workflows-1": "^1.0.0-beta.12", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-beta.12.tgz", - "integrity": "sha512-SP5Sz1ywsW3vZxrl+/NBGDNvP/rZJ8tm8+0OQJ+HISwcpwSR92rYDUEYBuuxPX1Bw4c1V0UkQqqEVf59NksCsQ==", - "optional": true, + "node_modules/@pm2/agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@pm2/agent/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-ast": "^1.0.0-beta.12", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@swagger-api/apidom-error": "^1.0.0-beta.12", - "@tree-sitter-grammars/tree-sitter-yaml": "=0.7.0", - "@types/ramda": "~0.30.0", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0", - "tree-sitter": "=0.22.1", - "web-tree-sitter": "=0.24.5" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2/node_modules/@tree-sitter-grammars/tree-sitter-yaml": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@tree-sitter-grammars/tree-sitter-yaml/-/tree-sitter-yaml-0.7.0.tgz", - "integrity": "sha512-GOMIK3IaDvECD0eZEhAsLl03RMtM1E8StxuGMn6PpMKFg7jyQ+jSzxJZ4Jmc/tYitah9/AECt8o4tlRQ5yEZQg==", - "hasInstallScript": true, - "optional": true, + "node_modules/@pm2/agent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@pm2/io": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.0.1.tgz", + "integrity": "sha512-KiA+shC6sULQAr9mGZ1pg+6KVW9MF8NpG99x26Lf/082/Qy8qsTCtnJy+HQReW1A9Rdf0C/404cz0RZGZro+IA==", + "license": "Apache-2", "dependencies": { - "node-addon-api": "^8.3.0", - "node-gyp-build": "^4.8.4" + "async": "~2.6.1", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "~7.5.4", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" }, - "peerDependencies": { - "tree-sitter": "^0.22.1" + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@pm2/io/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" }, "peerDependenciesMeta": { - "tree-sitter": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/io/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "license": "MIT" + }, + "node_modules/@pm2/io/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@pm2/io/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "license": "Apache-2.0" + }, + "node_modules/@pm2/io/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@pm2/js-api": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.8.0.tgz", + "integrity": "sha512-nmWzrA/BQZik3VBz+npRcNIu01kdBhWL0mxKmP1ciF/gTcujPTQqt027N9fc1pK9ERM8RipFhymw7RcmCyOEYA==", + "license": "Apache-2", + "dependencies": { + "async": "^2.6.3", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "extrareqp2": "^1.0.0", + "ws": "^7.0.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pm2/js-api/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/js-api/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pm2/js-api/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==", + "license": "MIT" + }, + "node_modules/@pm2/js-api/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@pm2/pm2-version-check": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", + "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1" + } + }, + "node_modules/@pm2/pm2-version-check/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { "optional": true } } }, - "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2/node_modules/tree-sitter": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.1.tgz", - "integrity": "sha512-gRO+jk2ljxZlIn20QRskIvpLCMtzuLl5T0BY6L9uvPYD17uUrxlxWkvYCiVqED2q2q7CVtY52Uex4WcYo2FEXw==", - "hasInstallScript": true, - "optional": true, + "node_modules/@pm2/pm2-version-check/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@redis/bloom": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.1.0.tgz", + "integrity": "sha512-Gp5RWvVKbvItMU2sd848yhY/BnigToz8H4PYcvlBBSP5cQ3lVP1LMh5Kx2CYBNzCdDabVicwBKNvaoLBqPNqIg==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, + "node_modules/@redis/client": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.1.0.tgz", + "integrity": "sha512-FMD35y2KgCWTBLOfF0MhwDSaIVcu5mOUuTV9Kw3JOWHMgON3+ulht31cjTB/gph0BfD1vzUvCeROeRaf/d+35w==", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.1.0.tgz", + "integrity": "sha512-laXZt1Rlimk3py5ZoABBnd4xn/8dWbLUWGvVS7avgMhdczS+eWtXpElilJbFpc+7ZpVlol4vaSGFuR8ThDcTFw==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, + "node_modules/@redis/search": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.1.0.tgz", + "integrity": "sha512-afQYMeIdWGNPjr84GxxgJVkolxMW3BBrlNOwhRHPdzbdGh0rTUPjOJpGHBig34ostHX6AhZ6QwqceU1zLluDEg==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, + "node_modules/@redis/time-series": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.1.0.tgz", + "integrity": "sha512-BjKndLXREPeb4ZtxrI6z1bz2FbzBj7LnWyyevNk6Wd7VRWQ3W10LSvJAJwguPD62mSqpTPhy0lMPqFJwo0gS7A==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.1.0" + } + }, + "node_modules/@scarf/scarf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", + "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", + "hasInstallScript": true, + "license": "Apache-2.0" + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.2.tgz", + "integrity": "sha512-Sl/78VDtgqKxN2+1qduaVE140XF+Xg+TafkncspwM4jFP/LHr76ZHmIY/y3V1M0mMLNk+Je6IGbzxy23RSToMw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", + "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/chunked-blob-reader-native": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", + "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.2.tgz", + "integrity": "sha512-7r6mZGwb5LmLJ+zPtkLoznf2EtwEuSWdtid10pjGl/7HefCE4mueOkrfki8JCUm99W6UfP47/r3tbxx9CfBN5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.1", + "@smithy/types": "^4.2.0", + "@smithy/util-config-provider": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.3.2.tgz", + "integrity": "sha512-GlLv+syoWolhtjX12XplL9BXBu10cjjD8iQC69fiKTrVNOB3Fjt8CVI9ccm6G3bLbMNe1gzrLD7yyMkYo4hchw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.0.4", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-body-length-browser": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-stream": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.4.tgz", + "integrity": "sha512-jN6M6zaGVyB8FmNGG+xOPQB4N89M1x97MMdMnm1ESjljLS3Qju/IegQizKujaNcy2vXAvrz0en8bobe6E55FEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.1", + "@smithy/property-provider": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-codec": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.2.tgz", + "integrity": "sha512-p+f2kLSK7ZrXVfskU/f5dzksKTewZk8pJLPvER3aFHPt76C2MxD9vNatSfLzzQSQB4FNO96RK4PSXfhD1TTeMQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^4.2.0", + "@smithy/util-hex-encoding": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.2.tgz", + "integrity": "sha512-CepZCDs2xgVUtH7ZZ7oDdZFH8e6Y2zOv8iiX6RhndH69nlojCALSKK+OXwZUgOtUZEUaZ5e1hULVCHYbCn7pug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.0.tgz", + "integrity": "sha512-1PI+WPZ5TWXrfj3CIoKyUycYynYJgZjuQo8U+sphneOtjsgrttYybdqESFReQrdWJ+LKt6NEdbYzmmfDBmjX2A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.2.tgz", + "integrity": "sha512-C5bJ/C6x9ENPMx2cFOirspnF9ZsBVnBMtP6BdPl/qYSuUawdGQ34Lq0dMcf42QTjUZgWGbUIZnz6+zLxJlb9aw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.2.tgz", + "integrity": "sha512-St8h9JqzvnbB52FtckiHPN4U/cnXcarMniXRXTKn0r4b4XesZOGiAyUdj1aXbqqn1icSqBlzzUsCl6nPB018ng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.0.2.tgz", + "integrity": "sha512-+9Dz8sakS9pe7f2cBocpJXdeVjMopUDLgZs1yWeu7h++WqSbjUYv/JAJwKwXw1HV6gq1jyWjxuyn24E2GhoEcQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/util-base64": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-blob-browser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.2.tgz", + "integrity": "sha512-3g188Z3DyhtzfBRxpZjU8R9PpOQuYsbNnyStc/ZVS+9nVX1f6XeNOa9IrAh35HwwIZg+XWk8bFVtNINVscBP+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/chunked-blob-reader": "^5.0.0", + "@smithy/chunked-blob-reader-native": "^4.0.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.2.tgz", + "integrity": "sha512-VnTpYPnRUE7yVhWozFdlxcYknv9UN7CeOqSrMH+V877v4oqtVYuoqhIhtSjmGPvYrYnAkaM61sLMKHvxL138yg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-stream-node": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.2.tgz", + "integrity": "sha512-POWDuTznzbIwlEXEvvXoPMS10y0WKXK790soe57tFRfvf4zBHyzE529HpZMqmDdwG9MfFflnyzndUQ8j78ZdSg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.2.tgz", + "integrity": "sha512-GatB4+2DTpgWPday+mnUkoumP54u/MDM/5u44KF9hIu8jF0uafZtQLcdfIKkIcUNuF/fBojpLEHZS/56JqPeXQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", + "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/md5-js": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.2.tgz", + "integrity": "sha512-Hc0R8EiuVunUewCse2syVgA2AfSRco3LyAv07B/zCOMa+jpXI9ll+Q21Nc6FAlYPcpNcAXqBzMhNs1CD/pP2bA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.2.tgz", + "integrity": "sha512-hAfEXm1zU+ELvucxqQ7I8SszwQ4znWMbNv6PLMndN83JJN41EPuS93AIyh2N+gJ6x8QFhzSO6b7q2e6oClDI8A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.5.tgz", + "integrity": "sha512-WlpC9KVkajQf7RaGwi3n6lhHZzYTgm2PyX/2JjcwSHG417gFloNmYqN8rzDRXjT527/ZxZuvCsqq1gWZPW8lag==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.3.2", + "@smithy/middleware-serde": "^4.0.4", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "@smithy/url-parser": "^4.0.2", + "@smithy/util-middleware": "^4.0.2", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.6.tgz", + "integrity": "sha512-bl8q95nvCf7d22spxsBfs2giUDFf7prWLAxF5tmfgGBYHbUNW+OfnwMnabC15GMLA2AoE4HOtQR18a59lx6Blw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.1", + "@smithy/protocol-http": "^5.1.0", + "@smithy/service-error-classification": "^4.0.3", + "@smithy/smithy-client": "^4.2.5", + "@smithy/types": "^4.2.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-retry": "^4.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.4.tgz", + "integrity": "sha512-CaLvBtz+Xgs7eOwoinTXhZ02/9u8b28RT8lQAaDh7Q59nygeYYp1UiJjwl6zsay+lp0qVT/S7qLVI5RgcxjyfQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.2.tgz", + "integrity": "sha512-eSPVcuJJGVYrFYu2hEq8g8WWdJav3sdrI4o2c6z/rjnYDd3xH9j9E7deZQCzFn4QvGPouLngH3dQ+QVTxv5bOQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.1.tgz", + "integrity": "sha512-1slS5jf5icHETwl5hxEVBj+mh6B+LbVW4yRINsGtUKH+nxM5Pw2H59+qf+JqYFCHp9jssG4vX81f5WKnjMN3Vw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.2", + "@smithy/shared-ini-file-loader": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.0.4.tgz", + "integrity": "sha512-/mdqabuAT3o/ihBGjL94PUbTSPSRJ0eeVTdgADzow0wRJ0rN4A27EOrtlK56MYiO1fDvlO3jVTCxQtQmK9dZ1g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/querystring-builder": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.2.tgz", + "integrity": "sha512-wNRoQC1uISOuNc2s4hkOYwYllmiyrvVXWMtq+TysNRVQaHm4yoafYQyjN/goYZS+QbYlPIbb/QRjaUZMuzwQ7A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.0.tgz", + "integrity": "sha512-KxAOL1nUNw2JTYrtviRRjEnykIDhxc84qMBzxvu1MUfQfHTuBlCG7PA6EdVwqpJjH7glw7FqQoFxUJSyBQgu7g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.2.tgz", + "integrity": "sha512-NTOs0FwHw1vimmQM4ebh+wFQvOwkEf/kQL6bSM1Lock+Bv4I89B3hGYoUEPkmvYPkDKyp5UdXJYu+PoTQ3T31Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "@smithy/util-uri-escape": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.2.tgz", + "integrity": "sha512-v6w8wnmZcVXjfVLjxw8qF7OwESD9wnpjp0Dqry/Pod0/5vcEA3qxCr+BhbOHlxS8O+29eLpT3aagxXGwIoEk7Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.3.tgz", + "integrity": "sha512-FTbcajmltovWMjj3tksDQdD23b2w6gH+A0DYA1Yz3iSpjDj8fmkwy62UnXcWMy4d5YoMoSyLFHMfkEVEzbiN8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.2.tgz", + "integrity": "sha512-J9/gTWBGVuFZ01oVA6vdb4DAjf1XbDhK6sLsu3OS9qmLrS6KB5ygpeHiM3miIbj1qgSJ96GYszXFWv6ErJ8QEw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.0.tgz", + "integrity": "sha512-4t5WX60sL3zGJF/CtZsUQTs3UrZEDO2P7pEaElrekbLqkWPYkgqNW1oeiNYC6xXifBnT9dVBOnNQRvOE9riU9w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-middleware": "^4.0.2", + "@smithy/util-uri-escape": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.2.5.tgz", + "integrity": "sha512-T3gA/TShe52Ln0ywWGVoDiqRvaxqvrU0CKRRmzT71/I1rRBD8mY85rvMMME6vw5RpBLJC9ADmXSLmpohF7RRhA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.3.2", + "@smithy/middleware-endpoint": "^4.1.5", + "@smithy/middleware-stack": "^4.0.2", + "@smithy/protocol-http": "^5.1.0", + "@smithy/types": "^4.2.0", + "@smithy/util-stream": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.2.0.tgz", + "integrity": "sha512-7eMk09zQKCO+E/ivsjQv+fDlOupcFUCSC/L2YUPgwhvowVGWbPQHjEFcmjt7QQ4ra5lyowS92SV53Zc6XD4+fg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.2.tgz", + "integrity": "sha512-Bm8n3j2ScqnT+kJaClSVCMeiSenK6jVAzZCNewsYWuZtnBehEz4r2qP0riZySZVfzB+03XZHJeqfmJDkeeSLiQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", + "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", + "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", + "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", + "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", + "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.13.tgz", + "integrity": "sha512-HCLfXAyTEpVWLuyxDABg8UQukeRwChL1UErpSQ4KJK2ZoadmXuQY68pTL9KcuEtasTkIjnzyLUL9vhLdJ3VFHQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.0.2", + "@smithy/smithy-client": "^4.2.5", + "@smithy/types": "^4.2.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.13.tgz", + "integrity": "sha512-lu8E2RyzKzzFbNu4ICmY/2HltMZlJxMNg3saJ+r8I9vWbWbwdX7GOWUJdP4fbjEOm6aa52mnnd+uIRrT3dNEyA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.1.2", + "@smithy/credential-provider-imds": "^4.0.4", + "@smithy/node-config-provider": "^4.1.1", + "@smithy/property-provider": "^4.0.2", + "@smithy/smithy-client": "^4.2.5", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.4.tgz", + "integrity": "sha512-VfFATC1bmZLV2858B/O1NpMcL32wYo8DPPhHxYxDCodDl3f3mSZ5oJheW1IF91A0EeAADz2WsakM/hGGPGNKLg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.1.1", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", + "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.2.tgz", + "integrity": "sha512-6GDamTGLuBQVAEuQ4yDQ+ti/YINf/MEmIegrEeg7DdB/sld8BX1lqt9RRuIcABOhAGTA50bRbPzErez7SlDtDQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.3.tgz", + "integrity": "sha512-DPuYjZQDXmKr/sNvy9Spu8R/ESa2e22wXZzSAY6NkjOLj6spbIje/Aq8rT97iUMdDj0qHMRIe+bTxvlU74d9Ng==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.0.3", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.0.tgz", + "integrity": "sha512-Vj1TtwWnuWqdgQI6YTUF5hQ/0jmFiOYsc51CSMgj7QfyO+RF4EnT2HNjoviNlOOmgzgvf3f5yno+EiC4vrnaWQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.0.2", + "@smithy/node-http-handler": "^4.0.4", + "@smithy/types": "^4.2.0", + "@smithy/util-base64": "^4.0.0", + "@smithy/util-buffer-from": "^4.0.0", + "@smithy/util-hex-encoding": "^4.0.0", + "@smithy/util-utf8": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", + "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", + "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.3.tgz", + "integrity": "sha512-JtaY3FxmD+te+KSI2FJuEcfNC9T/DGGVf551babM7fAaXhjJUt7oSYurH1Devxd2+BOSUACCgt3buinx4UnmEA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.0.2", + "@smithy/types": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@streamparser/json": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@streamparser/json/-/json-0.0.6.tgz", + "integrity": "sha512-vL9EVn/v+OhZ+Wcs6O4iKE9EUpwHUqHmCtNUMWjqp+6dr85+XPOSGTEsqYNq1Vn04uk9SWlOVmx9J48ggJVT2Q==" + }, + "node_modules/@swagger-api/apidom-ast": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ast/-/apidom-ast-1.0.0-beta.39.tgz", + "integrity": "sha512-EWeSOtvI8XpbYMRkDyu4qAIlivhcplrskpau2cbrWfXGBjrqEtmHqWlbJ9xoXJbNshbIcZ0Z77QdxicimGjs0w==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "unraw": "^3.0.0" + } + }, + "node_modules/@swagger-api/apidom-core": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-core/-/apidom-core-1.0.0-beta.39.tgz", + "integrity": "sha512-tYZSVA+uDFvBJmnP104d8Qb/mye8B6ykNviohHAngHsy8ElcOPzSi5GKwwmJgf3taWzipMqWNM0ch5KytbXTqw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-ast": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "minim": "~0.23.8", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "short-unique-id": "^5.3.2", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-error": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-error/-/apidom-error-1.0.0-beta.39.tgz", + "integrity": "sha512-vQ3xQaRQGP9kNNBEDcFCmUd2PT9rCtYdkCyqYWZMxHBm5dXSBC/dQaC5VN1DbqQygE16fSQC+c5sqOrwg5d5WQ==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.20.7" + } + }, + "node_modules/@swagger-api/apidom-json-pointer": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-json-pointer/-/apidom-json-pointer-1.0.0-beta.39.tgz", + "integrity": "sha512-gPDNT+MCs/B1XYuNpmnz0rOHQ0ssN9YjVDqeGkX61v03BLJUF/JZKMo3J3FA2mgKb6ap+kRHzpzw5PpHLwRKAw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swaggerexpert/json-pointer": "^2.10.1" + } + }, + "node_modules/@swagger-api/apidom-ns-api-design-systems": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-api-design-systems/-/apidom-ns-api-design-systems-1.0.0-beta.39.tgz", + "integrity": "sha512-MpdCb8KS3Tz1mGTrU0XC0Q2OcsrUWKB+buFPzLFOv0dU36ArARERX+Mz6aCJQ1CqnkFVM49uMe2NECO93ZR52w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-ns-arazzo-1": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-arazzo-1/-/apidom-ns-arazzo-1-1.0.0-beta.39.tgz", + "integrity": "sha512-gIiZhlt51JxEZBAZ5PfHV1c73SMQJiwJX5DnazGehMO+ojR4HyLPFh1lc6mChMxPyPlRFOfqnmx/hmNcJ/XRiA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-ns-asyncapi-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-asyncapi-2/-/apidom-ns-asyncapi-2-1.0.0-beta.39.tgz", + "integrity": "sha512-Jtdo+6MgVhf8HynjRo5pIj+aYYICAQGwRkd0n0YtOhvvKoI0gWEMpcRkDbJrNcNYOHaSxMlQfotGlTCaMl7QJQ==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-2019-09": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2019-09/-/apidom-ns-json-schema-2019-09-1.0.0-beta.39.tgz", + "integrity": "sha512-I/XP4zbrWAmnq2KWPtbb9DKLWgzYFovIiSQOyh47bJqbYgz64/IhoZb/uGihZojVVHSqeeJH9o6JOahqHQzKOw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-draft-7": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-2020-12": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-2020-12/-/apidom-ns-json-schema-2020-12-1.0.0-beta.39.tgz", + "integrity": "sha512-9bpMp96fb76lOqeggtyCU457K/XBLyw3O9fxdVS3Tevhf8P3SJ6QpabmweRb6kFt4vI3+DiBschJGn0iqmlcXw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-2019-09": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-draft-4": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-4/-/apidom-ns-json-schema-draft-4-1.0.0-beta.39.tgz", + "integrity": "sha512-F25tm/nwPl1rRnUHzaVw4SAeASodO60oAtWX+GF3K61WEx/Aao4Maldv3CQtAoUk8L0Ml0l1KZL00sgfikwqlw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-ast": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-draft-6": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-6/-/apidom-ns-json-schema-draft-6-1.0.0-beta.39.tgz", + "integrity": "sha512-E2fQQHWIRtbM5C1m1EL95MQNDPL98mlgYomPQDDUEFbYrH3u9BQGAgpIu4KuYasKquyuhx9YXqS/jLRhMCRfAQ==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, + "node_modules/@swagger-api/apidom-ns-json-schema-draft-7": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-json-schema-draft-7/-/apidom-ns-json-schema-draft-7-1.0.0-beta.39.tgz", + "integrity": "sha512-mhzb7n3pm0yfYuM9bZowYMp6L61Cz+HrbjBowUIt5iOMMAATQd6x209pj81hnSmgHmEJCgv+8IO9dvweme698A==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-draft-6": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.4" + } + }, + "node_modules/@swagger-api/apidom-ns-openapi-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-2/-/apidom-ns-openapi-2-1.0.0-beta.39.tgz", + "integrity": "sha512-/Cjggp0eAM1GfKeLu53sLjCV9lFVUMucFruXJnD1TWdCKv5S5JAKsGBASbchw8hvwrfx6sPHslzZFV+tZKbn2Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-ns-openapi-3-0": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-0/-/apidom-ns-openapi-3-0-1.0.0-beta.39.tgz", + "integrity": "sha512-lvNlUtCmyHH8+52qOhgXXdzy4HEYA+t7xnFNvDb6dtP+epXCexux3uRs8+xEYBHo/WqUGzjdwd0qKFRgyP7Lrw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-draft-4": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-ns-openapi-3-1": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-ns-openapi-3-1/-/apidom-ns-openapi-3-1-1.0.0-beta.39.tgz", + "integrity": "sha512-sXMJxTGL2F36Uyv9iqvPwvzsD5NJM/dJ52tUuiJP8h4RqXwjrOC86hqf1/Xk/rxgpZShhW4PNEqifvPq/Mto3w==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-ast": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-json-pointer": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-json-schema-2020-12": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "ts-mixer": "^6.0.3" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-json": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-json/-/apidom-parser-adapter-api-design-systems-json-1.0.0-beta.39.tgz", + "integrity": "sha512-jrPxZMvE6I2X8FUx4ri7VTMy2wTNOLLz+qXSx9sSXWULImqwdscvEwSVug8zdBQYMy8HXwt0wHpxlGLXBEmL8Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-api-design-systems-yaml": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-api-design-systems-yaml/-/apidom-parser-adapter-api-design-systems-yaml-1.0.0-beta.39.tgz", + "integrity": "sha512-JKyRYc4cBajPkIpO0YTJnxI+p8ubXfA+/1L8Fpq5kDPAI5Wh744iZ/scVHTgpgY8g+GbPqIoWB0ilQbEdlF5Sg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-api-design-systems": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-arazzo-json-1": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-arazzo-json-1/-/apidom-parser-adapter-arazzo-json-1-1.0.0-beta.39.tgz", + "integrity": "sha512-qlQuj4jsEPpLRH4wpkMjbR3Id3qb4n/oerv4cKCi1TYJJphC7DG7QRS90tYjaAF7n2YA8HxUcEIu2+Y5QZKyMw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-arazzo-yaml-1": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-arazzo-yaml-1/-/apidom-parser-adapter-arazzo-yaml-1-1.0.0-beta.39.tgz", + "integrity": "sha512-MUChnj6dJZRGDtIIVItIojzDNBEdan8KkuV+3U1l8bBA4eJQIq7yzHYk7fq6bl4Yd17HG0HT+1xckbUnj5Ay4A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-json-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-json-2/-/apidom-parser-adapter-asyncapi-json-2-1.0.0-beta.39.tgz", + "integrity": "sha512-G+xIeYGetnCM3ylsWSwSyqCntpT6gt2Bv3f6hu/IonZxxCy2HqUl9JS41XF/cJHCoBchJU3G+bjOZXN22W3Xzw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-asyncapi-yaml-2/-/apidom-parser-adapter-asyncapi-yaml-2-1.0.0-beta.39.tgz", + "integrity": "sha512-VPwlMRwtMQtPmEv6JXNefBBjAK/IxPsq9XWP/7kJZQ6CDp6ljHrMJDPAHZNundSL09xu7Wbz6KVGcpruUPiWmA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-json": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-json/-/apidom-parser-adapter-json-1.0.0-beta.39.tgz", + "integrity": "sha512-Rg2SgI06CDTdm3qs3A5pNvhonVxa2jOcwypxyhKngelIHaTuOPgaFA6qyCIvX0oIhwTcKcvV+5tPlGIR3vshpA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-ast": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "tree-sitter": "=0.22.1", + "tree-sitter-json": "=0.24.8", + "web-tree-sitter": "=0.24.5" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-json/node_modules/tree-sitter": { + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.1.tgz", + "integrity": "sha512-gRO+jk2ljxZlIn20QRskIvpLCMtzuLl5T0BY6L9uvPYD17uUrxlxWkvYCiVqED2q2q7CVtY52Uex4WcYo2FEXw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^8.2.1", + "node-gyp-build": "^4.8.2" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-2/-/apidom-parser-adapter-openapi-json-2-1.0.0-beta.39.tgz", + "integrity": "sha512-DYB3jGcSnTu1ykbAKkMo550QW0BjnHlGxi1NaBbVYzdMfPYbBSQnDB3zYAwgakpoQx89OztFSlrNQ+3P8wuuSw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-0": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-0/-/apidom-parser-adapter-openapi-json-3-0-1.0.0-beta.39.tgz", + "integrity": "sha512-4ueWFNc3N4YZb7fTwsgrhWzdCo3TnZ7HgK5fPW3m0+Hm2wko2WYIUCIxU33Ef4DB+0Hd8y4Abjv8Mz0CCxRaeg==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-json-3-1": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-json-3-1/-/apidom-parser-adapter-openapi-json-3-1-1.0.0-beta.39.tgz", + "integrity": "sha512-8K/9J1CnQAwVqN+pvP9PH607WKA7WimNmZiyczgfnOgq93PUozNavrK97lwUUOQcZUmQra8pG2XrOrZZwd/M5w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-2/-/apidom-parser-adapter-openapi-yaml-2-1.0.0-beta.39.tgz", + "integrity": "sha512-EfzVZrYTnwNGXUXIOrZkigQxdze+VdXxJWp55t3CWTy0CA7w9eM+PDpzHu7iqJgXqTOixMGy02Gzyv6N6sDj8A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-0/-/apidom-parser-adapter-openapi-yaml-3-0-1.0.0-beta.39.tgz", + "integrity": "sha512-ElHueuGdwB35VeZaJnmhZE3ILGE8F74ThJqgTbY+F2JcNo4O8cBkoCq9syw1pJ+l2JoAUErmxaTOR+zNA/wK+g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-openapi-yaml-3-1/-/apidom-parser-adapter-openapi-yaml-3-1-1.0.0-beta.39.tgz", + "integrity": "sha512-lNSXp+vGcsA/d/3ukXJeovAnO5oxcTJ5OvFBL84grJvK1C6E2v0AOfsMlUEipIRNhIHq3zYKpUnhFJyE13VqXA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.39", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-parser-adapter-yaml-1-2/-/apidom-parser-adapter-yaml-1-2-1.0.0-beta.39.tgz", + "integrity": "sha512-30Lhgkg2ZrHY7tQ5h9umjWWhy0Fqcoi28SXJ9vtdj1cLSnFvclaLe5ZGbXP3wdW4sXZO0As3+msL9tMwrUJ/7w==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-ast": "^1.0.0-beta.39", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@tree-sitter-grammars/tree-sitter-yaml": "=0.7.0", + "@types/ramda": "~0.30.0", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0", + "tree-sitter": "=0.22.1", + "web-tree-sitter": "=0.24.5" + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2/node_modules/@tree-sitter-grammars/tree-sitter-yaml": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@tree-sitter-grammars/tree-sitter-yaml/-/tree-sitter-yaml-0.7.0.tgz", + "integrity": "sha512-GOMIK3IaDvECD0eZEhAsLl03RMtM1E8StxuGMn6PpMKFg7jyQ+jSzxJZ4Jmc/tYitah9/AECt8o4tlRQ5yEZQg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "peerDependencies": { + "tree-sitter": "^0.22.1" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/@swagger-api/apidom-parser-adapter-yaml-1-2/node_modules/tree-sitter": { + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.1.tgz", + "integrity": "sha512-gRO+jk2ljxZlIn20QRskIvpLCMtzuLl5T0BY6L9uvPYD17uUrxlxWkvYCiVqED2q2q7CVtY52Uex4WcYo2FEXw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^8.2.1", + "node-gyp-build": "^4.8.2" + } + }, + "node_modules/@swagger-api/apidom-reference": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.39.tgz", + "integrity": "sha512-PrV2/3Z6XGJPj4fv1JazY1dKjlnAg/BN22UQdUOzA5/A0TkfbImt8uVQuVzQSL2P8RA6G9TDsdpOalj80N47rw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime-corejs3": "^7.26.10", + "@swagger-api/apidom-core": "^1.0.0-beta.39", + "@swagger-api/apidom-error": "^1.0.0-beta.39", + "@types/ramda": "~0.30.0", + "axios": "^1.9.0", + "minimatch": "^7.4.3", + "process": "^0.11.10", + "ramda": "~0.30.0", + "ramda-adjunct": "^5.0.0" + }, + "optionalDependencies": { + "@swagger-api/apidom-json-pointer": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-ns-arazzo-1": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-arazzo-json-1": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-arazzo-yaml-1": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-openapi-json-2": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^1.0.0-beta.3 <1.0.0-rc.0", + "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.3 <1.0.0-rc.0" + } + }, + "node_modules/@swagger-api/apidom-reference/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@swagger-api/apidom-reference/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@swaggerexpert/cookie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@swaggerexpert/cookie/-/cookie-2.0.2.tgz", + "integrity": "sha512-DPI8YJ0Vznk4CT+ekn3rcFNq1uQwvUHZhH6WvTSPD0YKBIlMS9ur2RYKghXuxxOiqOam/i4lHJH4xTIiTgs3Mg==", + "license": "Apache-2.0", + "dependencies": { + "apg-lite": "^1.0.3" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@swaggerexpert/json-pointer": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@swaggerexpert/json-pointer/-/json-pointer-2.10.2.tgz", + "integrity": "sha512-qMx1nOrzoB+PF+pzb26Q4Tc2sOlrx9Ba2UBNX9hB31Omrq+QoZ2Gly0KLrQWw4Of1AQ4J9lnD+XOdwOdcdXqqw==", + "license": "Apache-2.0", + "dependencies": { + "apg-lite": "^1.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", + "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/ramda": { + "version": "0.30.2", + "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz", + "integrity": "sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA==", + "license": "MIT", + "dependencies": { + "types-ramda": "^0.30.1" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", + "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==", + "license": "MIT" + }, + "node_modules/amp-message": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", + "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==", + "license": "MIT", + "dependencies": { + "amp": "0.3.1" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/apg-lite": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/apg-lite/-/apg-lite-1.0.4.tgz", + "integrity": "sha512-B32zCN3IdHIc99Vy7V9BaYTUzLeRA8YXYY1aQD1/5I2aqIrO0coi4t6hJPqMisidlBxhyME8UexkHt31SlR6Og==", + "license": "BSD-2-Clause" + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/autolinker": { + "version": "3.16.2", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz", + "integrity": "sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1692.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1692.0.tgz", + "integrity": "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base32.js": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", + "integrity": "sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "license": "MIT", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/bodec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", + "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==", + "license": "MIT/X11" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/cli-tableau": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz", + "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==", + "dependencies": { + "chalk": "3.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/cli-tableau/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comma-separated-tokens": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", + "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "license": "MIT" + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "license": "MIT" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js-pure": { + "version": "3.42.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", + "integrity": "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", "dependencies": { - "node-addon-api": "^8.2.1", - "node-gyp-build": "^4.8.2" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@swagger-api/apidom-reference": { - "version": "1.0.0-beta.12", - "resolved": "https://registry.npmjs.org/@swagger-api/apidom-reference/-/apidom-reference-1.0.0-beta.12.tgz", - "integrity": "sha512-4A5dvra9NCsl9Dp3x3UyNV3tyTl1LJwvNowaLfMuY5r8jtQLzkcCW+CLPyP2Y64qeT30sklZp7/M3VVd6jKPOg==", + "node_modules/croner": { + "version": "4.1.97", + "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", + "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime-corejs3": "^7.20.7", - "@swagger-api/apidom-core": "^1.0.0-beta.12", - "@types/ramda": "~0.30.0", - "axios": "^1.7.4", - "minimatch": "^7.4.3", - "process": "^0.11.10", - "ramda": "~0.30.0", - "ramda-adjunct": "^5.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, - "optionalDependencies": { - "@swagger-api/apidom-error": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-json-pointer": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-ns-asyncapi-2": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-ns-openapi-2": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-ns-openapi-3-0": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-ns-openapi-3-1": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-ns-workflows-1": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-json": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-openapi-json-2": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-openapi-json-3-0": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-openapi-yaml-2": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-workflows-json-1": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-workflows-yaml-1": "^1.0.0-beta.3 <1.0.0-rc.0", - "@swagger-api/apidom-parser-adapter-yaml-1-2": "^1.0.0-beta.3 <1.0.0-rc.0" + "engines": { + "node": ">= 8" } }, - "node_modules/@swagger-api/apidom-reference/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/csrf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", + "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==", "dependencies": { - "balanced-match": "^1.0.0" + "rndm": "1.2.0", + "tsscmp": "1.0.6", + "uid-safe": "2.1.5" + }, + "engines": { + "node": ">= 0.8" } }, - "node_modules/@swagger-api/apidom-reference/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "license": "MIT" + }, + "node_modules/csurf": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.11.0.tgz", + "integrity": "sha512-UCtehyEExKTxgiu8UHdGvHj4tnpE/Qctue03Giq5gPgMQ9cg/ciod5blZQ5a4uCEenNQjxyGuzygLdKUmee/bQ==", + "deprecated": "This package is archived and no longer maintained. For support, visit https://github.com/expressjs/express/discussions", "dependencies": { - "brace-expansion": "^2.0.1" + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "csrf": "3.1.0", + "http-errors": "~1.7.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8.0" } }, - "node_modules/@swaggerexpert/cookie": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@swaggerexpert/cookie/-/cookie-2.0.2.tgz", - "integrity": "sha512-DPI8YJ0Vznk4CT+ekn3rcFNq1uQwvUHZhH6WvTSPD0YKBIlMS9ur2RYKghXuxxOiqOam/i4lHJH4xTIiTgs3Mg==", + "node_modules/csurf/node_modules/cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", "dependencies": { - "apg-lite": "^1.0.3" + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" }, "engines": { - "node": ">=12.20.0" + "node": ">= 0.6" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + "node_modules/csurf/node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "node_modules/csurf/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "dependencies": { - "@types/unist": "^2" + "node_modules/csurf/node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "engines": { + "node": ">=0.6" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + "node_modules/culvert": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", + "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==", + "license": "MIT" }, - "node_modules/@types/ramda": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz", - "integrity": "sha512-PyzHvjCalm2BRYjAU6nIB3TprYwMNOUY/7P/N8bSzp9W/yM2YrtGtAnnVtaCNSeOZ8DzKyFDvaqQs7LnWwwmBA==", + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { - "types-ramda": "^0.30.1" + "ms": "2.0.0" } }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "optional": true + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", - "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" } }, - "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/amp": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", - "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==" - }, - "node_modules/amp-message": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", - "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==", + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "license": "ISC", "dependencies": { - "amp": "0.3.1" + "asap": "^2.0.0", + "wrappy": "1" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", "dependencies": { - "color-convert": "^2.0.1" + "esutils": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" + } + }, + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://dotenvx.com" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/apg-lite": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/apg-lite/-/apg-lite-1.0.4.tgz", - "integrity": "sha512-B32zCN3IdHIc99Vy7V9BaYTUzLeRA8YXYY1aQD1/5I2aqIrO0coi4t6hJPqMisidlBxhyME8UexkHt31SlR6Og==" - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } }, - "node_modules/array-flatten": { + "node_modules/ee-first": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dependencies": { - "tslib": "^2.0.1" - }, + "node_modules/electron-to-chromium": { + "version": "1.5.152", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.152.tgz", + "integrity": "sha512-xBOfg/EBaIlVsHipHl2VdTPJRSvErNUaqW8ejTq5OlOlIYx1wOllCHsAvAIrr55jD1IYEfdR86miUEt8H5IeJg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, - "node_modules/autolinker": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.16.2.tgz", - "integrity": "sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==", + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", "dependencies": { - "tslib": "^2.3.0" + "once": "^1.4.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "license": "MIT", "dependencies": { - "possible-typed-array-names": "^1.0.0" + "ansi-colors": "^4.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8.6" } }, - "node_modules/aws-sdk": { - "version": "2.1692.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1692.0.tgz", - "integrity": "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==", - "hasInstallScript": true, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.6.2" - }, - "engines": { - "node": ">= 10.0.0" + "is-arrayish": "^0.2.1" } }, - "node_modules/aws-sdk/node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "bin": { - "uuid": "dist/bin/uuid" + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, - "node_modules/axios": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz", - "integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==", + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { - "node": ">=10.0.0" + "node": ">= 0.4" } }, - "node_modules/bcryptjs": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", - "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", - "bin": { - "bcrypt": "bin/bcrypt" + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/blessed": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", - "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, "bin": { - "blessed": "bin/tput.js" + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/bodec": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", - "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "node_modules/eslint": { + "version": "9.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", + "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==", + "dev": true, + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.13.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.26.0", + "@eslint/plugin-kit": "^0.2.8", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@modelcontextprotocol/sdk": "^1.8.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "zod": "^3.24.2" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/eslint-plugin-jsdoc": { + "version": "50.6.14", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.14.tgz", + "integrity": "sha512-JUudvooQbUx3iB8n/MzXMOV/VtaXq7xL4CeXhYryinr8osck7nV6fE2/xUXTiH3epPXcvq6TE3HQfGQuRHErTQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "fill-range": "^7.1.1" + "@es-joy/jsdoccomment": "~0.49.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.6", + "escape-string-regexp": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.6.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" } }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "node_modules/eslint-plugin-jsdoc/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + "node_modules/eslint-plugin-jsdoc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "streamsearch": "^1.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=10.16.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 0.8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "node_modules/eslint/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">=6" + "node": ">=0.10" } }, - "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" } }, - "node_modules/charm": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", - "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==" + "node_modules/eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg==", + "license": "MIT" }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "eventsource-parser": "^3.0.1" }, "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">=18.0.0" } }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + "node_modules/eventsource-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz", + "integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/cli-tableau": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz", - "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==", + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "license": "MIT", "dependencies": { - "chalk": "3.0.0" + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" }, "engines": { - "node": ">=8.10.0" + "node": ">=8.3.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/exceljs/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 6" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/exceljs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", + "node": ">=10" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "license": "MIT", "dependencies": { - "safe-buffer": "5.2.1" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 0.6" + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/express-rate-limit": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", + "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/express/node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/cookie-parser": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", - "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "node_modules/extrareqp2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", + "integrity": "sha512-Gum0g1QYb6wpPJCVypWP3bbIuaibcFiJcpuPM10YSXp/tzqi84x9PJageob+eN4xVRIOto4wjSGNLyMD54D2xA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "license": "MIT", "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.6" + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10.0.0" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "dependencies": { - "toggle-selection": "^1.0.6" - } + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "license": "MIT" }, - "node_modules/core-js-pure": { - "version": "3.41.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.41.0.tgz", - "integrity": "sha512-71Gzp96T9YPk63aUvE5Q5qP+DryB4ZloUZPSOebGM88VNw8VNfvdA7z6kGA8iGOTEzAomsRidp4jXSmUIJsL+Q==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "strnum": "^1.0.5" }, - "engines": { - "node": ">= 0.10" + "bin": { + "fxparser": "src/cli/cli.js" } }, - "node_modules/croner": { - "version": "4.1.97", - "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", - "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "format": "^0.2.0" }, - "engines": { - "node": ">= 8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==" - }, - "node_modules/culvert": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", - "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==" - }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "engines": { - "node": ">= 14" + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" } }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + "node_modules/fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==", + "license": "MIT" }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "flat-cache": "^4.0.0" + }, "engines": { - "node": ">=4.0.0" + "node": ">=16.0.0" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dompurify": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", - "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" + "node": ">=16" } }, - "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, - "node_modules/drange": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", - "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", "dependencies": { - "safe-buffer": "^5.0.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", "engines": { - "node": ">= 0.8" + "node": ">=0.4.x" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1" + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" }, "engines": { - "node": ">=8.6" + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.6" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.6" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": { - "es-errors": "^1.3.0" - }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" }, "engines": { - "node": ">= 0.4" + "node": ">=0.6" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "is-property": "^1.0.2" } }, - "node_modules/eslint": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", - "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.1.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.0", - "@eslint/js": "9.22.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "node": ">=6.9.0" } }, - "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "9.22.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", - "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=8.0.0" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, + "node_modules/get-uri": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -3853,11 +7540,51 @@ } } }, - "node_modules/eslint/node_modules/glob-parent": { + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/git-node-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", + "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ==", + "license": "MIT" + }, + "node_modules/git-sha1": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz", + "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -3865,332 +7592,455 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "es-define-property": "^1.0.0" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/eventemitter2": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", - "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg==" + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", "engines": { - "node": ">=0.4.x" + "node": "*" } }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, + "node_modules/html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "toidentifier": "1.0.1" }, "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">= 0.8" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, "engines": { - "node": ">= 0.6" + "node": ">= 14" } }, - "node_modules/extrareqp2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", - "integrity": "sha512-Gum0g1QYb6wpPJCVypWP3bbIuaibcFiJcpuPM10YSXp/tzqi84x9PJageob+eN4xVRIOto4wjSGNLyMD54D2xA==", + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { - "follow-redirects": "^1.14.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-patch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", - "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, - "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - }, - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - } - ], + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "agent-base": "^7.1.2", + "debug": "4" }, - "bin": { - "fxparser": "src/cli/cli.js" + "engines": { + "node": ">= 14" } }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { - "format": "^0.2.0" + "ms": "^2.1.3" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/fclone": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", - "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==" + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "dependencies": { - "flat-cache": "^4.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=16.0.0" + "node": ">=10.17.0" } }, - "node_modules/file-type": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", - "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", "engines": { - "node": ">=0.10.0" + "node": ">=0.4" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "license": "ISC" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" }, "engines": { - "node": ">=16" + "node": ">= 12" } }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">= 0.10" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4199,85 +8049,44 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "license": "MIT", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4286,152 +8095,144 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/get-uri": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", - "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4" - }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { - "node": ">= 14" + "node": ">=0.10.0" } }, - "node_modules/get-uri/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/get-uri/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/git-node-fs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", - "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ==" - }, - "node_modules/git-sha1": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz", - "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==" + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12.0" } }, - "node_modules/has-flag": { + "node_modules/is-promise": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" }, - "node_modules/has-property-descriptors": { + "node_modules/is-property": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -4440,91 +8241,82 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" }, - "node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" } }, - "node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==" - }, - "node_modules/html-comment-regex": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", - "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { - "node": ">= 14" + "node": ">=10" } }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -4537,336 +8329,615 @@ } } }, - "node_modules/http-proxy-agent/node_modules/ms": { + "node_modules/istanbul-lib-source-maps/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=6.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { - "supports-color": { + "node-notifier": { "optional": true } } }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/immutable": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", - "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, + "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", "dependencies": { - "loose-envify": "^1.0.0" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">= 12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, "engines": { - "node": ">= 0.10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.16" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, "node_modules/jmespath": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", "engines": { "node": ">= 0.6.0" } @@ -4874,12 +8945,14 @@ "node_modules/js-file-download": { "version": "0.4.12", "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", - "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==" + "integrity": "sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==", + "license": "MIT" }, "node_modules/js-git": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", "integrity": "sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA==", + "license": "MIT", "dependencies": { "bodec": "^0.1.0", "culvert": "^0.1.2", @@ -4890,12 +8963,14 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4906,36 +8981,110 @@ "node_modules/jsbn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC", "optional": true }, + "node_modules/json2csv": { + "version": "6.0.0-alpha.2", + "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-6.0.0-alpha.2.tgz", + "integrity": "sha512-nJ3oP6QxN8z69IT1HmrJdfVxhU1kLTBVgMfRnNZc37YEY+jZ4nU27rBGxT4vaqM/KUCavLRhntmTuBFqZLBUcA==", + "dependencies": { + "@streamparser/json": "^0.0.6", + "commander": "^6.2.0", + "lodash.get": "^4.4.2" + }, + "bin": { + "json2csv": "bin/json2csv.js" + }, + "engines": { + "node": ">= 12", + "npm": ">= 6.13.0" + } + }, + "node_modules/json2csv/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", @@ -4956,14 +9105,34 @@ "node_modules/jsonwebtoken/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } @@ -4972,6 +9141,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" @@ -4982,23 +9152,58 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/lazy": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==", + "license": "MIT", "engines": { "node": ">=0.2.0" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -5007,11 +9212,34 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -5025,75 +9253,155 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "license": "MIT" }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==" + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5105,6 +9413,7 @@ "version": "1.20.0", "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", + "license": "MIT", "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" @@ -5115,17 +9424,61 @@ } }, "node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", "engines": { - "node": ">=12" + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" } }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -5134,6 +9487,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5142,22 +9496,46 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -5169,6 +9547,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5177,6 +9556,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -5184,10 +9564,21 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minim": { "version": "0.23.8", "resolved": "https://registry.npmjs.org/minim/-/minim-0.23.8.tgz", "integrity": "sha512-bjdr2xW1dBCMsMGGsUeqM4eFI60m94+szhxWys+B1ztIt6gWSfeGBdSVCIawezeHYLYn0j6zrsXdQS/JllBzww==", + "license": "MIT", "dependencies": { "lodash": "^4.15.0" }, @@ -5199,6 +9590,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5210,43 +9602,55 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, "bin": { "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" } }, "node_modules/mnemonist": { "version": "0.38.3", "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "license": "MIT", "dependencies": { "obliterator": "^1.6.1" } }, + "node_modules/module-alias": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", + "license": "MIT" + }, "node_modules/module-details-from-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", - "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/multer": { - "version": "1.4.5-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", - "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "version": "1.4.5-lts.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", + "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", + "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.0.0", @@ -5264,6 +9668,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/multer-s3/-/multer-s3-3.0.1.tgz", "integrity": "sha512-BFwSO80a5EW4GJRBdUuSHblz2jhVSAze33ZbnGpcfEicoT0iRolx4kWR+AJV07THFRCQ78g+kelKFdjkCCaXeQ==", + "license": "MIT", "dependencies": { "@aws-sdk/lib-storage": "^3.46.0", "file-type": "^3.3.0", @@ -5277,32 +9682,77 @@ "@aws-sdk/client-s3": "^3.0.0" } }, - "node_modules/multer/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "node_modules/mysql2": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.1.tgz", + "integrity": "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==", + "license": "MIT", "dependencies": { - "minimist": "^1.2.6" + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">= 8.0" } }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/needle": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "license": "MIT", "dependencies": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", @@ -5319,6 +9769,7 @@ "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -5326,12 +9777,20 @@ "node_modules/needle/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/needle/node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5340,6 +9799,7 @@ "version": "0.6.18", "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", + "license": "MIT", "engines": { "node": ">= 10" } @@ -5348,6 +9808,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -5355,21 +9816,45 @@ "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "license": "MIT" }, "node_modules/node-addon-api": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "license": "MIT", "optional": true, "engines": { "node": "^18 || ^20 || >= 21" } }, + "node_modules/node-cron": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", + "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", + "license": "ISC", + "dependencies": { + "uuid": "8.3.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-cron/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", @@ -5380,6 +9865,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "license": "MIT", "engines": { "node": ">=10.5.0" } @@ -5388,6 +9874,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch-commonjs/-/node-fetch-commonjs-3.3.2.tgz", "integrity": "sha512-VBlAiynj3VMLrotgwOS3OyECFxas5y7ltLcK4t41lMUZeaK15Ym4QRkqN0EQKAFL42q9i21EPKjzLUPfltR72A==", + "license": "MIT", "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -5404,6 +9891,7 @@ "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -5411,18 +9899,119 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nssocket": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz", "integrity": "sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w==", + "license": "MIT", "dependencies": { "eventemitter2": "~0.4.14", "lazy": "~1.0.11" @@ -5434,12 +10023,14 @@ "node_modules/nssocket/node_modules/eventemitter2": { "version": "0.4.14", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==" + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", + "license": "MIT" }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5448,6 +10039,7 @@ "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5458,12 +10050,14 @@ "node_modules/obliterator": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", - "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==" + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "license": "MIT" }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -5475,14 +10069,32 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/openapi-path-templating": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/openapi-path-templating/-/openapi-path-templating-2.1.1.tgz", - "integrity": "sha512-nv9S9865cmJWY1E+MkUUSdxscVrsqqehYh7tRpkfj+0+HUI+w390c+DUBK1cS9n2d9TypzWPmhsBHFcqc1lp9w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/openapi-path-templating/-/openapi-path-templating-2.2.1.tgz", + "integrity": "sha512-eN14VrDvl/YyGxxrkGOHkVkWEoPyhyeydOUrbvjoz8K5eIGgELASwN1eqFOJ2CTQMGCy2EntOK1KdtJ8ZMekcg==", + "license": "Apache-2.0", "dependencies": { "apg-lite": "^1.0.4" }, @@ -5494,6 +10106,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/openapi-server-url-templating/-/openapi-server-url-templating-1.3.0.tgz", "integrity": "sha512-DPlCms3KKEbjVQb0spV6Awfn6UWNheuG/+folQPzh/wUaKwuqvj8zt5gagD7qoyxtE03cIiKPgLFS3Q8Bz00uQ==", + "license": "Apache-2.0", "dependencies": { "apg-lite": "^1.0.4" }, @@ -5505,6 +10118,7 @@ "version": "12.1.3", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "license": "MIT", "peer": true }, "node_modules/optionator": { @@ -5512,6 +10126,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -5529,6 +10144,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -5544,6 +10160,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -5554,10 +10171,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", @@ -5573,9 +10200,10 @@ } }, "node_modules/pac-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -5591,12 +10219,14 @@ "node_modules/pac-proxy-agent/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/pac-resolver": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -5608,13 +10238,15 @@ "node_modules/pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -5626,6 +10258,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "license": "MIT", "dependencies": { "character-entities": "^1.0.0", "character-entities-legacy": "^1.0.0", @@ -5635,14 +10268,51 @@ "is-hexadecimal": "^1.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5651,7 +10321,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5660,6 +10330,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5669,6 +10340,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5676,17 +10348,27 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-to-regexp": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -5698,6 +10380,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.2.tgz", "integrity": "sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w==", + "license": "MIT", "dependencies": { "safe-buffer": "^5.2.1" }, @@ -5705,10 +10388,100 @@ "node": ">=10" } }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pm2": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.4.3.tgz", "integrity": "sha512-4/I1htIHzZk1Y67UgOCo4F1cJtas1kSds31N8zN0PybO230id1nigyjGuGFzUnGmUFPmrJ0On22fO1ChFlp7VQ==", + "license": "AGPL-3.0", "dependencies": { "@pm2/agent": "~2.0.0", "@pm2/io": "~6.0.1", @@ -5757,6 +10530,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz", "integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==", + "license": "MIT", "dependencies": { "amp": "~0.3.1", "amp-message": "~0.1.1", @@ -5771,6 +10545,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz", "integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==", + "license": "MIT", "dependencies": { "debug": "^4.3.1" }, @@ -5779,9 +10554,10 @@ } }, "node_modules/pm2-axon-rpc/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -5797,12 +10573,14 @@ "node_modules/pm2-axon-rpc/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/pm2-axon/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -5818,12 +10596,14 @@ "node_modules/pm2-axon/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/pm2-deploy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz", "integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==", + "license": "MIT", "dependencies": { "run-series": "^1.1.8", "tv4": "^1.3.0" @@ -5836,6 +10616,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz", "integrity": "sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA==", + "license": "MIT/X11", "dependencies": { "charm": "~0.1.1" } @@ -5844,6 +10625,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz", "integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==", + "license": "Apache", "optional": true, "dependencies": { "async": "^3.2.0", @@ -5854,9 +10636,10 @@ } }, "node_modules/pm2-sysmonit/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "optional": true, "dependencies": { "ms": "^2.1.3" @@ -5874,12 +10657,14 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", "optional": true }, "node_modules/pm2-sysmonit/node_modules/pidusage": { "version": "2.0.21", "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "license": "MIT", "optional": true, "dependencies": { "safe-buffer": "^5.2.1" @@ -5888,10 +10673,24 @@ "node": ">=8" } }, + "node_modules/pm2/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/pm2/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -5904,15 +10703,48 @@ } } }, + "node_modules/pm2/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pm2/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/pm2/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -5922,14 +10754,44 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -5938,6 +10800,7 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -5945,30 +10808,54 @@ "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" }, "node_modules/promptly": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz", "integrity": "sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA==", + "license": "MIT", "dependencies": { "read": "^1.0.4" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/property-information": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz", "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", + "license": "MIT", "dependencies": { "xtend": "^4.0.0" }, @@ -5981,6 +10868,7 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -5993,6 +10881,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "^4.3.4", @@ -6008,9 +10897,10 @@ } }, "node_modules/proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -6023,25 +10913,200 @@ } } }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-agent/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "license": "MIT" }, "node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -6064,7 +11129,8 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -6083,12 +11149,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/ramda": { "version": "0.30.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.30.1.tgz", "integrity": "sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/ramda" @@ -6098,6 +11166,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/ramda-adjunct/-/ramda-adjunct-5.1.0.tgz", "integrity": "sha512-8qCpl2vZBXEJyNbi4zqcgdfHtcdsWjOGbiNSEnEBrM6Y0OKOT8UxJbIVGm1TIcjaSu2MxaWcgtsNlKlCk7o7qg==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.3" }, @@ -6113,6 +11182,7 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "license": "MIT", "dependencies": { "drange": "^1.0.2", "ret": "^0.2.0" @@ -6121,10 +11191,19 @@ "node": ">=4" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } @@ -6133,6 +11212,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -6141,6 +11221,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -6155,6 +11236,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -6166,6 +11248,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", "integrity": "sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==", + "license": "MIT", "dependencies": { "copy-to-clipboard": "^3.3.1", "prop-types": "^15.8.1" @@ -6178,6 +11261,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.3.0.tgz", "integrity": "sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA==", + "license": "MIT", "dependencies": { "lodash.debounce": "^4", "prop-types": "^15.8.1" @@ -6190,6 +11274,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6202,6 +11287,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/react-immutable-proptypes/-/react-immutable-proptypes-2.2.0.tgz", "integrity": "sha512-Vf4gBsePlwdGvSZoLSBfd4HAP93HDauMY4fDjXhreg/vg6F3Fj/MXDNyTbltPC/xZKmZc+cjLu3598DdYK6sgQ==", + "license": "MIT", "dependencies": { "invariant": "^2.2.2" }, @@ -6213,6 +11299,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz", "integrity": "sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A==", + "license": "MIT", "peerDependencies": { "immutable": ">= 2 || >= 4.0.0-rc", "react": ">= 16.6", @@ -6223,19 +11310,23 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/react-inspector/-/react-inspector-6.0.2.tgz", "integrity": "sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ==", + "license": "MIT", "peerDependencies": { "react": "^16.8.4 || ^17.0.0 || ^18.0.0" } }, "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -6258,6 +11349,7 @@ "version": "15.6.1", "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.1.tgz", "integrity": "sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.3.1", "highlight.js": "^10.4.1", @@ -6274,6 +11366,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "license": "ISC", "dependencies": { "mute-stream": "~0.0.4" }, @@ -6285,6 +11378,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6295,15 +11389,47 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -6311,15 +11437,32 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.1.0.tgz", + "integrity": "sha512-5G5k9sYo5H5L0kd7UETiJZFTkIClH31fSmaEk2eU8E7mrmF0J1t6RqmFXOCJmSRlNd3QyvDUK/AWL9psbKaN0Q==", + "dependencies": { + "@redis/bloom": "5.1.0", + "@redis/client": "5.1.0", + "@redis/json": "5.1.0", + "@redis/search": "5.1.0", + "@redis/time-series": "5.1.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" }, "node_modules/redux-immutable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz", "integrity": "sha512-SchSn/DWfGb3oAejd+1hhHx01xUoxY+V7TeK0BKqpkLKiQPVFf7DYzEaKmrEVxsWxielKfSK9/Xq66YyxgR1cg==", + "license": "BSD-3-Clause", "peerDependencies": { "immutable": "^3.8.1 || ^4.0.0-rc.1" } @@ -6328,6 +11471,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/refractor/-/refractor-3.6.0.tgz", "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", + "license": "MIT", "dependencies": { "hastscript": "^6.0.0", "parse-entities": "^2.0.0", @@ -6342,19 +11486,16 @@ "version": "1.27.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.27.0.tgz", "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/remarkable": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.1.tgz", "integrity": "sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==", + "license": "MIT", "dependencies": { "argparse": "^1.0.10", "autolinker": "^3.11.0" @@ -6370,6 +11511,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -6377,20 +11519,32 @@ "node_modules/remarkable/node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" }, "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "license": "MIT", "engines": { "node": ">=0.10" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-in-the-middle": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz", "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==", + "license": "MIT", "dependencies": { "debug": "^4.1.1", "module-details-from-path": "^1.0.3", @@ -6401,9 +11555,10 @@ } }, "node_modules/require-in-the-middle/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -6419,22 +11574,32 @@ "node_modules/require-in-the-middle/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", - "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -6450,23 +11615,128 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ret": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rndm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6485,6 +11755,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -6506,7 +11777,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-buffer": { "version": "5.2.1", @@ -6525,12 +11797,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6546,25 +11820,41 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "license": "ISC" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6576,6 +11866,7 @@ "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -6599,6 +11890,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6606,12 +11898,19 @@ "node_modules/send/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" }, "node_modules/serialize-error": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -6622,10 +11921,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -6636,10 +11948,17 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -6652,15 +11971,23 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -6674,6 +12001,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -6686,6 +12014,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6693,12 +12022,14 @@ "node_modules/shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==", + "license": "BSD-2-Clause" }, "node_modules/short-unique-id": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz", - "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.3.2.tgz", + "integrity": "sha512-KRT/hufMSxXKEDSQujfVE0Faa/kZ51ihUcZQAcmP04t00DvPj7Ox5anHke1sJYUtzSuiT/Y5uyzg/W7bBEGhCg==", + "license": "Apache-2.0", "bin": { "short-unique-id": "bin/short-unique-id", "suid": "bin/short-unique-id" @@ -6708,6 +12039,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -6726,6 +12058,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -6741,6 +12074,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6758,6 +12092,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -6775,12 +12110,43 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -6790,6 +12156,7 @@ "version": "2.8.4", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", + "license": "MIT", "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" @@ -6803,6 +12170,7 @@ "version": "8.0.5", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -6813,9 +12181,10 @@ } }, "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -6831,20 +12200,24 @@ "node_modules/socks-proxy-agent/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -6854,20 +12227,92 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/speakeasy": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", + "integrity": "sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==", + "license": "MIT", + "dependencies": { + "base32.js": "0.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "license": "BSD-3-Clause" + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6876,6 +12321,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" @@ -6885,6 +12331,7 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -6906,42 +12353,175 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", + "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/superagent": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "bin": { + "mime": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4.0.0" } }, - "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ] + "node_modules/superagent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/supertest": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", + "license": "MIT", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^10.2.1" + }, + "engines": { + "node": ">=14.18.0" + } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6953,6 +12533,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6961,17 +12542,18 @@ } }, "node_modules/swagger-client": { - "version": "3.34.1", - "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.34.1.tgz", - "integrity": "sha512-aqk315C959936kijVpR28Q07eugElW9vp77a57hdFlQDF8Kuln7SeB1MwXnTCOQEM6/pIWYN00QlvIEwHqQkqw==", + "version": "3.35.3", + "resolved": "https://registry.npmjs.org/swagger-client/-/swagger-client-3.35.3.tgz", + "integrity": "sha512-4bO+dhBbasP485Ak67o46cWNVUnV0/92ypb2997bhvxTO2M+IuQZM1ilkN/7nSaiGuxDKJhkuL54I35PVI3AAw==", + "license": "Apache-2.0", "dependencies": { "@babel/runtime-corejs3": "^7.22.15", "@scarf/scarf": "=1.4.0", - "@swagger-api/apidom-core": ">=1.0.0-beta.12 <1.0.0-rc.0", - "@swagger-api/apidom-error": ">=1.0.0-beta.12 <1.0.0-rc.0", - "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.12 <1.0.0-rc.0", - "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.12 <1.0.0-rc.0", - "@swagger-api/apidom-reference": ">=1.0.0-beta.12 <1.0.0-rc.0", + "@swagger-api/apidom-core": ">=1.0.0-beta.39 <1.0.0-rc.0", + "@swagger-api/apidom-error": ">=1.0.0-beta.39 <1.0.0-rc.0", + "@swagger-api/apidom-json-pointer": ">=1.0.0-beta.39 <1.0.0-rc.0", + "@swagger-api/apidom-ns-openapi-3-1": ">=1.0.0-beta.39 <1.0.0-rc.0", + "@swagger-api/apidom-reference": ">=1.0.0-beta.39 <1.0.0-rc.0", "@swaggerexpert/cookie": "^2.0.2", "deepmerge": "~4.3.0", "fast-json-patch": "^3.0.0-1", @@ -6979,16 +12561,17 @@ "neotraverse": "=0.6.18", "node-abort-controller": "^3.1.1", "node-fetch-commonjs": "^3.3.2", - "openapi-path-templating": "^2.0.1", - "openapi-server-url-templating": "^1.2.0", + "openapi-path-templating": "^2.2.1", + "openapi-server-url-templating": "^1.3.0", "ramda": "^0.30.1", - "ramda-adjunct": "^5.0.0" + "ramda-adjunct": "^5.1.0" } }, "node_modules/swagger-jsdoc": { "version": "6.2.8", "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "license": "MIT", "dependencies": { "commander": "6.2.0", "doctrine": "3.0.0", @@ -7008,14 +12591,37 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "license": "MIT", "engines": { "node": ">= 6" } }, + "node_modules/swagger-jsdoc/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/swagger-parser": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "license": "MIT", "dependencies": { "@apidevtools/swagger-parser": "10.0.3" }, @@ -7024,11 +12630,12 @@ } }, "node_modules/swagger-ui": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-5.20.0.tgz", - "integrity": "sha512-sqRZGkODumnNr3Ienb+cKRowwYd/Q4b3j6gMLrBVtnQRIr92wT0O3TAH5S0x/v84jxHfibug/e0y4glSfbd0og==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/swagger-ui/-/swagger-ui-5.22.0.tgz", + "integrity": "sha512-xGc24TU6wgS5YWVcxwy90GUgqbHYvWvtvftjptfjXEMgetdvGWnI8CNRaYG3hG5bIl5Zt1rfai4NO2lBxAUswg==", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime-corejs3": "^7.26.7", + "@babel/runtime-corejs3": "^7.27.1", "@scarf/scarf": "=1.4.0", "base64-js": "^1.5.1", "classnames": "^2.5.1", @@ -7058,7 +12665,7 @@ "reselect": "^5.1.1", "serialize-error": "^8.1.0", "sha.js": "^2.4.11", - "swagger-client": "^3.34.1", + "swagger-client": "^3.35.3", "url-parse": "^1.5.10", "xml": "=1.0.1", "xml-but-prettier": "^1.0.1", @@ -7066,9 +12673,10 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.20.0.tgz", - "integrity": "sha512-V5pozVTZxivdoQq/SQWxj3A4cOu5opk9MEbcZANX3Pj8X8xCrD1QCtBT7744Pz9msOvt0nnmy9JvM/9PGonCdg==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.21.0.tgz", + "integrity": "sha512-E0K3AB6HvQd8yQNSMR7eE5bk+323AUxjtCz/4ZNKiahOlPhPJxqn3UPIGs00cyY/dhrTDJ61L7C/a8u6zhGrZg==", + "license": "Apache-2.0", "dependencies": { "@scarf/scarf": "=1.4.0" } @@ -7077,6 +12685,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", + "license": "MIT", "dependencies": { "swagger-ui-dist": ">=5.0.0" }, @@ -7104,12 +12713,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/systeminformation": { "version": "5.25.11", "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.25.11.tgz", "integrity": "sha512-jI01fn/t47rrLTQB0FTlMCC+5dYx8o0RRF+R4BPiUNsvg5OdY0s9DKMFmJGrx5SwMZQ4cag0Gl6v8oycso9b/g==", + "license": "MIT", "optional": true, "os": [ "darwin", @@ -7132,10 +12743,72 @@ "url": "https://www.buymeacoffee.com/systeminfo" } }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -7146,21 +12819,42 @@ "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, "node_modules/tree-sitter": { "version": "0.21.1", "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.21.1.tgz", "integrity": "sha512-7dxoA6kYvtgWw80265MyqJlkRl4yawIjO7S5MigytjELkX43fV2WsAXzsNfO7sBpPPCF5Gp0+XzHk0DwLCq3xQ==", "hasInstallScript": true, + "license": "MIT", "optional": true, "peer": true, "dependencies": { @@ -7173,6 +12867,7 @@ "resolved": "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.24.8.tgz", "integrity": "sha512-Tc9ZZYwHyWZ3Tt1VEw7Pa2scu1YO7/d2BCBbKTx5hXwig3UfdQjsOPkPyLpDJOn/m1UBEWYAtSdGAwCSyagBqQ==", "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { "node-addon-api": "^8.2.2", @@ -7190,22 +12885,43 @@ "node_modules/ts-mixer": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz", - "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==" + "integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==", + "license": "MIT" }, "node_modules/ts-toolbelt": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", - "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==" + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "license": "Apache-2.0" }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } }, "node_modules/tv4": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==", + "license": [ + { + "type": "Public Domain", + "url": "http://geraintluff.github.io/tv4/LICENSE.txt" + }, + { + "type": "MIT", + "url": "http://jsonary.com/LICENSE.txt" + } + ], "engines": { "node": ">= 0.8.0" } @@ -7214,6 +12930,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz", "integrity": "sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==", + "license": "MIT", "optional": true, "dependencies": { "json-stringify-safe": "^5.0.1" @@ -7224,6 +12941,7 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -7231,10 +12949,22 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -7246,6 +12976,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -7257,20 +12988,47 @@ "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" }, "node_modules/types-ramda": { "version": "0.30.1", "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.30.1.tgz", "integrity": "sha512-1HTsf5/QVRmLzcGfldPFvkVsAdi1db1BBKzi7iW3KBUlOICg/nKnFS+jGqDJS3YD8VsWbAh7JiHeBvbsw8RPxA==", + "license": "MIT", "dependencies": { "ts-toolbelt": "^9.6.0" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7278,30 +13036,73 @@ "node_modules/unraw": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", - "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==", + "license": "MIT" + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "license": "MIT", "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" @@ -7311,15 +13112,23 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "license": "MIT" + }, "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -7328,6 +13137,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", @@ -7339,12 +13149,14 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -7357,14 +13169,31 @@ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz", + "integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==", + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -7373,6 +13202,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7381,6 +13211,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz", "integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==", + "license": "Apache-2.0", "dependencies": { "async": "^2.6.3", "git-node-fs": "^1.0.0", @@ -7395,14 +13226,26 @@ "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "license": "MIT", "dependencies": { "lodash": "^4.17.14" } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -7411,6 +13254,7 @@ "version": "0.24.5", "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.24.5.tgz", "integrity": "sha512-+J/2VSHN8J47gQUAvF8KDadrfz6uFYVjxoxbKWDoXVsH2u7yLdarCnIURnrMA6uSRkgX3SdmqM5BOoQjPdSh5w==", + "license": "MIT", "optional": true }, "node_modules/which": { @@ -7418,6 +13262,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -7428,10 +13273,17 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/which-typed-array": { "version": "1.1.19", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -7453,19 +13305,54 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } }, "node_modules/ws": { "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -7485,12 +13372,14 @@ "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", - "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==" + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "license": "MIT" }, "node_modules/xml-but-prettier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml-but-prettier/-/xml-but-prettier-1.0.1.tgz", "integrity": "sha512-C2CJaadHrZTqESlH03WOyw0oZTtoy2uEg6dSDF6YRg+9GnYNub53RRemLpnvtbHDFelxMx4LajiFsYeR6XJHgQ==", + "license": "MIT", "dependencies": { "repeat-string": "^1.5.2" } @@ -7499,6 +13388,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -7511,36 +13401,87 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", "engines": { "node": ">=4.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", "engines": { "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "2.0.0-1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "license": "ISC", "engines": { "node": ">= 6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7552,6 +13493,7 @@ "version": "5.0.5", "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "license": "MIT", "dependencies": { "lodash.get": "^4.4.2", "lodash.isequal": "^4.5.0", @@ -7571,6 +13513,7 @@ "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", "optional": true, "engines": { "node": "^12.20.0 || >=14" @@ -7579,7 +13522,77 @@ "node_modules/zenscroll": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/zenscroll/-/zenscroll-4.0.2.tgz", - "integrity": "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==" + "integrity": "sha512-jEA1znR7b4C/NnaycInCU6h/d15ZzCd1jmsruqOKnZP6WXQSMH3W2GL+OXbkruslU4h+Tzuos0HdswzRUk/Vgg==", + "license": "Unlicense" + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/zod": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", + "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.5", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", + "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } } } } diff --git a/package.json b/package.json index 2b01574c..f8296d10 100644 --- a/package.json +++ b/package.json @@ -1,36 +1,147 @@ { - "name": "prueba-arquitectura-backend-textiles", + "name": "backend-textiles", "version": "1.0.0", - "description": "prueba de arquitectura del backend utilizando AWS para el proyecto de textiles Code&Co", + "description": "Backend del proyecto para Altertex del departamento CodeCode", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "lint": "npx eslint . --fix" + "start": "nodemon app.js", + "test": "jest", + "lint": "npx eslint .", + "lint-fix": "npx eslint . --fix" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@aws-sdk/client-dynamodb": "^3.751.0", - "@aws-sdk/client-s3": "^3.750.0", + "@aws-sdk/client-s3": "^3.808.0", "@aws-sdk/lib-dynamodb": "^3.751.0", + "@aws-sdk/s3-request-presigner": "^3.787.0", + "@aws-sdk/types": "^3.775.0", "aws-sdk": "^2.1692.0", "bcryptjs": "^3.0.0", "body-parser": "^1.20.3", "cookie-parser": "^1.4.7", "cors": "^2.8.5", + "csurf": "^1.11.0", + "date-fns": "^4.1.0", "dotenv": "^16.4.7", + "exceljs": "^4.4.0", "express": "^4.21.2", + "helmet": "^8.1.0", + "i": "^0.3.7", + "json2csv": "^6.0.0-alpha.2", "jsonwebtoken": "^9.0.2", + "module-alias": "^2.2.3", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", + "mysql2": "^3.14.0", + "node-cron": "^3.0.3", + "nodemon": "^3.1.9", "pm2": "^5.4.3", + "qrcode": "^1.5.4", + "redis": "^5.1.0", + "speakeasy": "^2.0.0", + "supertest": "^7.1.0", "swagger-jsdoc": "^6.2.8", - "swagger-ui": "^5.20.0", + "swagger-ui": "^5.21.0", "swagger-ui-express": "^5.0.1" }, "devDependencies": { "@eslint/js": "^9.23.0", - "eslint": "^9.22.0" + "eslint": "^9.22.0", + "eslint-plugin-jsdoc": "^50.6.11", + "jest": "^29.7.0" + }, + "_moduleAliases": { + "@altertex/root": ".", + "@altertex/aut": "Autenticacion/", + "@altertex/aut/ctrl": "Autenticacion/Controladores/", + "@altertex/aut/datos": "Autenticacion/Datos/", + "@altertex/aut/repos": "Autenticacion/Datos/Repositorios", + "@altertex/aut/rutas": "Autenticacion/Rutas/", + "@altertex/aut/rutasInd": "Autenticacion/Rutas/RutasIndividuales", + "@altertex/usu": "Usuarios/", + "@altertex/usu/ctrl": "Usuarios/Controladores/", + "@altertex/usu/datos": "Usuarios/Datos/", + "@altertex/usu/repos": "Usuarios/Datos/Repositorios", + "@altertex/usu/rutas": "Usuarios/Rutas/", + "@altertex/usu/rutasInd": "Usuarios/Rutas/RutasIndividuales", + "@altertex/emp": "Empleados/", + "@altertex/emp/ctrl": "Empleados/Controladores/", + "@altertex/emp/datos": "Empleados/Datos/", + "@altertex/emp/repos": "Empleados/Datos/Repositorios", + "@altertex/emp/rutas": "Empleados/Rutas/", + "@altertex/emp/rutasInd": "Empleados/Rutas/RutasIndividuales", + "@altertex/cli": "Clientes/", + "@altertex/cli/ctrl": "Clientes/Controladores/", + "@altertex/cli/datos": "Clientes/Datos/", + "@altertex/cli/repos": "Clientes/Datos/Repositorios", + "@altertex/cli/rutas": "Clientes/Rutas/", + "@altertex/cli/rutasInd": "Clientes/Rutas/RutasIndividuales", + "@altertex/cuota": "Cuotas/", + "@altertex/cuota/ctrl": "Cuotas/Controladores/", + "@altertex/cuota/datos": "Cuotas/Datos/", + "@altertex/cuota/repos": "Cuotas/Datos/Repositorios", + "@altertex/cuota/rutas": "Cuotas/Rutas/", + "@altertex/cuota/rutasInd": "Cuotas/Rutas/RutasIndividuales", + "@altertex/config": "Configuracion/", + "@altertex/util": "Utilidades/", + "@altertex/util/bd": "Utilidades/BaseDeDatos/", + "@altertex/util/const": "Utilidades/Constantes/", + "@altertex/util/inter": "Utilidades/Intermediarios/", + "@altertex/util/vali": "Utilidades/Intermediarios/Validaciones", + "@altertex/rol": "Roles", + "@altertex/rol/ctrl": "Roles/Controladores", + "@altertex/rol/datos": "Roles/Datos", + "@altertex/rol/repos": "Roles/Datos/Repositorios", + "@altertex/rol/rutas": "Roles/Rutas", + "@altertex/rol/rutasInd": "Roles/Rutas/RutasIndividuales", + "@altertex/CRON": "CRON_JOBS/", + "@altertex/CRON/ctrl": "CRON_JOBS/Controladores/", + "@altertex/CRON/datos": "CRON_JOBS/Datos/", + "@altertex/CRON/repos": "CRON_JOBS/Datos/Repositorios/", + "@altertex/pro": "Productos/", + "@altertex/pro/ctrl": "Productos/Controladores/", + "@altertex/pro/datos": "Productos/Datos/", + "@altertex/pro/repos": "Productos/Datos/Repositorios", + "@altertex/pro/rutas": "Productos/Rutas/", + "@altertex/pro/rutasInd": "Productos/Rutas/RutasIndividuales", + "@altertex/prove": "Proveedores", + "@altertex/prove/ctrl": "Proveedores/Controladores/", + "@altertex/prove/datos": "Proveedores/Datos/", + "@altertex/prove/repos": "Proveedores/Datos/Repositorios", + "@altertex/prove/rutas": "Proveedores/Rutas/", + "@altertex/prove/rutasInd": "Proveedores/Rutas/RutasIndividuales", + "@altertex/cat": "Categorias/", + "@altertex/cat/ctrl": "Categorias/Controladores/", + "@altertex/cat/datos": "Categorias/Datos/", + "@altertex/cat/repos": "Categorias/Datos/Repositorios", + "@altertex/cat/rutas": "Categorias/Rutas/", + "@altertex/cat/rutasInd": "Categorias/Rutas/RutasIndividuales", + "@altertex/util/ser": "Utilidades/Servicios/", + "@altertex/setspro": "SetsProductos/", + "@altertex/setspro/ctrl": "SetsProductos/Controladores/", + "@altertex/setspro/datos": "SetsProductos/Datos/", + "@altertex/setspro/repos": "SetsProductos/Datos/Repositorios", + "@altertex/setspro/rutas": "SetsProductos/Rutas/", + "@altertex/setspro/rutasInd": "SetsProductos/Rutas/RutasIndividuales", + "@altertex/pedidos/": "Pedidos/", + "@altertex/pedidos/ctrl": "Pedidos/Controladores/", + "@altertex/pedidos/datos": "Pedidos/Datos/", + "@altertex/pedidos/repos": "Pedidos/Datos/Repositorios", + "@altertex/pedidos/rutas": "Pedidos/Rutas", + "@altertex/pedidos/rutasInd": "Pedidos/Rutas/RutasIndividuales", + "@altertex/eve": "Eventos/", + "@altertex/eve/ctrl": "Eventos/Controladores/", + "@altertex/eve/datos": "Eventos/Datos/", + "@altertex/eve/repos": "Eventos/Datos/Repositorios", + "@altertex/eve/rutas": "Eventos/Rutas/", + "@altertex/eve/rutasInd": "Eventos/Rutas/RutasIndividuales", + "@altertex/pago/ctrl": "Pagos/Controladores/", + "@altertex/pago/datos": "Pagos/Datos", + "@altertex/pago/repos": "Pagos/Datos/Repositorios/", + "@altertex/pago/rutas": "Pagos/Rutas/", + "@altertex/pago/rutas/rutasInd": "Pagos/Rutas/RutasIndividuales" } } diff --git a/util/middlewares/autorizarToken.js b/util/middlewares/autorizarToken.js deleted file mode 100644 index 48861021..00000000 --- a/util/middlewares/autorizarToken.js +++ /dev/null @@ -1,22 +0,0 @@ -const jwt = require("jsonwebtoken"); - -module.exports = async (req, res, next) => { - const token = req.cookies.token; // Get token from cookies - - console.log(req.cookies); - console.log(token); - - if (!token) { - return res.status(403).json({ message: "Acceso denegado" }); - } - - try { - const verificado = jwt.verify(token, process.env.JWT_SECRET); - req.user = verificado; - console.log("Verificado"); - next(); - } catch (error) { - console.log("no Verificado"); - res.status(401).json({ message: "Token inválido", error: error }); - } -}; diff --git a/util/middlewares/revisarApiKey.js b/util/middlewares/revisarApiKey.js deleted file mode 100644 index 23f62c82..00000000 --- a/util/middlewares/revisarApiKey.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = (nombreHeader, mensajeError = "Api key invalida") => { - return (req, res, next) => { - const valorHeader = req.get(nombreHeader); - if (valorHeader !== process.env.API_KEY) { - return res.status(400).json({ message: mensajeError }); - } - next(); - }; -}; diff --git a/util/services/enviarDatos.js b/util/services/enviarDatos.js deleted file mode 100644 index fdaf4d8c..00000000 --- a/util/services/enviarDatos.js +++ /dev/null @@ -1,14 +0,0 @@ -const { DynamoDBClient } = require("@aws-sdk/client-dynamodb"); -const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb"); - -const clienteDynamo = new DynamoDBClient({ region: "us-east-1" }); -const db = DynamoDBDocumentClient.from(clienteDynamo); - -module.exports = async (nombreTabla, modelo) => { - const comando = new PutCommand({ - TableName: nombreTabla, - Item: modelo, - }); - - return db.send(comando); -}; diff --git a/util/services/enviarS3.js b/util/services/enviarS3.js deleted file mode 100644 index cd6aef0e..00000000 --- a/util/services/enviarS3.js +++ /dev/null @@ -1,16 +0,0 @@ -const { S3Client, PutObjectCommand } = require("@aws-sdk/client-s3"); - -const s3 = new S3Client({ - region: "us-east-1", -}); - -module.exports = async (params) => { - try { - await s3.send(new PutObjectCommand(params)); - const fileName = params.Key; - return `https://${process.env.AWS_BUCKET_NAME}.s3.${process.env.AWS_REGION}.amazonaws.com/uploads/${fileName}`; - } catch (error) { - console.error("Error uploading to S3:", error); - throw new Error("Failed to upload file to S3"); - } -}; diff --git a/util/services/generarNombreUnico.js b/util/services/generarNombreUnico.js deleted file mode 100644 index 967b004a..00000000 --- a/util/services/generarNombreUnico.js +++ /dev/null @@ -1,7 +0,0 @@ -const crypto = require("crypto"); -const path = require("path"); - -module.exports = (originalName) => { - const randomBytes = crypto.randomBytes(16).toString("hex"); - return `${Date.now()}-${randomBytes}${path.extname(originalName)}`; -}; diff --git a/util/services/recibirDatos.js b/util/services/recibirDatos.js deleted file mode 100644 index d0382cbf..00000000 --- a/util/services/recibirDatos.js +++ /dev/null @@ -1,14 +0,0 @@ -const { DynamoDBClient } = require("@aws-sdk/client-dynamodb"); -const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb"); - -const clienteDynamo = new DynamoDBClient({ region: "us-east-1" }); -const db = DynamoDBDocumentClient.from(clienteDynamo); - -module.exports = async (nombreTabla, llaves) => { - const comando = new GetCommand({ - TableName: nombreTabla, - Key: llaves, - }); - const response = await db.send(comando); - return response.Item; -};