From c0073e1e9d4f0a04e1f5fe180c6d29a0fcbff9e9 Mon Sep 17 00:00:00 2001 From: Desmond Obisi Date: Tue, 5 Nov 2024 09:41:17 +0100 Subject: [PATCH] chore: eslint, prettier config setup --- .prettierignore | 4 + .prettierrc | 8 + README.md | 15 +- eslint.config.js | 32 + package-lock.json | 1831 ++++++++++++++++++++- package.json | 11 + src/app.js | 27 +- src/configs/db.config.js | 9 +- src/configs/jwt.config.js | 12 +- src/configs/logger.config.js | 41 +- src/controllers/auth.controller.js | 144 +- src/controllers/invitation.controller.js | 84 +- src/controllers/user.controller.js | 99 +- src/middlewares/auth.js | 52 +- src/middlewares/logger.js | 97 +- src/middlewares/validate.js | 38 +- src/models/achievement.model.js | 38 +- src/models/invitation.model.js | 78 +- src/models/maintainer.model.js | 62 +- src/models/project.model.js | 124 +- src/models/user.model.js | 65 +- src/repositories/auth.repository.js | 73 +- src/repositories/invitation.repository.js | 78 +- src/repositories/user.repository.js | 24 +- src/routes/auth.route.js | 35 +- src/routes/invitation.route.js | 29 +- src/routes/user.route.js | 15 +- src/server.js | 22 +- src/services/auth.service.js | 157 +- src/services/invitation.service.js | 52 +- src/services/user.service.js | 48 +- src/validations/achievement.js | 16 +- src/validations/auth.js | 62 +- src/validations/index.js | 51 +- src/validations/invitation.js | 30 +- src/validations/maintainer.js | 30 +- src/validations/project.js | 184 +-- src/validations/user.js | 20 +- 38 files changed, 2801 insertions(+), 996 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 eslint.config.js diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6667779 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +node_modules +dist +coverage +.env diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..cfc57d0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "endOfLine": "lf" +} diff --git a/README.md b/README.md index 9e12883..c200411 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,42 @@ # AFOS-API -This is the backend API for the African Open Source(AFOS) project. AFOS is a platform that showcases open source projects from Africa and provides a platform for developers to showcase their skills. +This is the backend API for the African Open Source(AFOS) project. AFOS is a platform that showcases open source projects from Africa and provides a platform for developers to showcase their skills. ## Architecture + This is a monorepo that contains the backend API for the AFOS project. The backend API is built using Node.js, Express, and MySQL. The backend API is responsible for handling all the requests from the frontend. Tools and technologies used in this project include: + - [Node.js](https://nodejs.org/en): A JavaScript runtime built on Chrome's V8 JavaScript engine. - [Express](https://expressjs.com/): A web application framework for Node.js. - [MySQL](https://www.mysql.com/): A relational database management system. -- [Sequelize](https://sequelize.org/): A promise-based Node.js ORM for MySQL. +- [Sequelize](https://sequelize.org/): A promise-based Node.js ORM for MySQL. - [JWT](https://jwt.io/): A JSON Web Token library for node.js. - [Zod](https://zod.dev/): A TypeScript-first schema validation library. - [Nodemailer](https://nodemailer.com/): A module for Node.js applications to allow easy as cake email sending. -- [Docker](https://www.docker.com/): A platform for developers and sysadmins to build, ship, and run distributed applications. +- [Docker](https://www.docker.com/): A platform for developers and sysadmins to build, ship, and run distributed applications. - [Jest](https://jestjs.io/): A delightful JavaScript Testing Framework with a focus on simplicity. [![AFOS Architecture](/public/images/AFOS%20Arch%20Diagram.png)](https://dbdiagram.io/d/67299654e9daa85aca52308f) - ## Database Diagram + This is the database diagram for the AFOS project. The database is built using MySQL. The schema diagram below shows the tables in the database and the relationships between them. [![AFOS Database Diagram](/public/images/AFOS%20DB.png)](https://dbdiagram.io/d/67299654e9daa85aca52308f) ## How to setup project + - Clone the repository. - Run `npm install` to install all dependencies. - Create a `.env` file in the root directory and copy the contents of `.env.example` into it. Fill the variables with the required values. - Run `npm run dev` to start the server. ## How to run tests + - Run `npm run test` to run all tests. - Run `npm run test:watch` to run all tests in watch mode. - Run `npm run test:coverage` to run all tests and generate a coverage report. ## Contributing -Contributions are welcome! Please feel free to open an issue or submit a pull request. Make sure to follow the [Contributing Guidelines](CONTRIBUTING.md) before submitting a pull request. \ No newline at end of file + +Contributions are welcome! Please feel free to open an issue or submit a pull request. Make sure to follow the [Contributing Guidelines](CONTRIBUTING.md) before submitting a pull request. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..985a39c --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,32 @@ +import globals from 'globals'; +import js from '@eslint/js'; +import eslintPluginPrettier from 'eslint-plugin-prettier'; +import prettierConfig from 'eslint-config-prettier'; + +export default [ + js.configs.recommended, + prettierConfig, + { + files: ['**/*.js'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + globals: { + ...globals.node, + }, + }, + plugins: { + prettier: eslintPluginPrettier, + }, + rules: { + 'prettier/prettier': 'error', + 'no-console': 'warn', + 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + 'no-duplicate-imports': 'error', + 'no-template-curly-in-string': 'error', + 'prefer-const': 'error', + 'require-await': 'error', + 'no-return-await': 'error', + }, + }, +]; diff --git a/package-lock.json b/package-lock.json index 5d1de74..29bb13e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,375 @@ "sequelize": "^6.37.5", "winston": "^3.16.0", "zod": "^3.23.8" + }, + "devDependencies": { + "@babel/eslint-parser": "^7.25.9", + "eslint": "^9.14.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "globals": "^15.12.0", + "prettier": "^3.3.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", + "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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, + "peer": true + }, + "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, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/eslint-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.9.tgz", + "integrity": "sha512-5UXfgpK0j0Xr/xIdgdLEhOFxaDZ0bRPWJJchRpqOSur/3rZoPbqqki5mm0p4NE2cs28krBEiSM2MB7//afRSQQ==", + "dev": true, + "dependencies": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || >=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0", + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/@babel/eslint-parser/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, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", + "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", + "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.26.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", + "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/generator": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/template": "^7.25.9", + "@babel/types": "^7.25.9", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "peer": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "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, + "peer": true + }, + "node_modules/@babel/types": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", + "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@colors/colors": { @@ -38,6 +407,314 @@ "kuler": "^2.0.0" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "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" + }, + "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" + }, + "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.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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 + }, + "node_modules/@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "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" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.14.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.14.0.tgz", + "integrity": "sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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==", + "dev": true, + "engines": { + "node": ">=18.18.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==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.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==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.0.tgz", + "integrity": "sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "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, + "peer": true + }, + "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, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -46,6 +723,18 @@ "@types/ms": "*" } }, + "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/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", @@ -92,6 +781,82 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "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, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansi-styles/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ansi-styles/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==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -110,6 +875,12 @@ "node": ">= 6.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==", + "dev": true + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -157,6 +928,49 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.24.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", + "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "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" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001669", + "electron-to-chromium": "^1.5.41", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -211,6 +1025,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001677", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz", + "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==", + "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" + } + ], + "peer": true + }, + "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, + "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/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -251,6 +1111,12 @@ "text-hex": "1.0.x" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -270,6 +1136,13 @@ "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, + "peer": true + }, "node_modules/cookie": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", @@ -283,6 +1156,20 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -291,6 +1178,12 @@ "ms": "2.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/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -361,6 +1254,13 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, + "node_modules/electron-to-chromium": { + "version": "1.5.51", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz", + "integrity": "sha512-kKeWV57KSS8jH4alKt/jKnvHPmJgBxXzGUSbMd4eQF+iOsVPl7bz2KUmu6eo80eMP8wVioTfTyTzdMgM15WXNg==", + "dev": true, + "peer": true + }, "node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", @@ -393,11 +1293,306 @@ "node": ">= 0.4" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.14.0.tgz", + "integrity": "sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.14.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.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", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.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==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/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, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -463,28 +1658,99 @@ "node": ">= 0.10.0" } }, + "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-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "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/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" }, - "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/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, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "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" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">= 0.8" + "node": ">=16" } }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -522,6 +1788,16 @@ "is-property": "^1.0.2" } }, + "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, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -540,6 +1816,30 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", + "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -551,6 +1851,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -640,6 +1949,40 @@ } ] }, + "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, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/inflection": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", @@ -666,6 +2009,27 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.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==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", @@ -682,6 +2046,75 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "peer": true, + "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 + }, + "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 + }, + "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 + }, + "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, + "peer": true, + "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", @@ -727,11 +2160,48 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -767,6 +2237,12 @@ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, + "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 + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -874,6 +2350,18 @@ "node": ">= 0.6" } }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -939,6 +2427,12 @@ "node": ">=12.0.0" } }, + "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 + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -947,6 +2441,13 @@ "node": ">= 0.6" } }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "peer": true + }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -977,6 +2478,65 @@ "fn.name": "1.x.x" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -985,6 +2545,24 @@ "node": ">= 0.8" } }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", @@ -995,6 +2573,49 @@ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" }, + "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, + "peer": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -1015,6 +2636,15 @@ "node": ">= 0.10" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -1064,6 +2694,15 @@ "node": ">= 6" } }, + "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, + "engines": { + "node": ">=4" + } + }, "node_modules/retry-as-promised": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", @@ -1278,6 +2917,27 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -1335,11 +2995,57 @@ "safe-buffer": "~5.2.0" } }, + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/text-hex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -1361,6 +3067,24 @@ "node": ">= 14.0.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==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1386,6 +3110,46 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "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" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "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, + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -1423,6 +3187,21 @@ "node": ">= 0.8" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/winston": { "version": "3.16.0", "resolved": "https://registry.npmjs.org/winston/-/winston-3.16.0.tgz", @@ -1480,6 +3259,34 @@ "@types/node": "*" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", diff --git a/package.json b/package.json index 1b865b1..85c8923 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "main": "index.js", "type": "module", "scripts": { + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write .", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], @@ -20,5 +23,13 @@ "sequelize": "^6.37.5", "winston": "^3.16.0", "zod": "^3.23.8" + }, + "devDependencies": { + "@babel/eslint-parser": "^7.25.9", + "eslint": "^9.14.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "globals": "^15.12.0", + "prettier": "^3.3.3" } } diff --git a/src/app.js b/src/app.js index 0c7f2e3..8d40c34 100644 --- a/src/app.js +++ b/src/app.js @@ -12,24 +12,21 @@ app.use(logger); // Database initialization sequelize - .authenticate() - .then(() => { - logger.info('Database connected successfully'); - return sequelize.sync(); - }) - .then(() => { - logger.info('Database synchronized'); - }) - .catch((error) => { - logger.error('Unable to connect to the database:', error); - }); + .authenticate() + .then(() => { + logger.info('Database connected successfully'); + return sequelize.sync(); + }) + .then(() => { + logger.info('Database synchronized'); + }) + .catch((error) => { + logger.error('Unable to connect to the database:', error); + }); // Routes app.get('/healthcheck', (req, res) => { - res.status(200).send('AFOS server is up and running'); + res.status(200).send('AFOS server is up and running'); }); -// Error handling middleware -app.use(handleServerError); - export default app; diff --git a/src/configs/db.config.js b/src/configs/db.config.js index 5c836ad..298257b 100644 --- a/src/configs/db.config.js +++ b/src/configs/db.config.js @@ -3,15 +3,10 @@ import dotenv from 'dotenv'; dotenv.config(); -const sequelize = new Sequelize( - process.env.DB_NAME, - process.env.DB_USER, - process.env.DB_PASSWORD, - { +const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USER, process.env.DB_PASSWORD, { host: process.env.DB_HOST, dialect: 'mysql', logging: false, - } -); +}); export default sequelize; diff --git a/src/configs/jwt.config.js b/src/configs/jwt.config.js index f27b4ea..2cc9be2 100644 --- a/src/configs/jwt.config.js +++ b/src/configs/jwt.config.js @@ -4,13 +4,13 @@ import dotenv from 'dotenv'; dotenv.config(); export const generateToken = (user) => { - return jwt.sign( - { id: user.id, email: user.email, isSuperAdmin: user.isSuperAdmin }, - process.env.JWT_SECRET, - { expiresIn: '24h' } - ); + return jwt.sign( + { id: user.id, email: user.email, isSuperAdmin: user.isSuperAdmin }, + process.env.JWT_SECRET, + { expiresIn: '24h' } + ); }; export const verifyToken = (token) => { - return jwt.verify(token, process.env.JWT_SECRET); + return jwt.verify(token, process.env.JWT_SECRET); }; diff --git a/src/configs/logger.config.js b/src/configs/logger.config.js index daee0a5..a1da6e6 100644 --- a/src/configs/logger.config.js +++ b/src/configs/logger.config.js @@ -1,31 +1,28 @@ import winston from 'winston'; const logger = winston.createLogger({ - level: 'info', - format: winston.format.combine( - winston.format.timestamp({ - format: 'YYYY-MM-DD HH:mm:ss', - }), - winston.format.errors({ stack: true }), - winston.format.splat(), - winston.format.json() - ), - defaultMeta: { service: 'afos-api' }, - transports: [ - new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), - new winston.transports.File({ filename: 'logs/combined.log' }), - ], + level: 'info', + format: winston.format.combine( + winston.format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss', + }), + winston.format.errors({ stack: true }), + winston.format.splat(), + winston.format.json() + ), + defaultMeta: { service: 'afos-api' }, + transports: [ + new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), + new winston.transports.File({ filename: 'logs/combined.log' }), + ], }); if (process.env.NODE_ENV !== 'production') { - logger.add( - new winston.transports.Console({ - format: winston.format.combine( - winston.format.colorize(), - winston.format.simple() - ), - }) - ); + logger.add( + new winston.transports.Console({ + format: winston.format.combine(winston.format.colorize(), winston.format.simple()), + }) + ); } export default logger; diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 08743ea..725f3d4 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,93 +1,93 @@ import * as authService from '../services/auth.service.js'; export const login = async (req, res) => { - try { - const { email, password } = req.body; - const result = await authService.loginService(email, password); + try { + const { email, password } = req.body; + const result = await authService.loginService(email, password); - return res.status(200).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(401).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(401).json({ + status: 'error', + message: error.message, + }); + } }; export const acceptInvitation = async (req, res) => { - try { - const { token, fullname, password } = req.body; - const result = await authService.acceptInvitationService(token, { - fullname, - password, - }); + try { + const { token, fullname, password } = req.body; + const result = await authService.acceptInvitationService(token, { + fullname, + password, + }); - return res.status(200).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const changePassword = async (req, res) => { - try { - const { currentPassword, newPassword } = req.body; - const result = await authService.changePasswordService( - req.user.id, - currentPassword, - newPassword - ); + try { + const { currentPassword, newPassword } = req.body; + const result = await authService.changePasswordService( + req.user.id, + currentPassword, + newPassword + ); - return res.status(200).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const forgotPassword = async (req, res) => { - try { - const { email } = req.body; - const result = await authService.forgotPasswordService(email); + try { + const { email } = req.body; + const result = await authService.forgotPasswordService(email); - return res.status(200).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const resetPassword = async (req, res) => { - try { - const { token, newPassword } = req.body; - const result = await authService.resetPasswordService(token, newPassword); + try { + const { token, newPassword } = req.body; + const result = await authService.resetPasswordService(token, newPassword); - return res.status(200).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; diff --git a/src/controllers/invitation.controller.js b/src/controllers/invitation.controller.js index b9d4962..543d9a3 100644 --- a/src/controllers/invitation.controller.js +++ b/src/controllers/invitation.controller.js @@ -1,56 +1,54 @@ import * as invitationService from '../services/invitation.service.js'; export const createInvitation = async (req, res) => { - try { - const { invitee_email, isSuperAdmin } = req.body; - const result = await invitationService.createInvitationService( - req.user.id, - invitee_email, - isSuperAdmin - ); + try { + const { invitee_email, isSuperAdmin } = req.body; + const result = await invitationService.createInvitationService( + req.user.id, + invitee_email, + isSuperAdmin + ); - return res.status(201).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + return res.status(201).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const getInvitations = async (req, res) => { - try { - const invitations = await invitationService.getInvitationsService(); + try { + const invitations = await invitationService.getInvitationsService(); - return res.status(200).json({ - status: 'success', - data: invitations, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: invitations, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const getInvitationById = async (req, res) => { - try { - const invitation = await invitationService.getInvitationByIdService( - req.params.id - ); + try { + const invitation = await invitationService.getInvitationByIdService(req.params.id); - return res.status(200).json({ - status: 'success', - data: invitation, - }); - } catch (error) { - return res.status(404).json({ - status: 'error', - message: error.message, - }); - } + return res.status(200).json({ + status: 'success', + data: invitation, + }); + } catch (error) { + return res.status(404).json({ + status: 'error', + message: error.message, + }); + } }; diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index 26bf69a..3d443ea 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -1,64 +1,61 @@ import * as userService from '../services/user.service.js'; export const getUsers = async (req, res) => { - try { - const users = await userService.getUsersService(); - return res.status(200).json({ - status: 'success', - data: users, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + try { + const users = await userService.getUsersService(); + return res.status(200).json({ + status: 'success', + data: users, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const getUserById = async (req, res) => { - try { - const user = await userService.getUserByIdService(req.params.id); - return res.status(200).json({ - status: 'success', - data: user, - }); - } catch (error) { - return res.status(404).json({ - status: 'error', - message: error.message, - }); - } + try { + const user = await userService.getUserByIdService(req.params.id); + return res.status(200).json({ + status: 'success', + data: user, + }); + } catch (error) { + return res.status(404).json({ + status: 'error', + message: error.message, + }); + } }; export const updateUser = async (req, res) => { - try { - const updatedUser = await userService.updateUserService( - req.params.id, - req.body - ); - return res.status(200).json({ - status: 'success', - data: updatedUser, - }); - } catch (error) { - return res.status(400).json({ - status: 'error', - message: error.message, - }); - } + try { + const updatedUser = await userService.updateUserService(req.params.id, req.body); + return res.status(200).json({ + status: 'success', + data: updatedUser, + }); + } catch (error) { + return res.status(400).json({ + status: 'error', + message: error.message, + }); + } }; export const deleteUser = async (req, res) => { - try { - const result = await userService.deleteUserService(req.params.id); - return res.status(200).json({ - status: 'success', - data: result, - }); - } catch (error) { - return res.status(404).json({ - status: 'error', - message: error.message, - }); - } + try { + const result = await userService.deleteUserService(req.params.id); + return res.status(200).json({ + status: 'success', + data: result, + }); + } catch (error) { + return res.status(404).json({ + status: 'error', + message: error.message, + }); + } }; diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index 30e2ba3..021bb3d 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -1,34 +1,34 @@ import { verifyToken } from '../configs/jwt.config.js'; -export const authenticate = async (req, res, next) => { - try { - const authHeader = req.headers.authorization; +export const authenticate = (req, res, next) => { + try { + const authHeader = req.headers.authorization; - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return res.status(401).json({ - status: 'error', - message: 'Authentication required', - }); - } + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ + status: 'error', + message: 'Authentication required', + }); + } - const token = authHeader.split(' ')[1]; - const decoded = verifyToken(token); - req.user = decoded; - next(); - } catch (error) { - return res.status(401).json({ - status: 'error', - message: 'Invalid or expired token', - }); - } + const token = authHeader.split(' ')[1]; + const decoded = verifyToken(token); + req.user = decoded; + next(); + } catch (error) { + return res.status(401).json({ + status: 'error', + message: error.message || 'Invalid or expired token', + }); + } }; export const authorizeAdmin = (req, res, next) => { - if (!req.user.isSuperAdmin) { - return res.status(403).json({ - status: 'error', - message: 'Admin access required', - }); - } - next(); + if (!req.user.isSuperAdmin) { + return res.status(403).json({ + status: 'error', + message: 'Admin access required', + }); + } + next(); }; diff --git a/src/middlewares/logger.js b/src/middlewares/logger.js index c0ba91b..167f541 100644 --- a/src/middlewares/logger.js +++ b/src/middlewares/logger.js @@ -1,59 +1,58 @@ import logger from '../configs/logger.config.js'; const requestLogger = (req, res, next) => { - const start = new Date(); - const requestData = { - ip: req.ip, - method: req.method, - path: req.path, - url: req.originalUrl, - userAgent: req.get('user-agent'), - }; - - // Incoming request log - logger.info('Incoming request', requestData); - - // Capture response data - const originalSend = res.send; - res.send = function (body) { - res.send = originalSend; - res.responseBody = body; - return res.send(body); - }; - - res.on('finish', () => { - const duration = new Date() - start; - const level = res.statusCode >= 400 ? 'error' : 'info'; - - // Outgoing response log - const responseData = { - ...requestData, - duration: `${duration}ms`, - httpStatus: res.statusCode, - contentType: res.get('content-type'), + const start = new Date(); + const requestData = { + ip: req.ip, + method: req.method, + path: req.path, + url: req.originalUrl, + userAgent: req.get('user-agent'), }; - logger[level]('Outgoing response', responseData); + // Incoming request log + logger.info('Incoming request', requestData); - // Log response status (success/error) without data - if (res.statusCode >= 400) { - logger.error('Error response', { - status: res.statusCode, - path: req.path, - method: req.method, - message: - JSON.parse(res.responseBody).message || 'Internal Server Error', - }); - } else { - logger.info('Success response', { - status: res.statusCode, - path: req.path, - method: req.method, - }); - } - }); + // Capture response data + const originalSend = res.send; + res.send = function (body) { + res.send = originalSend; + res.responseBody = body; + return res.send(body); + }; - next(); + res.on('finish', () => { + const duration = new Date() - start; + const level = res.statusCode >= 400 ? 'error' : 'info'; + + // Outgoing response log + const responseData = { + ...requestData, + duration: `${duration}ms`, + httpStatus: res.statusCode, + contentType: res.get('content-type'), + }; + + logger[level]('Outgoing response', responseData); + + // Log response status (success/error) without data + if (res.statusCode >= 400) { + logger.error('Error response', { + status: res.statusCode, + path: req.path, + method: req.method, + message: JSON.parse(res.responseBody).message || 'Internal Server Error', + }); + } else { + logger.info('Success response', { + status: res.statusCode, + path: req.path, + method: req.method, + }); + } + }); + + next(); }; export default requestLogger; diff --git a/src/middlewares/validate.js b/src/middlewares/validate.js index 445ab99..a05d59f 100644 --- a/src/middlewares/validate.js +++ b/src/middlewares/validate.js @@ -1,26 +1,26 @@ import { ZodError } from 'zod'; const validate = (schema) => async (req, res, next) => { - try { - await schema.parseAsync({ - body: req.body, - query: req.query, - params: req.params, - }); - return next(); - } catch (error) { - if (error instanceof ZodError) { - return res.status(400).json({ - status: 'error', - message: 'Validation failed', - errors: error.errors.map((err) => ({ - field: err.path.join('.'), - message: err.message, - })), - }); + try { + await schema.parseAsync({ + body: req.body, + query: req.query, + params: req.params, + }); + return next(); + } catch (error) { + if (error instanceof ZodError) { + return res.status(400).json({ + status: 'error', + message: 'Validation failed', + errors: error.errors.map((err) => ({ + field: err.path.join('.'), + message: err.message, + })), + }); + } + return next(error); } - return next(error); - } }; export default validate; diff --git a/src/models/achievement.model.js b/src/models/achievement.model.js index 87ea468..45ceb87 100644 --- a/src/models/achievement.model.js +++ b/src/models/achievement.model.js @@ -3,26 +3,26 @@ import sequelize from '../configs/db.config.js'; import Project from './project.model.js'; const Achievement = sequelize.define('Achievement', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - project_id: { - type: DataTypes.INTEGER, - references: { - model: Project, - key: 'id', + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + project_id: { + type: DataTypes.INTEGER, + references: { + model: Project, + key: 'id', + }, + }, + item: { + type: DataTypes.TEXT, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, }, - }, - item: { - type: DataTypes.TEXT, - allowNull: false, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, }); Achievement.belongsTo(Project); diff --git a/src/models/invitation.model.js b/src/models/invitation.model.js index 1fe89ed..9a73238 100644 --- a/src/models/invitation.model.js +++ b/src/models/invitation.model.js @@ -3,45 +3,45 @@ import sequelize from '../configs/db.config.js'; import User from './user.model.js'; const Invitation = sequelize.define('Invitation', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - inviter_id: { - type: DataTypes.INTEGER, - references: { - model: User, - key: 'id', - }, - }, - invitee_email: { - type: DataTypes.STRING, - allowNull: false, - validate: { - isEmail: true, - }, - }, - isSuperAdmin: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - token: { - type: DataTypes.STRING, - allowNull: false, - }, - status: { - type: DataTypes.ENUM('pending', 'accepted', 'expired'), - defaultValue: 'pending', - }, - expires_at: { - type: DataTypes.DATE, - allowNull: false, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + inviter_id: { + type: DataTypes.INTEGER, + references: { + model: User, + key: 'id', + }, + }, + invitee_email: { + type: DataTypes.STRING, + allowNull: false, + validate: { + isEmail: true, + }, + }, + isSuperAdmin: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + token: { + type: DataTypes.STRING, + allowNull: false, + }, + status: { + type: DataTypes.ENUM('pending', 'accepted', 'expired'), + defaultValue: 'pending', + }, + expires_at: { + type: DataTypes.DATE, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + }, }); Invitation.belongsTo(User, { foreignKey: 'inviter_id', as: 'inviter' }); diff --git a/src/models/maintainer.model.js b/src/models/maintainer.model.js index 47c8eee..feb7e49 100644 --- a/src/models/maintainer.model.js +++ b/src/models/maintainer.model.js @@ -3,39 +3,39 @@ import sequelize from '../configs/db.config.js'; import Project from './project.model.js'; const Maintainer = sequelize.define('Maintainer', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - project_id: { - type: DataTypes.INTEGER, - references: { - model: Project, - key: 'id', + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, }, - }, - fullname: { - type: DataTypes.STRING, - allowNull: false, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - validate: { - isEmail: true, + project_id: { + type: DataTypes.INTEGER, + references: { + model: Project, + key: 'id', + }, + }, + fullname: { + type: DataTypes.STRING, + allowNull: false, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + validate: { + isEmail: true, + }, + }, + linkedin_url: { + type: DataTypes.STRING, + }, + x_url: { + type: DataTypes.STRING, + }, + created_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, }, - }, - linkedin_url: { - type: DataTypes.STRING, - }, - x_url: { - type: DataTypes.STRING, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, }); Maintainer.belongsTo(Project); diff --git a/src/models/project.model.js b/src/models/project.model.js index 6b7ee36..378aba0 100644 --- a/src/models/project.model.js +++ b/src/models/project.model.js @@ -2,68 +2,68 @@ import { DataTypes } from 'sequelize'; import sequelize from '../configs/db.config.js'; const Project = sequelize.define('Project', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - title: { - type: DataTypes.STRING, - allowNull: false, - }, - description: { - type: DataTypes.TEXT, - allowNull: false, - }, - url: { - type: DataTypes.STRING, - allowNull: false, - }, - license_status: { - type: DataTypes.ENUM('licensed', 'not licensed'), - allowNull: false, - }, - license_details: { - type: DataTypes.ENUM( - 'MIT License', - 'Apache License 2.0', - 'BSD License (2-Clause and 3-Clause)', - 'Microsoft Public License (MS-PL)', - 'Zlib License', - 'GNU General Public License (GPL) 2.0 & 3.0', - 'GNU Lesser General Public License (LGPL)', - 'Affero General Public License (AGPL)', - 'Mozilla Public License 2.0 (MPL)', - 'Creative Commons Zero (CC0)', - 'Unlicense', - 'Eclipse Public License (EPL)', - 'Common Development and Distribution License (CDDL)', - 'SIL Open Font License (OFL)', - 'End User License Agreement (EULA)', - 'Proprietary License', - 'Subscription-Based License', - 'Trial or Evaluation License' - ), - }, - status: { - type: DataTypes.ENUM('submitted', 'approved', 'rejected', 'pending'), - defaultValue: 'pending', - }, - category: { - type: DataTypes.STRING, - allowNull: false, - }, - country: { - type: DataTypes.STRING, - allowNull: false, - }, - logo_url: { - type: DataTypes.STRING, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + title: { + type: DataTypes.STRING, + allowNull: false, + }, + description: { + type: DataTypes.TEXT, + allowNull: false, + }, + url: { + type: DataTypes.STRING, + allowNull: false, + }, + license_status: { + type: DataTypes.ENUM('licensed', 'not licensed'), + allowNull: false, + }, + license_details: { + type: DataTypes.ENUM( + 'MIT License', + 'Apache License 2.0', + 'BSD License (2-Clause and 3-Clause)', + 'Microsoft Public License (MS-PL)', + 'Zlib License', + 'GNU General Public License (GPL) 2.0 & 3.0', + 'GNU Lesser General Public License (LGPL)', + 'Affero General Public License (AGPL)', + 'Mozilla Public License 2.0 (MPL)', + 'Creative Commons Zero (CC0)', + 'Unlicense', + 'Eclipse Public License (EPL)', + 'Common Development and Distribution License (CDDL)', + 'SIL Open Font License (OFL)', + 'End User License Agreement (EULA)', + 'Proprietary License', + 'Subscription-Based License', + 'Trial or Evaluation License' + ), + }, + status: { + type: DataTypes.ENUM('submitted', 'approved', 'rejected', 'pending'), + defaultValue: 'pending', + }, + category: { + type: DataTypes.STRING, + allowNull: false, + }, + country: { + type: DataTypes.STRING, + allowNull: false, + }, + logo_url: { + type: DataTypes.STRING, + }, + created_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + }, }); export default Project; diff --git a/src/models/user.model.js b/src/models/user.model.js index 3e5c1a0..0d4633e 100644 --- a/src/models/user.model.js +++ b/src/models/user.model.js @@ -1,40 +1,41 @@ import { DataTypes } from 'sequelize'; +import bcrypt from 'bcryptjs'; import sequelize from '../configs/db.config.js'; const User = sequelize.define('User', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - fullname: { - type: DataTypes.STRING, - allowNull: false, - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - validate: { - isEmail: true, + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + fullname: { + type: DataTypes.STRING, + allowNull: false, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: true, + }, + }, + isSuperAdmin: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + passwordResetToken: { + type: DataTypes.STRING, + allowNull: true, + }, + passwordResetExpires: { + type: DataTypes.DATE, + allowNull: true, + }, + created_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, }, - }, - isSuperAdmin: { - type: DataTypes.BOOLEAN, - defaultValue: false, - }, - passwordResetToken: { - type: DataTypes.STRING, - allowNull: true, - }, - passwordResetExpires: { - type: DataTypes.DATE, - allowNull: true, - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW, - }, }); // Hash password before saving diff --git a/src/repositories/auth.repository.js b/src/repositories/auth.repository.js index 4dd1fda..710faea 100644 --- a/src/repositories/auth.repository.js +++ b/src/repositories/auth.repository.js @@ -1,56 +1,53 @@ import { Op } from 'sequelize'; import { User, Invitation } from '../models/index.js'; -export const findUserByEmail = async (email) => { - return User.findOne({ where: { email } }); +export const findUserByEmail = (email) => { + return User.findOne({ where: { email } }); }; -export const createUser = async (userData) => { - return User.create(userData); +export const createUser = (userData) => { + return User.create(userData); }; -export const findInvitationByToken = async (token) => { - return Invitation.findOne({ - where: { - token, - status: 'pending', - }, - }); +export const findInvitationByToken = (token) => { + return Invitation.findOne({ + where: { + token, + status: 'pending', + }, + }); }; -export const updateInvitationStatus = async (invitation, status) => { - return invitation.update({ status }); +export const updateInvitationStatus = (invitation, status) => { + return invitation.update({ status }); }; export const updateUserPassword = async (userId, newPassword) => { - const user = await User.findByPk(userId); - return user.update({ password: newPassword }); + const user = await User.findByPk(userId); + return user.update({ password: newPassword }); }; export const createPasswordResetToken = async (userId) => { - const resetToken = crypto.randomBytes(32).toString('hex'); - const hashedToken = crypto - .createHash('sha256') - .update(resetToken) - .digest('hex'); - - await User.update( - { - passwordResetToken: hashedToken, - passwordResetExpires: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes - }, - { where: { id: userId } } - ); - - return resetToken; + const resetToken = crypto.randomBytes(32).toString('hex'); + const hashedToken = crypto.createHash('sha256').update(resetToken).digest('hex'); + + await User.update( + { + passwordResetToken: hashedToken, + passwordResetExpires: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes + }, + { where: { id: userId } } + ); + + return resetToken; }; -export const findUserByResetToken = async (token) => { - const hashedToken = crypto.createHash('sha256').update(token).digest('hex'); - return User.findOne({ - where: { - passwordResetToken: hashedToken, - passwordResetExpires: { [Op.gt]: new Date() }, - }, - }); +export const findUserByResetToken = (token) => { + const hashedToken = crypto.createHash('sha256').update(token).digest('hex'); + return User.findOne({ + where: { + passwordResetToken: hashedToken, + passwordResetExpires: { [Op.gt]: new Date() }, + }, + }); }; diff --git a/src/repositories/invitation.repository.js b/src/repositories/invitation.repository.js index ee75332..bfb4085 100644 --- a/src/repositories/invitation.repository.js +++ b/src/repositories/invitation.repository.js @@ -1,52 +1,48 @@ import { Invitation, User } from '../models/index.js'; import crypto from 'crypto'; -export const createInvitation = async ( - inviterId, - inviteeEmail, - isSuperAdmin -) => { - const token = crypto.randomBytes(32).toString('hex'); - const expires_at = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours +export const createInvitation = (inviterId, inviteeEmail, isSuperAdmin) => { + const token = crypto.randomBytes(32).toString('hex'); + const expires_at = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours - return Invitation.create({ - inviter_id: inviterId, - invitee_email: inviteeEmail, - isSuperAdmin, - token, - expires_at, - }); + return Invitation.create({ + inviter_id: inviterId, + invitee_email: inviteeEmail, + isSuperAdmin, + token, + expires_at, + }); }; -export const findPendingInvitation = async (inviteeEmail) => { - return Invitation.findOne({ - where: { - invitee_email: inviteeEmail, - status: 'pending', - }, - }); +export const findPendingInvitation = (inviteeEmail) => { + return Invitation.findOne({ + where: { + invitee_email: inviteeEmail, + status: 'pending', + }, + }); }; -export const findAllInvitations = async () => { - return Invitation.findAll({ - include: [ - { - model: User, - as: 'inviter', - attributes: ['id', 'fullname', 'email'], - }, - ], - }); +export const findAllInvitations = () => { + return Invitation.findAll({ + include: [ + { + model: User, + as: 'inviter', + attributes: ['id', 'fullname', 'email'], + }, + ], + }); }; -export const findInvitationById = async (id) => { - return Invitation.findByPk(id, { - include: [ - { - model: User, - as: 'inviter', - attributes: ['id', 'fullname', 'email'], - }, - ], - }); +export const findInvitationById = (id) => { + return Invitation.findByPk(id, { + include: [ + { + model: User, + as: 'inviter', + attributes: ['id', 'fullname', 'email'], + }, + ], + }); }; diff --git a/src/repositories/user.repository.js b/src/repositories/user.repository.js index 89cfd7d..9828c17 100644 --- a/src/repositories/user.repository.js +++ b/src/repositories/user.repository.js @@ -1,23 +1,23 @@ import { User } from '../models/index.js'; -export const findAllUsers = async () => { - return User.findAll({ - attributes: ['id', 'fullname', 'email', 'isSuperAdmin', 'created_at'], - }); +export const findAllUsers = () => { + return User.findAll({ + attributes: ['id', 'fullname', 'email', 'isSuperAdmin', 'created_at'], + }); }; -export const findUserById = async (id) => { - return User.findByPk(id, { - attributes: ['id', 'fullname', 'email', 'isSuperAdmin', 'created_at'], - }); +export const findUserById = (id) => { + return User.findByPk(id, { + attributes: ['id', 'fullname', 'email', 'isSuperAdmin', 'created_at'], + }); }; export const updateUser = async (id, userData) => { - const user = await User.findByPk(id); - return user.update(userData); + const user = await User.findByPk(id); + return user.update(userData); }; export const deleteUser = async (id) => { - const user = await User.findByPk(id); - return user.destroy(); + const user = await User.findByPk(id); + return user.destroy(); }; diff --git a/src/routes/auth.route.js b/src/routes/auth.route.js index fff41bc..40567dd 100644 --- a/src/routes/auth.route.js +++ b/src/routes/auth.route.js @@ -2,36 +2,29 @@ import express from 'express'; import { authenticate } from '../middlewares/auth.js'; import validate from '../middlewares/validate.js'; import { - loginSchema, - forgotPasswordSchema, - resetPasswordSchema, - changePasswordSchema, + loginSchema, + forgotPasswordSchema, + resetPasswordSchema, + changePasswordSchema, } from '../validations/auth.js'; +import { acceptInvitationSchema } from '../validations/invitation.js'; import * as authController from '../controllers/auth.controller.js'; const authRouter = express.Router(); authRouter.post('/login', validate(loginSchema), authController.login); +authRouter.post('/forgot-password', validate(forgotPasswordSchema), authController.forgotPassword); +authRouter.post('/reset-password', validate(resetPasswordSchema), authController.resetPassword); authRouter.post( - '/forgot-password', - validate(forgotPasswordSchema), - authController.forgotPassword + '/change-password', + authenticate, + validate(changePasswordSchema), + authController.changePassword ); authRouter.post( - '/reset-password', - validate(resetPasswordSchema), - authController.resetPassword -); -authRouter.post( - '/change-password', - authenticate, - validate(changePasswordSchema), - authController.changePassword -); -authRouter.post( - '/accept-invitation', - validate(acceptInvitationSchema), - authController.acceptInvitation + '/accept-invitation', + validate(acceptInvitationSchema), + authController.acceptInvitation ); export default authRouter; diff --git a/src/routes/invitation.route.js b/src/routes/invitation.route.js index f296d2b..ab997d2 100644 --- a/src/routes/invitation.route.js +++ b/src/routes/invitation.route.js @@ -1,8 +1,5 @@ import express from 'express'; -import { - authenticate, - authorizeAdmin, -} from '../middlewares/auth.js'; +import { authenticate, authorizeAdmin } from '../middlewares/auth.js'; import validate from '../middlewares/validate.js'; import { createInvitationSchema } from '../validations/invitation.js'; import * as invitationController from '../controllers/invitation.controller.js'; @@ -10,23 +7,13 @@ import * as invitationController from '../controllers/invitation.controller.js'; const router = express.Router(); router.post( - '/', - authenticate, - authorizeAdmin, - validate(createInvitationSchema), - invitationController.createInvitation -); -router.get( - '/', - authenticate, - authorizeAdmin, - invitationController.getInvitations -); -router.get( - '/:id', - authenticate, - authorizeAdmin, - invitationController.getInvitationById + '/', + authenticate, + authorizeAdmin, + validate(createInvitationSchema), + invitationController.createInvitation ); +router.get('/', authenticate, authorizeAdmin, invitationController.getInvitations); +router.get('/:id', authenticate, authorizeAdmin, invitationController.getInvitationById); export default router; diff --git a/src/routes/user.route.js b/src/routes/user.route.js index d0dc3b1..9b92c36 100644 --- a/src/routes/user.route.js +++ b/src/routes/user.route.js @@ -1,8 +1,5 @@ import express from 'express'; -import { - authenticate, - authorizeAdmin, -} from '../middlewares/auth.js'; +import { authenticate, authorizeAdmin } from '../middlewares/auth.js'; import validate from '../middlewares/validate.js'; import { updateUserSchema } from '../validations/user.js'; import * as userController from '../controllers/user.controller.js'; @@ -12,11 +9,11 @@ const router = express.Router(); router.get('/', authenticate, authorizeAdmin, userController.getUsers); router.get('/:id', authenticate, authorizeAdmin, userController.getUserById); router.put( - '/:id', - authenticate, - authorizeAdmin, - validate(updateUserSchema), - userController.updateUser + '/:id', + authenticate, + authorizeAdmin, + validate(updateUserSchema), + userController.updateUser ); router.delete('/:id', authenticate, authorizeAdmin, userController.deleteUser); diff --git a/src/server.js b/src/server.js index e52aef6..b50803d 100644 --- a/src/server.js +++ b/src/server.js @@ -3,20 +3,20 @@ import logger from './configs/logger.config.js'; const PORT = process.env.PORT || 3000; const server = app.listen(PORT, () => { - logger.info(`Server is running on port ${PORT}`); + logger.info(`Server is running on port ${PORT}`); }); function gracefulShutdown(signal) { - logger.info(`Received ${signal}. Shutting down gracefully...`); - server.close(async () => { - try { - // Perform any necessary cleanup or garbage collection here - process.exit(0); - } catch (error) { - logger.error('Error during graceful shutdown:', error); - process.exit(1); - } - }); + logger.info(`Received ${signal}. Shutting down gracefully...`); + server.close(() => { + try { + // Perform any necessary cleanup or garbage collection here + process.exit(0); + } catch (error) { + logger.error('Error during graceful shutdown:', error); + process.exit(1); + } + }); } process.on('SIGINT', gracefulShutdown); diff --git a/src/services/auth.service.js b/src/services/auth.service.js index c136c67..4889290 100644 --- a/src/services/auth.service.js +++ b/src/services/auth.service.js @@ -3,105 +3,104 @@ import { generateToken } from '../configs/jwt.config.js'; import * as authRepository from '../repositories/auth.repository.js'; export const loginService = async (email, password) => { - const user = await authRepository.findUserByEmail(email); - if (!user) { - throw new Error('Invalid credentials'); - } - - const isValidPassword = await bcrypt.compare(password, user.password); - if (!isValidPassword) { - throw new Error('Invalid credentials'); - } - - const token = generateToken(user); - - return { - user: { - id: user.id, - fullname: user.fullname, - email: user.email, - isSuperAdmin: user.isSuperAdmin, - }, - token, - }; + const user = await authRepository.findUserByEmail(email); + if (!user) { + throw new Error('Invalid credentials'); + } + + const isValidPassword = await bcrypt.compare(password, user.password); + if (!isValidPassword) { + throw new Error('Invalid credentials'); + } + + const token = generateToken(user); + + return { + user: { + id: user.id, + fullname: user.fullname, + email: user.email, + isSuperAdmin: user.isSuperAdmin, + }, + token, + }; }; export const acceptInvitationService = async (token, userData) => { - const invitation = await authRepository.findInvitationByToken(token); - - if (!invitation || invitation.expires_at < new Date()) { - await invitation?.update({ status: 'expired' }); - throw new Error('Invalid or expired invitation'); - } - - const user = await authRepository.createUser({ - ...userData, - email: invitation.invitee_email, - isSuperAdmin: invitation.isSuperAdmin, - }); - - await authRepository.updateInvitationStatus(invitation, 'accepted'); - - const authToken = generateToken(user); - - return { - user: { - id: user.id, - fullname: user.fullname, - email: user.email, - isSuperAdmin: user.isSuperAdmin, - }, - token: authToken, - }; + const invitation = await authRepository.findInvitationByToken(token); + + if (!invitation || invitation.expires_at < new Date()) { + await invitation?.update({ status: 'expired' }); + throw new Error('Invalid or expired invitation'); + } + + const user = await authRepository.createUser({ + ...userData, + email: invitation.invitee_email, + isSuperAdmin: invitation.isSuperAdmin, + }); + + await authRepository.updateInvitationStatus(invitation, 'accepted'); + + const authToken = generateToken(user); + + return { + user: { + id: user.id, + fullname: user.fullname, + email: user.email, + isSuperAdmin: user.isSuperAdmin, + }, + token: authToken, + }; }; export const changePasswordService = async (userId, currentPassword, newPassword) => { - const user = await authRepository.findUserByPk(userId); + const user = await authRepository.findUserByPk(userId); - const isValidPassword = await bcrypt.compare(currentPassword, user.password); - if (!isValidPassword) { - throw new Error('Current password is incorrect'); - } + const isValidPassword = await bcrypt.compare(currentPassword, user.password); + if (!isValidPassword) { + throw new Error('Current password is incorrect'); + } - const salt = await bcrypt.genSalt(10); - const hashedPassword = await bcrypt.hash(newPassword, salt); + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(newPassword, salt); - await authRepository.updateUserPassword(userId, hashedPassword); + await authRepository.updateUserPassword(userId, hashedPassword); - return { message: 'Password updated successfully' }; + return { message: 'Password updated successfully' }; }; export const forgotPasswordService = async (email) => { - const user = await authRepository.findUserByEmail(email); - if (!user) { - throw new Error('No user found with this email'); - } + const user = await authRepository.findUserByEmail(email); + if (!user) { + throw new Error('No user found with this email'); + } - const resetToken = await authRepository.createPasswordResetToken(user.id); + const resetToken = await authRepository.createPasswordResetToken(user.id); - return { - message: 'Password reset token generated', - resetToken, // This should be sent via email in production - }; + return { + message: 'Password reset token generated', + resetToken, // This should be sent via email in production + }; }; export const resetPasswordService = async (token, newPassword) => { - const user = await authRepository.findUserByResetToken(token); - if (!user) { - throw new Error('Invalid or expired reset token'); - } + const user = await authRepository.findUserByResetToken(token); + if (!user) { + throw new Error('Invalid or expired reset token'); + } - const salt = await bcrypt.genSalt(10); - const hashedPassword = await bcrypt.hash(newPassword, salt); + const salt = await bcrypt.genSalt(10); + const hashedPassword = await bcrypt.hash(newPassword, salt); - await authRepository.updateUserPassword(user.id, hashedPassword); + await authRepository.updateUserPassword(user.id, hashedPassword); - // Clear reset token fields - await user.update({ - passwordResetToken: null, - passwordResetExpires: null, - }); + // Clear reset token fields + await user.update({ + passwordResetToken: null, + passwordResetExpires: null, + }); - return { message: 'Password has been reset successfully' }; + return { message: 'Password has been reset successfully' }; }; - diff --git a/src/services/invitation.service.js b/src/services/invitation.service.js index 57c17e7..3be4c51 100644 --- a/src/services/invitation.service.js +++ b/src/services/invitation.service.js @@ -1,41 +1,35 @@ import * as invitationRepository from '../repositories/invitation.repository.js'; -export const createInvitationService = async ( - inviterId, - inviteeEmail, - isSuperAdmin -) => { - const existingInvitation = await invitationRepository.findPendingInvitation( - inviteeEmail - ); +export const createInvitationService = async (inviterId, inviteeEmail, isSuperAdmin) => { + const existingInvitation = await invitationRepository.findPendingInvitation(inviteeEmail); - if (existingInvitation) { - throw new Error('Pending invitation already exists for this email'); - } + if (existingInvitation) { + throw new Error('Pending invitation already exists for this email'); + } - const invitation = await invitationRepository.createInvitation( - inviterId, - inviteeEmail, - isSuperAdmin - ); + const invitation = await invitationRepository.createInvitation( + inviterId, + inviteeEmail, + isSuperAdmin + ); - return { - id: invitation.id, - inviteeEmail: invitation.invitee_email, - expiresAt: invitation.expires_at, - token: invitation.token, - }; + return { + id: invitation.id, + inviteeEmail: invitation.invitee_email, + expiresAt: invitation.expires_at, + token: invitation.token, + }; }; export const getInvitationsService = async () => { - const invitations = await invitationRepository.findAllInvitations(); - return invitations; + const invitations = await invitationRepository.findAllInvitations(); + return invitations; }; export const getInvitationByIdService = async (id) => { - const invitation = await invitationRepository.findInvitationById(id); - if (!invitation) { - throw new Error('Invitation not found'); - } - return invitation; + const invitation = await invitationRepository.findInvitationById(id); + if (!invitation) { + throw new Error('Invitation not found'); + } + return invitation; }; diff --git a/src/services/user.service.js b/src/services/user.service.js index bd5d3c9..1d78273 100644 --- a/src/services/user.service.js +++ b/src/services/user.service.js @@ -1,39 +1,39 @@ import * as userRepository from '../repositories/user.repository.js'; export const getUsersService = async () => { - const users = await userRepository.findAllUsers(); - return users; + const users = await userRepository.findAllUsers(); + return users; }; export const getUserByIdService = async (id) => { - const user = await userRepository.findUserById(id); - if (!user) { - throw new Error('User not found'); - } - return user; + const user = await userRepository.findUserById(id); + if (!user) { + throw new Error('User not found'); + } + return user; }; export const updateUserService = async (id, userData) => { - const user = await userRepository.findUserById(id); - if (!user) { - throw new Error('User not found'); - } + const user = await userRepository.findUserById(id); + if (!user) { + throw new Error('User not found'); + } - const updatedUser = await userRepository.updateUser(id, userData); - return { - id: updatedUser.id, - fullname: updatedUser.fullname, - email: updatedUser.email, - isSuperAdmin: updatedUser.isSuperAdmin, - }; + const updatedUser = await userRepository.updateUser(id, userData); + return { + id: updatedUser.id, + fullname: updatedUser.fullname, + email: updatedUser.email, + isSuperAdmin: updatedUser.isSuperAdmin, + }; }; export const deleteUserService = async (id) => { - const user = await userRepository.findUserById(id); - if (!user) { - throw new Error('User not found'); - } + const user = await userRepository.findUserById(id); + if (!user) { + throw new Error('User not found'); + } - await userRepository.deleteUser(id); - return { message: 'User deleted successfully' }; + await userRepository.deleteUser(id); + return { message: 'User deleted successfully' }; }; diff --git a/src/validations/achievement.js b/src/validations/achievement.js index f503351..c57143a 100644 --- a/src/validations/achievement.js +++ b/src/validations/achievement.js @@ -1,15 +1,15 @@ import { z } from 'zod'; export const createAchievementSchema = z.object({ - body: z.object({ - project_id: z.number().int().positive(), - item: z.string().min(3), - }), + body: z.object({ + project_id: z.number().int().positive(), + item: z.string().min(3), + }), }); export const updateAchievementSchema = z.object({ - body: z.object({ - project_id: z.number().int().positive().optional(), - item: z.string().min(3).optional(), - }), + body: z.object({ + project_id: z.number().int().positive().optional(), + item: z.string().min(3).optional(), + }), }); diff --git a/src/validations/auth.js b/src/validations/auth.js index 0aa0f96..b88f9bc 100644 --- a/src/validations/auth.js +++ b/src/validations/auth.js @@ -1,44 +1,44 @@ import { z } from 'zod'; export const loginSchema = z.object({ - body: z.object({ - email: z.string().email(), - password: z.string().min(6), - }), + body: z.object({ + email: z.string().email(), + password: z.string().min(6), + }), }); export const forgotPasswordSchema = z.object({ - body: z.object({ - email: z.string().email(), - }), + body: z.object({ + email: z.string().email(), + }), }); export const resetPasswordSchema = z.object({ - body: z - .object({ - token: z.string(), - password: z.string().min(6), - confirmPassword: z.string().min(6), - }) - .refine((data) => data.password === data.confirmPassword, { - message: "Passwords don't match", - path: ['confirmPassword'], - }), + body: z + .object({ + token: z.string(), + password: z.string().min(6), + confirmPassword: z.string().min(6), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ['confirmPassword'], + }), }); export const changePasswordSchema = z.object({ - body: z - .object({ - currentPassword: z.string().min(6), - newPassword: z.string().min(6), - confirmNewPassword: z.string().min(6), - }) - .refine((data) => data.newPassword === data.confirmNewPassword, { - message: "New passwords don't match", - path: ['confirmNewPassword'], - }) - .refine((data) => data.currentPassword !== data.newPassword, { - message: 'New password must be different from current password', - path: ['newPassword'], - }), + body: z + .object({ + currentPassword: z.string().min(6), + newPassword: z.string().min(6), + confirmNewPassword: z.string().min(6), + }) + .refine((data) => data.newPassword === data.confirmNewPassword, { + message: "New passwords don't match", + path: ['confirmNewPassword'], + }) + .refine((data) => data.currentPassword !== data.newPassword, { + message: 'New password must be different from current password', + path: ['newPassword'], + }), }); diff --git a/src/validations/index.js b/src/validations/index.js index 3f934e1..81ead98 100644 --- a/src/validations/index.js +++ b/src/validations/index.js @@ -1,37 +1,28 @@ -import { - createAchievementSchema, - updateAchievementSchema, -} from './achievement.js'; -import { - updateMaintainerSchema, - createMaintainerSchema, -} from './maintainer.js'; +import { createAchievementSchema, updateAchievementSchema } from './achievement.js'; +import { updateMaintainerSchema, createMaintainerSchema } from './maintainer.js'; import { createProjectSchema, updateProjectSchema } from './project.js'; import { createUserSchema, updateUserSchema } from './user.js'; import { - loginSchema, - forgotPasswordSchema, - resetPasswordSchema, - changePasswordSchema, + loginSchema, + forgotPasswordSchema, + resetPasswordSchema, + changePasswordSchema, } from './auth.js'; -import { - createInvitationSchema, - acceptInvitationSchema, -} from './invitation.js'; +import { createInvitationSchema, acceptInvitationSchema } from './invitation.js'; export { - createAchievementSchema, - updateAchievementSchema, - createMaintainerSchema, - updateMaintainerSchema, - createProjectSchema, - updateProjectSchema, - createUserSchema, - updateUserSchema, - loginSchema, - forgotPasswordSchema, - resetPasswordSchema, - changePasswordSchema, - createInvitationSchema, - acceptInvitationSchema, + createAchievementSchema, + updateAchievementSchema, + createMaintainerSchema, + updateMaintainerSchema, + createProjectSchema, + updateProjectSchema, + createUserSchema, + updateUserSchema, + loginSchema, + forgotPasswordSchema, + resetPasswordSchema, + changePasswordSchema, + createInvitationSchema, + acceptInvitationSchema, }; diff --git a/src/validations/invitation.js b/src/validations/invitation.js index 3206259..fc2a406 100644 --- a/src/validations/invitation.js +++ b/src/validations/invitation.js @@ -1,22 +1,22 @@ import { z } from 'zod'; export const createInvitationSchema = z.object({ - body: z.object({ - invitee_email: z.string().email(), - isSuperAdmin: z.boolean().optional(), - }), + body: z.object({ + invitee_email: z.string().email(), + isSuperAdmin: z.boolean().optional(), + }), }); export const acceptInvitationSchema = z.object({ - body: z - .object({ - token: z.string(), - fullname: z.string().min(3).max(255), - password: z.string().min(6), - confirmPassword: z.string().min(6), - }) - .refine((data) => data.password === data.confirmPassword, { - message: "Passwords don't match", - path: ['confirmPassword'], - }), + body: z + .object({ + token: z.string(), + fullname: z.string().min(3).max(255), + password: z.string().min(6), + confirmPassword: z.string().min(6), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ['confirmPassword'], + }), }); diff --git a/src/validations/maintainer.js b/src/validations/maintainer.js index c0032ee..8ddabbc 100644 --- a/src/validations/maintainer.js +++ b/src/validations/maintainer.js @@ -1,21 +1,21 @@ import { z } from 'zod'; export const createMaintainerSchema = z.object({ - body: z.object({ - project_id: z.number().int().positive(), - fullname: z.string().min(3).max(255), - email: z.string().email(), - linkedin_url: z.string().url().optional(), - x_url: z.string().url().optional(), - }), + body: z.object({ + project_id: z.number().int().positive(), + fullname: z.string().min(3).max(255), + email: z.string().email(), + linkedin_url: z.string().url().optional(), + x_url: z.string().url().optional(), + }), }); export const updateMaintainerSchema = z.object({ - body: z.object({ - project_id: z.number().int().positive().optional(), - fullname: z.string().min(3).max(255).optional(), - email: z.string().email().optional(), - linkedin_url: z.string().url().optional(), - x_url: z.string().url().optional(), - }), -}); \ No newline at end of file + body: z.object({ + project_id: z.number().int().positive().optional(), + fullname: z.string().min(3).max(255).optional(), + email: z.string().email().optional(), + linkedin_url: z.string().url().optional(), + x_url: z.string().url().optional(), + }), +}); diff --git a/src/validations/project.js b/src/validations/project.js index 2700de3..b4719b7 100644 --- a/src/validations/project.js +++ b/src/validations/project.js @@ -1,99 +1,99 @@ import { z } from 'zod'; export const createProjectSchema = z.object({ - body: z.object({ - title: z.string().min(3).max(255), - description: z.string().min(10), - url: z.string().url(), - license_status: z.enum(['licensed', 'not licensed']), - license_details: z.enum([ - 'MIT License', - 'Apache License 2.0', - 'BSD License (2-Clause and 3-Clause)', - 'Microsoft Public License (MS-PL)', - 'Zlib License', - 'GNU General Public License (GPL) 2.0 & 3.0', - 'GNU Lesser General Public License (LGPL)', - 'Affero General Public License (AGPL)', - 'Mozilla Public License 2.0 (MPL)', - 'Creative Commons Zero (CC0)', - 'Unlicense', - 'Eclipse Public License (EPL)', - 'Common Development and Distribution License (CDDL)', - 'SIL Open Font License (OFL)', - 'End User License Agreement (EULA)', - 'Proprietary License', - 'Subscription-Based License', - 'Trial or Evaluation License', - ]), - category: z.string(), - country: z.string(), - logo_url: z.string().url().optional(), - maintainers: z.array( - z.object({ - fullname: z.string(), - email: z.string().email(), - linkedin_url: z.string().url().optional(), - x_url: z.string().url().optional(), - }) - ), - achievements: z - .array( - z.object({ - item: z.string(), - }) - ) - .optional(), - }), + body: z.object({ + title: z.string().min(3).max(255), + description: z.string().min(10), + url: z.string().url(), + license_status: z.enum(['licensed', 'not licensed']), + license_details: z.enum([ + 'MIT License', + 'Apache License 2.0', + 'BSD License (2-Clause and 3-Clause)', + 'Microsoft Public License (MS-PL)', + 'Zlib License', + 'GNU General Public License (GPL) 2.0 & 3.0', + 'GNU Lesser General Public License (LGPL)', + 'Affero General Public License (AGPL)', + 'Mozilla Public License 2.0 (MPL)', + 'Creative Commons Zero (CC0)', + 'Unlicense', + 'Eclipse Public License (EPL)', + 'Common Development and Distribution License (CDDL)', + 'SIL Open Font License (OFL)', + 'End User License Agreement (EULA)', + 'Proprietary License', + 'Subscription-Based License', + 'Trial or Evaluation License', + ]), + category: z.string(), + country: z.string(), + logo_url: z.string().url().optional(), + maintainers: z.array( + z.object({ + fullname: z.string(), + email: z.string().email(), + linkedin_url: z.string().url().optional(), + x_url: z.string().url().optional(), + }) + ), + achievements: z + .array( + z.object({ + item: z.string(), + }) + ) + .optional(), + }), }); export const updateProjectSchema = z.object({ - body: z.object({ - title: z.string().min(3).max(255).optional(), - description: z.string().min(10).optional(), - url: z.string().url().optional(), - license_status: z.enum(['licensed', 'not licensed']).optional(), - license_details: z - .enum([ - 'MIT License', - 'Apache License 2.0', - 'BSD License (2-Clause and 3-Clause)', - 'Microsoft Public License (MS-PL)', - 'Zlib License', - 'GNU General Public License (GPL) 2.0 & 3.0', - 'GNU Lesser General Public License (LGPL)', - 'Affero General Public License (AGPL)', - 'Mozilla Public License 2.0 (MPL)', - 'Creative Commons Zero (CC0)', - 'Unlicense', - 'Eclipse Public License (EPL)', - 'Common Development and Distribution License (CDDL)', - 'SIL Open Font License (OFL)', - 'End User License Agreement (EULA)', - 'Proprietary License', - 'Subscription-Based License', - 'Trial or Evaluation License', - ]) - .optional(), - category: z.string().optional(), - country: z.string().optional(), - logo_url: z.string().url().optional(), - maintainers: z - .array( - z.object({ - fullname: z.string(), - email: z.string().email(), - linkedin_url: z.string().url().optional(), - x_url: z.string().url().optional(), - }) - ) - .optional(), - achievements: z - .array( - z.object({ - item: z.string(), - }) - ) - .optional(), - }), + body: z.object({ + title: z.string().min(3).max(255).optional(), + description: z.string().min(10).optional(), + url: z.string().url().optional(), + license_status: z.enum(['licensed', 'not licensed']).optional(), + license_details: z + .enum([ + 'MIT License', + 'Apache License 2.0', + 'BSD License (2-Clause and 3-Clause)', + 'Microsoft Public License (MS-PL)', + 'Zlib License', + 'GNU General Public License (GPL) 2.0 & 3.0', + 'GNU Lesser General Public License (LGPL)', + 'Affero General Public License (AGPL)', + 'Mozilla Public License 2.0 (MPL)', + 'Creative Commons Zero (CC0)', + 'Unlicense', + 'Eclipse Public License (EPL)', + 'Common Development and Distribution License (CDDL)', + 'SIL Open Font License (OFL)', + 'End User License Agreement (EULA)', + 'Proprietary License', + 'Subscription-Based License', + 'Trial or Evaluation License', + ]) + .optional(), + category: z.string().optional(), + country: z.string().optional(), + logo_url: z.string().url().optional(), + maintainers: z + .array( + z.object({ + fullname: z.string(), + email: z.string().email(), + linkedin_url: z.string().url().optional(), + x_url: z.string().url().optional(), + }) + ) + .optional(), + achievements: z + .array( + z.object({ + item: z.string(), + }) + ) + .optional(), + }), }); diff --git a/src/validations/user.js b/src/validations/user.js index eb58258..b0fbcf0 100644 --- a/src/validations/user.js +++ b/src/validations/user.js @@ -1,17 +1,17 @@ import { z } from 'zod'; export const createUserSchema = z.object({ - body: z.object({ - fullname: z.string().min(3).max(255), - email: z.string().email(), - isSuperAdmin: z.boolean().optional(), - }), + body: z.object({ + fullname: z.string().min(3).max(255), + email: z.string().email(), + isSuperAdmin: z.boolean().optional(), + }), }); export const updateUserSchema = z.object({ - body: z.object({ - fullname: z.string().min(3).max(255).optional(), - email: z.string().email().optional(), - isSuperAdmin: z.boolean().optional(), - }), + body: z.object({ + fullname: z.string().min(3).max(255).optional(), + email: z.string().email().optional(), + isSuperAdmin: z.boolean().optional(), + }), });