diff --git a/.gitignore b/.gitignore index 7d999646db..14c3149e6f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,8 @@ __pycache__/ *.log -*.swp \ No newline at end of file +*.swp + +# Dependency directories +node_modules/ +**/node_modules/ \ No newline at end of file diff --git a/backend/.editorconfig b/backend/.editorconfig new file mode 100644 index 0000000000..5c7d7001e8 --- /dev/null +++ b/backend/.editorconfig @@ -0,0 +1,9 @@ +#Configuracoes para defini os parametros do editor de texto, Ex tamanho do espacamento dentre outros. +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = \ No newline at end of file diff --git a/backend/.env.development b/backend/.env.development new file mode 100644 index 0000000000..e4a03913ed --- /dev/null +++ b/backend/.env.development @@ -0,0 +1,2 @@ +ENV=DEV +PORT=5001 diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000000..79d098753b --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,2 @@ +ENV=DEV +PORT=5001 \ No newline at end of file diff --git a/backend/.eslintrc b/backend/.eslintrc new file mode 100644 index 0000000000..986cd80749 --- /dev/null +++ b/backend/.eslintrc @@ -0,0 +1,10 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["no-unused-expressions"], + "rules": { + "no-unused-expressions": [ + "warn", + { "allowShortCircuit": true, "allowTernary": true } + ] + } +} diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000000..229a840a75 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,38 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +coverage/ + +# Dependency directories +node_modules/ +**/node_modules/ + + +# Optional npm cache directory +.npm + +# TypeScript cache +*.tsbuildinfo + +# System files +.DS_Store +Thumbs.db + +# Editor files +.vscode/ + +# Build files +dist/ +lib/ + +# Env +.env +.env.test +.env.dev +.env.prod + +# Deploy +.vercel diff --git a/backend/.husky/pre-commit b/backend/.husky/pre-commit new file mode 100755 index 0000000000..0a86f55779 --- /dev/null +++ b/backend/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run lint diff --git a/backend/.husky/pre-push b/backend/.husky/pre-push new file mode 100755 index 0000000000..9c7ed53f5b --- /dev/null +++ b/backend/.husky/pre-push @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run test \ No newline at end of file diff --git a/backend/.prettierrc b/backend/.prettierrc new file mode 100644 index 0000000000..1502887d63 --- /dev/null +++ b/backend/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "es5" +} \ No newline at end of file diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000000..5b4edcf871 --- /dev/null +++ b/backend/README.md @@ -0,0 +1,80 @@ +# ESS Back-end Node.js + +This is the Back-end base project in Node.js for the Software and Systems Engineering discipline, offered by the Informatics Center (CIn) of the Federal University of Pernambuco (UFPE). + +## Table of Contents + +1. [Getting Started](##getting-started) +2. [Scripts](#scripts) +3. [Dependencies](#dependencies) +4. [Architecture](#architecture) + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. + +### Prerequisites + +To run this project, you'll need to have the following software installed on your system: + +- Node.js +- npm (Node Package Manager) + +### Installing + +Clone the repository and install the dependencies by running the following command in the project directory: + +``` +npm install +``` + +### First time running ? + +Run the follow scripts + +``` +chmod +x .husky/pre-commit +chmod +x .husky/pre-push +``` + +``` +npm run +``` + +### Environment + +This project uses `.env` files to manage environment variables. You can create a `.env.dev` file in the project directory and set the environment variables in the file (iou can create it from .`env.example`). The `env` script in the `package.json` file uses the `env-cmd` package to load the environment variables from the `.env.dev` file. + +### Running the Server + +To start the server, run the following command: + +``` +env=dev npm run start +``` + +This command will run the TypeScript compiler in watch mode and start the server using nodemon. + +## Scripts + +The following scripts are available in the `package.json` file: + +- `start`: Runs the TypeScript compiler in watch mode and starts the server using nodemon. +- `build`: Compiles the TypeScript code. +- `test`: Runs the Jest tests for the project. +- `prettier`: Formats the code using Prettier. +- `lint`: Lints the code using ESLint. + +## Dependencies + +The following dependencies are used in the project: + +- [env-cmd](https://github.com/toddbluhm/env-cmd): A simple way to manage your environment variables in npm scripts. +- [express](https://github.com/expressjs/express): Fast, unopinionated, minimalist web framework for Node.js. +- [typescript](https://github.com/microsoft/TypeScript): A typed superset of JavaScript that compiles to plain JavaScript. +- [jest](https://github.com/microsoft/TypeScript): Jest is a delightful JavaScript Testing Framework with a focus on simplicity. +- [pino](https://github.com/pinojs/pino): Very low overhead Node.js logger. + +## Architecture + +To understand and learn more details about the structure of the project, click [here](./docs/architecture-pattern.md) to be redirected to the README that contains this information. diff --git a/backend/babel.config.js b/backend/babel.config.js new file mode 100644 index 0000000000..c16b7c97f9 --- /dev/null +++ b/backend/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: ['@babel/preset-env'], + }; + \ No newline at end of file diff --git a/backend/database.sqlite b/backend/database.sqlite new file mode 100644 index 0000000000..980c9fed3a Binary files /dev/null and b/backend/database.sqlite differ diff --git a/backend/docs/architecture-pattern.md b/backend/docs/architecture-pattern.md new file mode 100644 index 0000000000..970f4b378d --- /dev/null +++ b/backend/docs/architecture-pattern.md @@ -0,0 +1,82 @@ +# Architecture Pattern + +The base project was created to give a quick "start" to the discipline's team projects. But, at the same time, we didn't want to give up a certain level of organization that helps to develop features safely and makes it easier to create tests. + +## Table of Contents + +1. [Layered architecture](#layered-architecture) +2. [What is the Model for?](#what-is-the-model-for) +3. [Dependency Injection](#dependency-injection) +4. [Database](#database) +5. [Routes](#routes) +6. [Conclusion](#conclusion) + +--- + +# Layered architecture + +Layered architecture is a software design approach where the software is separated into different layers (functional areas), each with a specific role and responsibility. In a typical layered architecture, you will find the following layers: + +## Entities Layer + +This layer represents the business data and the rules or logic that govern access to and updates of this data. These are typically the business objects (like Users, Products, Orders, etc.) and are independent of any specific technological choice (database, external services, etc.). They just contain data and methods to manipulate this data. + +## Repository Layer + +This layer acts as a bridge between the entities (business data) and the database. It is responsible for all database operations like create, read, update, and delete (CRUD). It basically interacts with your database or any other storage system. + +## Service Layer + +This layer contains the business logic of your application. It uses the repository layer to persist or retrieve the business data, performs necessary processing and passes the data to the controller. The service layer acts as an intermediary between the web layer (controllers) and the data access layer (repositories). + +## Controller Layer + +This layer is responsible for handling user requests and controlling the flow of the application. It interacts with the service layer to perform business operations and sends back the responses to the client. In a web application, these are typically the endpoints of your API. + +The layered architecture approach helps in separating concerns, making the software solution scalable, and also promotes high cohesion and low coupling. This design pattern is beneficial because it aligns with the Single Responsibility Principle (SRP), which is a key aspect of SOLID principles in software design and architecture. Each layer has a specific role and does not need to concern itself with the responsibilities of any other layer. This makes each layer independently modifiable and testable, leading to a software design that is easier to maintain and expand. + +--- + +# What is the Model for? + +Models here are not absolutely essential, but are primarily used as a final representation of returned data. Unlike entities, which we can think of as a direct representation of a database table in code, we use models to represent what we could return in an endpoint. +For example, when retrieving a user from the database, we might not want to return its password. In this case, the model could represent a user excluding the password field. + +But we can follow for example the idea of [​​DTOs](https://www.okta.com/identity-101/dto/), which would also be a good idea. +Feel free to explore alternatives for representing these data operations. + +# Dependency Injection + +Is a design pattern that allows a system to be more flexible, testable, and modular. It's a form of Inversion of Control (IoC) which means that the control is inverted - instead of an object controlling its own dependencies, it's controlled by an outside party. + +In our case, we came up with a "homemade" solution that, overall, works very well. We use the Injector class, which is in charge of controlling our application's dependencies through its methods: registerService, getService, registerRepository and getRepository. The use of this class can be seen in the index.ts file, located in the **src/di folder**. + +# Database + +The idea of ​​a base project may seem complex when it proposes to cover many possibilities of use. Considering a practical and simple case, in this base project we are using the idea of ​​a "database" at runtime. In the **src/database/index.ts** file, we can observe the implementation of a simple interface for a database, where each "table" would be a set of key-values. Please don't see this as a limiter. If you prefer, you can choose to use a real database, be it SQL or NoSQL. By maintaining a similar level of project organization, the benefits will be the same. + +To manipulate our database with base methods, we have a class called BaseRepository. This will be extended by other repositories, thus allowing the inheritance of these methods and the reuse of our code's logic. + +# Routes + +Worth mentioning is our backend routes/endpoints definition file located at **src/routes/index.ts**. Its idea is simple: for each new controller, you can configure it inside 'export default' using **'app.use'**. + +``` +export default (app: Express) => { + app.use( + prefix, + new TestController(router, di.getService(TestService)).router + ); + app.use( + prefix, + new ExampleController(router, di.getService(ExampleService)).router + ); +}; + +``` + +There is already an example in the file and the approach would be the same for all controllers. Thus, the controllers and their endpoints are registered and recognized in the application. + +# Conclusion + +Certainly, there are more details about the functionalities that are used in the project. However, the **Test** example case already covers many aspects, including testing. The base project is designed to make it easy, not to limit your preferences. What needs to be emphasized is that the main idea is the architecture used, which clearly distributes code responsibilities, thus facilitating maintenance and testing. diff --git a/backend/jest.config.cjs b/backend/jest.config.cjs new file mode 100644 index 0000000000..42e2f0ea1a --- /dev/null +++ b/backend/jest.config.cjs @@ -0,0 +1,19 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + moduleFileExtensions: ['js', 'json'], + rootDir: '', + testRegex: ['.*\.step\.js$'], + transform: { + '^.+\\.(tsx?|jsx?)$': 'ts-jest', + '^.+\\.ts$': 'ts-jest', + '^.+\\.js$': 'ts-jest', + '^.+\\.tsx?$': 'ts-jest', // Para arquivos TypeScript + '^.+\\.jsx?$': 'babel-jest', // Para arquivos JavaScript + }, + setupFilesAfterEnv: [], + transformIgnorePatterns: ['/node_modules/'], + moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'], + testEnvironment: 'node', + extensionsToTreatAsEsm: ['.ts', '.tsx'] +}; \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000000..83e8befae7 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,8196 @@ +{ + "name": "backend-nodejs-ess", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend-nodejs-ess", + "version": "0.1.0", + "license": "ISC", + "dependencies": { + "bcryptjs": "^3.0.0", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "env-cmd": "^10.1.0", + "expres": "^0.0.5", + "express": "^4.21.2", + "express-async-errors": "^3.1.1", + "jsonwebtoken": "^9.0.2", + "pino": "^8.10.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@babel/core": "^7.26.10", + "@babel/preset-env": "^7.26.9", + "@types/babel__core": "^7.20.5", + "@types/babel__traverse": "^7.20.6", + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "@types/jest": "^29.5.0", + "@types/node": "^20.17.19", + "@types/prettier": "^2.7.2", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.0", + "@typescript-eslint/parser": "^5.51.0", + "babel-jest": "^29.7.0", + "eslint-plugin-no-unused-expressions": "^0.1.0", + "jest": "^29.7.0", + "jest-cucumber": "^4.5.0", + "nodemon": "^3.1.9", + "prettier": "^2.8.4", + "supertest": "^6.3.4", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.2", + "typescript": "^5.7.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "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, + "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.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", + "@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-annotate-as-pure": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@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/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/helper-create-class-features-plugin": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/traverse": "^7.26.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", + "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", + "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "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, + "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-optimise-call-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", + "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", + "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-wrap-function": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz", + "integrity": "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.25.9", + "@babel/helper-optimise-call-expression": "^7.25.9", + "@babel/traverse": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", + "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.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, + "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, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", + "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", + "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", + "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", + "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", + "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", + "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", + "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", + "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.26.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", + "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", + "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", + "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", + "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", + "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9", + "@babel/traverse": "^7.25.9", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", + "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/template": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", + "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", + "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", + "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", + "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", + "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", + "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", + "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", + "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", + "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", + "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", + "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", + "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", + "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", + "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^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-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", + "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", + "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", + "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.26.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", + "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", + "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", + "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", + "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-replace-supers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", + "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", + "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", + "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", + "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", + "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.9", + "@babel/helper-create-class-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", + "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", + "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", + "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", + "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", + "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", + "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", + "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", + "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.26.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", + "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", + "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", + "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", + "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", + "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-plugin-utils": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.26.8", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.26.5", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.26.3", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.26.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.26.3", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.26.8", + "@babel/plugin-transform-typeof-symbol": "^7.26.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.11.0", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.40.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@cucumber/gherkin": { + "version": "28.0.0", + "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-28.0.0.tgz", + "integrity": "sha512-Ee6zJQq0OmIUPdW0mSnsCsrWA2PZAELNDPICD2pLfs0Oz7RAPgj80UsD2UCtqyAhw2qAR62aqlktKUlai5zl/A==", + "dev": true, + "dependencies": { + "@cucumber/messages": ">=19.1.4 <=24" + } + }, + "node_modules/@cucumber/messages": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-24.1.0.tgz", + "integrity": "sha512-hxVHiBurORcobhVk80I9+JkaKaNXkW6YwGOEFIh/2aO+apAN+5XJgUUWjng9NwqaQrW1sCFuawLB1AuzmBaNdQ==", + "dev": true, + "dependencies": { + "@types/uuid": "9.0.8", + "class-transformer": "0.5.1", + "reflect-metadata": "0.2.1", + "uuid": "9.0.1" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true, + "peer": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "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.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": 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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": 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, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.17", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", + "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.35", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", + "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", + "integrity": "sha512-LEwC7o1ifqg/6r2gn9Dns0f1rhK+fPFDoMiceTJ6kWmVk6bgXBI/9IOWfVan4WiAavK9pIVWdX0/e3J+eEUh5A==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", + "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.2.tgz", + "integrity": "sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/superagent": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.18.tgz", + "integrity": "sha512-LOWgpacIV8GHhrsQU+QMZuomfqXiqzz3ILLkCtKx3Us6AmomFViuzKT9D693QTKgyut2oCytMG8/efOop+DB+w==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, + "node_modules/@types/supertest": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", + "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", + "dev": true, + "dependencies": { + "@types/superagent": "*" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", + "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.60.0", + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/typescript-estree": "5.60.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", + "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", + "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", + "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.0", + "@typescript-eslint/visitor-keys": "5.60.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.60.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", + "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.60.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "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, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.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, + "peer": 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-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "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, + "peer": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", + "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.4", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", + "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.4" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bcryptjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.0.tgz", + "integrity": "sha512-Q2vVGpGC7B7m9wggpcA5lq4OYR5OS1nrXoUpnH9MmogXU8HpxzKg63uxtCrLebY5v/y3o0r7JcGCpR/vTGGn7A==", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "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" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", + "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" + } + ] + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "dev": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "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/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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, + "node_modules/core-js-compat": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.101", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz", + "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/env-cmd": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/env-cmd/-/env-cmd-10.1.0.tgz", + "integrity": "sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==", + "dependencies": { + "commander": "^4.0.0", + "cross-spawn": "^7.0.0" + }, + "bin": { + "env-cmd": "bin/env-cmd.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": 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, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-no-unused-expressions": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-unused-expressions/-/eslint-plugin-no-unused-expressions-0.1.0.tgz", + "integrity": "sha512-Y2iXE9zZz+8N7PrjV6P+NqogFFWh9s2F72VG13yfL8zoz8bFUG5Kr2Zc4SzP2j7wkGMG7xgLDyyOSxp5NFuUIw==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": 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", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expres": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/expres/-/expres-0.0.5.tgz", + "integrity": "sha512-pK2yFc2Ke66rveH6iq6YJr63v55cFP6fs2xhoJ7RUGg+uwwApMlod3IwVm8zEtKH4ALqSvaGQF4cNemsyApd/w==" + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", + "peerDependencies": { + "express": "^4.16.2" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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, + "peer": true + }, + "node_modules/fast-redact": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.2.0.tgz", + "integrity": "sha512-zaTadChr+NekyzallAMXATXLOR8MNx3zqpZ0MUF2aGf4EathnG0f32VLODNlY8IuGY3HoRO2L6/6fSzNsLaHIw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "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, + "peer": 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": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true, + "peer": true + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "peer": true + }, + "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-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "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, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "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==" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/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/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-cucumber": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/jest-cucumber/-/jest-cucumber-4.5.0.tgz", + "integrity": "sha512-EGVqkeE6xM/wnpWuLuB3AMQs4vNkLDwOuH3bsH2AigphAqDp+k3E+AIh0FAKhJ/1IjLTfZKyupIPRlYN62YZ+A==", + "dev": true, + "dependencies": { + "@cucumber/gherkin": "^28.0.0", + "callsites": "^3.0.0", + "glob": "^10.3.10", + "uuid": "^10.0.0" + }, + "peerDependencies": { + "@types/jest": ">=29.5.12", + "jest": ">=29.7.0", + "vitest": ">=1.4.0" + }, + "peerDependenciesMeta": { + "@types/jest": { + "optional": true + }, + "jest": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/jest-cucumber/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/jest-cucumber/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-cucumber/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-cucumber/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/jest-cucumber/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "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, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "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, + "peer": 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, + "peer": 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, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "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, + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "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, + "peer": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=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/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "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", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", + "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "peer": 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.3" + }, + "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, + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "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, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "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-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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 + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.14.1.tgz", + "integrity": "sha512-8LYNv7BKWXSfS+k6oEc6occy5La+q2sPwU3q2ljTX5AZk7v+5kND2o5W794FyRaqha6DJajmkNRsWtPpFyMUdw==", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "v1.0.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^2.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.1.0", + "thread-stream": "^2.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz", + "integrity": "sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==", + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", + "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.1.tgz", + "integrity": "sha512-wHuWB+CvSVb2XqXM0W/WOYUkVSPbiJb9S5fNB7TBhd8s892Xq910bRxwHtC4l71hgztObTjXL6ZheZXFjhDrDQ==" + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-warning": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.2.0.tgz", + "integrity": "sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==" + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "deprecated": "This version has a critical bug in fallback handling. Please upgrade to reflect-metadata@0.2.2 or newer.", + "dev": true + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/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, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "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==", + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sonic-boom": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", + "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "deprecated": "Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/supertest": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", + "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", + "dev": true, + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.1.2" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "peer": true + }, + "node_modules/thread-stream": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.3.0.tgz", + "integrity": "sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "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, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "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" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000000..91b35355db --- /dev/null +++ b/backend/package.json @@ -0,0 +1,55 @@ +{ + "name": "backend-nodejs-ess", + "version": "0.1.0", + "description": "", + "scripts": { + "start": "env-cmd -f .env.development nodemon --watch 'src/**/*.ts' --exec ts-node src/index.js", + "build": "tsc -p .", + "prod": "node dist/src/index.js", + "test": "env-cmd -f .env jest --verbose --coverage --config ./jest.config.cjs --detectOpenHandles", + "prettier": "prettier --write 'src/**/*.{ts,js}'", + "lint": "eslint '**/*.ts' --report-unused-disable-directives --max-warnings 0 --fix", + "prepare": "husky install" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcryptjs": "^3.0.0", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.4.7", + "env-cmd": "^10.1.0", + "expres": "^0.0.5", + "express": "^4.21.2", + "express-async-errors": "^3.1.1", + "jsonwebtoken": "^9.0.2", + "pino": "^8.10.0", + "uuid": "^9.0.0" + }, + "type": "module", + "devDependencies": { + "@babel/core": "^7.26.10", + "@babel/preset-env": "^7.26.9", + "@types/babel__core": "^7.20.5", + "@types/babel__traverse": "^7.20.6", + "@types/cors": "^2.8.13", + "@types/express": "^4.17.17", + "@types/jest": "^29.5.0", + "@types/node": "^20.17.19", + "@types/prettier": "^2.7.2", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.0", + "@typescript-eslint/parser": "^5.51.0", + "babel-jest": "^29.7.0", + "eslint-plugin-no-unused-expressions": "^0.1.0", + "jest": "^29.7.0", + "jest-cucumber": "^4.5.0", + "nodemon": "^3.1.9", + "prettier": "^2.8.4", + "supertest": "^6.3.4", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.2", + "typescript": "^5.7.3" + } +} diff --git a/backend/src/app.js b/backend/src/app.js new file mode 100644 index 0000000000..0c52dee16c --- /dev/null +++ b/backend/src/app.js @@ -0,0 +1,14 @@ +import express from 'express'; +import cors from 'cors'; // Corrigido para importação de ES Modules +import likeRoutes from './routes/like.routes.js'; +import recomendationsRoutes from './routes/recomendations.routes.js'; + +const app = express(); + +app.use(cors()); // Permite todas as origens +app.use(express.json()); + +app.use('/user', likeRoutes); +app.use('/user', recomendationsRoutes); + +export default app; diff --git a/backend/src/controllers/auth.controllers.js b/backend/src/controllers/auth.controllers.js new file mode 100644 index 0000000000..5b1d9173d0 --- /dev/null +++ b/backend/src/controllers/auth.controllers.js @@ -0,0 +1,110 @@ +import fs from 'fs' +import path from 'path' +import bcrypt from 'bcryptjs' +import generateTokenAndSetCookie from '../utils/generateToken.js' + +export const signup = async (req, res) => { + try { + const {fullName, username, birth_date, gender, photo, password, confirmPassword } = req.body + + if (password !== confirmPassword) { + return res.status(400).json({ + error: "Passwords do not match" + }) + } + + var data = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')) + + // USERNAME ALREADY USED + const user = data.filter(element => element.username === username) + if (user && user.length > 0) { + console.log("Username already used") + return res.status(409).json({ + error: "Username already used" + }) + } + + // HASH PASSWORD HERE + const salt = await bcrypt.genSalt(10) + const hashedPassword = await bcrypt.hash(password, salt) + + const id = JSON.stringify(data.length + 1) + + const newUser = { + id, + fullName, + username, + birth_date, + gender, + photo, + password: hashedPassword + } + + generateTokenAndSetCookie(id, res) + + data.push(newUser) + + fs.writeFileSync(path.resolve('./samples/users.json'), JSON.stringify(data, null, 2)) + + res.status(201).json({ + id, + fullName, + username, + birth_date, + gender, + }) + + } catch (error) { + console.log("Error in signup controller:", error.message) + res.status(500).json({ + error:"Internal server error" + }) + } +} + +export const login = async (req, res) => { + try { + const { username, password } = req.body; + var data = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')) + var data = data.find(({ username }) => username === username) + const isPasswordCorrect = await bcrypt.compare(password, data.password) + + if (!data || !isPasswordCorrect) { + console.log("Invalid credentials") + return res.status(401).json({ + error: "Invalid credentials" + }) + } + + generateTokenAndSetCookie(data.id, res) + + res.status(200).json({ + id: data.id, + fullName: data.fullName, + username: data.username, + }) + + + } catch (error) { + console.log("Error in login controller:", error.message) + res.status(500).json({ + error:"Internal server error" + }) + } +} + +export const logout = async (req, res) => { + try { + res.cookie("jwt", "", {maxAge: 0}) + + res.status(200).json({ + message: "Logged out succesfully" + }) + + } catch (error) { + console.log("Error in logout controller:", error.message) + res.status(500).json({ + error:"Internal server error" + }) + } +} \ No newline at end of file diff --git a/backend/src/controllers/filmes.controllers.js b/backend/src/controllers/filmes.controllers.js new file mode 100644 index 0000000000..93a7c0b32d --- /dev/null +++ b/backend/src/controllers/filmes.controllers.js @@ -0,0 +1,180 @@ +import fs from 'fs' +import path from 'path' + + +//Retorna todos os filmes da categoria AÇÃO +export const getAcaoJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/acao.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} +//Retorna filmes de SUSPENSE +export const getSuspenseJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/suspense.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} +//Retorna filmes de AVENTURA +export const getAventuraJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/aventura.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +//Retorna filmes de TERROR +export const getTerrorJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/terror.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} +//Retorna filmes de DRAMA +export const getDramaJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/drama.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +//Retorna filmes de COMEDIA +export const getComediaJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/comedia.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +//Retorna filmes de ROMANCE +export const getRomanceJson = (req,res) =>{ + + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/romance.json'), 'utf-8')) + + if(!data){ + + console.log("Lista vazia") + return res.status(200).json({}) + } + + return res.status(200).json(data) + + } catch (error) { + console.log("Error in getALL", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +//Retorna os filmes por nome do diretor +export const getDiretorJson = (req,res) =>{ + try { + + const parser = JSON.parse(fs.readFileSync(path.resolve('./lista_filmes/acao.json'), 'utf-8')) + const data = parser.filter(element => element.author === req.params.diretor_nome) + + return res.status(200).json(data) + + + } catch (error) { + console.log("Error in getBoodId", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + diff --git a/backend/src/controllers/history.controller.js b/backend/src/controllers/history.controller.js new file mode 100644 index 0000000000..aa10fa4077 --- /dev/null +++ b/backend/src/controllers/history.controller.js @@ -0,0 +1,49 @@ +const { Router } = require('express'); +const HistoryService = require('../services/history.service'); +const { SuccessResult } = require('../utils/result'); + +class HistoryController { + constructor(router, historyService) { + this.router = router; + this.historyService = historyService; + this.prefix = '/users'; + this.initRoutes(); + } + + initRoutes() { + this.router.get(`${this.prefix}/:id/history`, this.getHistory.bind(this)); + this.router.put(`${this.prefix}/:id/history`, this.updateHistory.bind(this)); + } + + async getHistory(req, res) { + try { + const userId = req.params.id; + const result = await this.historyService.getHistory(userId); + // Retorna somente os videoIds para os testes + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: result.data.map(item => item.videoId), + code: result.code, + }).handle(res); + } catch (error) { + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } + + async updateHistory(req, res) { + try { + const userId = req.params.id; + const videoData = req.body; + const result = await this.historyService.addOrUpdateHistory(userId, videoData); + new SuccessResult({ + msg: result.msg, + data: { history: result.data.map(item => item.videoId) }, + code: result.code, + }).handle(res); + } catch (error) { + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } +} + +module.exports = HistoryController; diff --git a/backend/src/controllers/like.controllers.js b/backend/src/controllers/like.controllers.js new file mode 100644 index 0000000000..a5d6a78733 --- /dev/null +++ b/backend/src/controllers/like.controllers.js @@ -0,0 +1,115 @@ +import fs from 'fs'; +import path from 'path'; + +export const seriesCurtidas = async (req, res) => { + try { + const parser = JSON.parse(fs.readFileSync(path.resolve('./src/database/users.json'), 'utf-8')); + + // Filtra apenas os objetos do usuário especificado + const userData = parser.filter(element => element.user === req.params.userid); + + if (userData.length === 0) { + console.log("Nenhum dado encontrado para o usuário:", req.params.userid); + return res.status(404).json({ error: "Usuário não encontrado ou sem séries curtidas" }); + } + + // Extrai apenas a propriedade "Séries Curtidas" + // const seriesCurtidas = userData.map(item => ({ + // "Séries Curtidas": item["Séries Curtidas"] + // })); + const seriesCurtidas = userData.map(item => item["Séries Curtidas"]).flat(); + + + res.status(200).json(seriesCurtidas); + } catch (error) { + console.log("Erro na função get seriesCurtidas:", error.message); + res.status(500).json({ + error: "Internal Server Error" + }); + } +}; + +export const curtir = async(req, res) => { + console.log(req.body) + try { + + const { userid } = req.params; + const { serie } = req.body; + + const filePath = path.resolve('./src/database/users.json'); + + let data = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + let user = data.find(element => element.user == userid); + + if (typeof user["Séries Curtidas"] === "string") { + user["Séries Curtidas"] = [user["Séries Curtidas"]]; + } + + if (!user["Séries Curtidas"]) { + user["Séries Curtidas"] = []; + } + + if (user["Séries Curtidas"].includes(serie)) { + console.log(`Série "${serie}" já foi curtida pelo usuário "${userid}"`); + return res.status(400).json({ error: "Série já foi curtida pelo usuário" }); + } + + user["Séries Curtidas"].push(serie); + + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); + + res.status(200).json({ message: "Série curtida com sucesso!", user }); + } catch (error) { + console.error("Erro na função curtir:", error); + res.status(500).json({ + error: "Internal Server Error" + }); + } +} + +export const descurtir = async(req,res) => { + try { + const filePath = path.resolve('./src/database/users.json'); + const { userid } = req.params; + const { serie } = req.body; + + // Ler o arquivo JSON + let data = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + + // Encontrar o usuário + let user = data.find(element => element.user === userid); + + if (!user) { + console.log("Usuário não encontrado:", userid); + return res.status(404).json({ error: "Usuário não encontrado" }); + } + + // Se "Séries Curtidas" for uma string, transforma em array + if (typeof user["Séries Curtidas"] === "string") { + user["Séries Curtidas"] = [user["Séries Curtidas"]]; + } + + // Se não existir, inicializa como array vazio + if (!user["Séries Curtidas"]) { + user["Séries Curtidas"] = []; + } + + // Verificar se a série existe na lista + if (!user["Séries Curtidas"].includes(serie)) { + console.log(`Série "${serie}" não encontrada para o usuário "${userid}"`); + return res.status(400).json({ error: "Série não encontrada na lista do usuário" }); + } + + // // Remover a série do array + user["Séries Curtidas"] = user["Séries Curtidas"].filter(s => s !== serie); + + // Escrever de volta no arquivo JSON + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); + + res.status(200).json({ message: "Série removida com sucesso!", user }); + } catch (error) { + res.status(500).json({ + error: "Internal Server Error" + }); + } +} \ No newline at end of file diff --git a/backend/src/controllers/list.controller.js b/backend/src/controllers/list.controller.js new file mode 100644 index 0000000000..4101ebb5b1 --- /dev/null +++ b/backend/src/controllers/list.controller.js @@ -0,0 +1,78 @@ +const ListRepository = require('../repositories/list.repository'); +const { SuccessResult } = require('../utils/result'); + +class ListController { + constructor(router, listService) { + this.router = router; + this.listService = listService; + this.prefix = '/listas'; + this.initRoutes(); + } + + initRoutes() { + this.router.post(`${this.prefix}/`, this.addList.bind(this)); + this.router.post(`${this.prefix}/:id/video`, this.addVideoToList.bind(this)); + this.router.delete(`${this.prefix}/:id`, this.deleteList.bind(this)); + this.router.delete(`${this.prefix}/:id/video`, this.removeVideoFromList.bind(this)); + } + + async addList(req, res) { + try { + const { titulo, userId } = req.body; + const list = await this.listService.addList(titulo, userId); + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: list, + }).handle(res); + } catch (error) { + console.error(error); + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } + + async addVideoToList(req, res) { + try { + const { videoId } = req.body; + const { id } = req.params; + await this.listService.addVideoToList(videoId, id); + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: true, + }).handle(res); + } catch (error) { + console.error(error); + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } + + async removeVideoFromList(req, res) { + try { + const { videoId } = req.body; + const { id } = req.params; + await this.listService.removeVideoFromList(videoId, id); + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: true, + }).handle(res); + } catch (error) { + console.error(error); + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } + + async deleteList(req, res) { + try { + const { id } = req.params; + await this.listService.deleteList(id); + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: true, + }).handle(res); + } catch (error) { + console.error(error); + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } +} + +module.exports = ListController; diff --git a/backend/src/controllers/recomendations.controllers.js b/backend/src/controllers/recomendations.controllers.js new file mode 100644 index 0000000000..ae4524204b --- /dev/null +++ b/backend/src/controllers/recomendations.controllers.js @@ -0,0 +1,79 @@ +import fs from 'fs'; +import path from 'path'; + +export const top10 = async (req, res) => { + try { + const parser = JSON.parse(fs.readFileSync(path.resolve('./src/database/users.json'), 'utf-8')); + + // Filtra apenas os objetos do usuário especificado + const userData = parser.find(element => element.user === 'Sistema'); + + if (userData.length === 0) { + console.log("Nenhum dado encontrado para o usuário:", 'Sistema'); + return res.status(404).json({ error: "Usuário não encontrado ou sem séries curtidas" }); + } + + const top10 = userData.top10 + + res.status(200).json(top10); + } catch (error) { + console.log("Erro na função get top10:", error.message); + res.status(500).json({ + error: "Internal Server Error" + }); + } +}; + +export const recomendacaoGeral = async (req, res) => { + try { + const parser = JSON.parse(fs.readFileSync(path.resolve('./src/database/users.json'), 'utf-8')); + + // Filtra apenas os objetos do usuário especificado + const userData = parser.find(element => element.user === 'Sistema'); + + if (userData.length === 0) { + console.log("Nenhum dado encontrado para o usuário:", 'Sistema'); + return res.status(404).json({ error: "Usuário não encontrado ou sem séries curtidas" }); + } + + const geral = userData.geral + + res.status(200).json(geral); + } catch (error) { + console.log("Erro na função get recomendacaoGeral:", error.message); + res.status(500).json({ + error: "Internal Server Error" + }); + } +}; + +export const recomendacaoGenero = async (req, res) => { + try { + const { generoid } = req.params; // Recebe o gênero pelo parâmetro + const parser = JSON.parse(fs.readFileSync(path.resolve('./src/database/users.json'), 'utf-8')); + + // Filtra o usuário "Sistema" + const userData = parser.find(element => element.user === 'Sistema'); + + if (!userData) { + console.log("Nenhum dado encontrado para o usuário:", 'Sistema'); + return res.status(404).json({ error: "Usuário não encontrado" }); + } + + // Verifica se o gênero existe no usuário + if (!userData[generoid]) { + console.log(`Gênero '${generoid}' não encontrado.`); + return res.status(404).json({ error: `Gênero '${generoid}' não encontrado.` }); + } + + // Retorna a lista de filmes do gênero solicitado + const recomendacaoGenero = userData[generoid]; + + res.status(200).json(recomendacaoGenero); + } catch (error) { + console.log("Erro na função get top10:", error.message); + res.status(500).json({ + error: "Internal Server Error" + }); + } +}; \ No newline at end of file diff --git a/backend/src/controllers/test.controller.js b/backend/src/controllers/test.controller.js new file mode 100644 index 0000000000..0051ddee8d --- /dev/null +++ b/backend/src/controllers/test.controller.js @@ -0,0 +1,178 @@ +const { Router } = require('express'); +const { Result, SuccessResult } = require('../utils/result'); +const TestService = require('../services/test.service'); +const TestEntity = require('../entities/test.entity'); + +class TestController { + constructor(router, testService) { + this.router = router; + this.testService = testService; + this.prefix = '/tests'; + this.initRoutes(); + } + + initRoutes() { + this.router.get(this.prefix, (req, res) => this.getTests(req, res)); + this.router.get(`${this.prefix}/others`, (req, res) => this.getOthersTests(req, res)); + this.router.get(`${this.prefix}/:id`, (req, res) => this.getTest(req, res)); + this.router.post(this.prefix, (req, res) => this.createTest(req, res)); + this.router.put(`${this.prefix}/:id`, (req, res) => this.updateTest(req, res)); + this.router.delete(`${this.prefix}/:id`, (req, res) => this.deleteTest(req, res)); + } + + async getTests(req, res) { + const tests = await this.testService.getTests(); + + return new SuccessResult({ + msg: Result.transformRequestOnMsg(req), + data: tests, + }).handle(res); + } + + async getOthersTests(req, res) { + const tests = await this.testService.getOtherTests(); + + return new SuccessResult({ + msg: Result.transformRequestOnMsg(req), + data: tests, + }).handle(res); + } + + async getTest(req, res) { + const test = await this.testService.getTest(req.params.id); + + return new SuccessResult({ + msg: Result.transformRequestOnMsg(req), + data: test, + }).handle(res); + } + + async createTest(req, res) { + const test = await this.testService.createTest(new TestEntity(req.body)); + + return new SuccessResult({ + msg: Result.transformRequestOnMsg(req), + data: test, + }).handle(res); + } + + async updateTest(req, res) { + const test = await this.testService.updateTest(req.params.id, new TestEntity(req.body)); + + return new SuccessResult({ + msg: Result.transformRequestOnMsg(req), + data: test, + }).handle(res); + } + + async deleteTest(req, res) { + await this.testService.deleteTest(req.params.id); + + return new SuccessResult({ + msg: Result.transformRequestOnMsg(req), + }).handle(res); + } +} + +module.exports = TestController; + + + + +// import { Router, Request, Response } from 'express'; +// import { Result, SuccessResult } from '../utils/result'; +// import TestService from '../services/test.service'; +// import TestEntity from '../entities/test.entity'; + +// class TestController { +// private prefix: string = '/tests'; +// public router: Router; +// private testService: TestService; + +// constructor(router: Router, testService: TestService) { +// this.router = router; +// this.testService = testService; +// this.initRoutes(); +// } + +// private initRoutes() { +// this.router.get(this.prefix, (req: Request, res: Response) => +// this.getTests(req, res) +// ); + +// this.router.get(`${this.prefix}/others`, (req: Request, res: Response) => +// this.getOthersTests(req, res) +// ); + +// this.router.get(`${this.prefix}/:id`, (req: Request, res: Response) => +// this.getTest(req, res) +// ); +// this.router.post(this.prefix, (req: Request, res: Response) => +// this.createTest(req, res) +// ); +// this.router.put(`${this.prefix}/:id`, (req: Request, res: Response) => +// this.updateTest(req, res) +// ); +// this.router.delete(`${this.prefix}/:id`, (req: Request, res: Response) => +// this.deleteTest(req, res) +// ); +// } + +// private async getTests(req: Request, res: Response) { +// const tests = await this.testService.getTests(); + +// return new SuccessResult({ +// msg: Result.transformRequestOnMsg(req), +// data: tests, +// }).handle(res); +// } + +// private async getOthersTests(req: Request, res: Response) { +// const tests = await this.testService.getOtherTests(); + +// return new SuccessResult({ +// msg: Result.transformRequestOnMsg(req), +// data: tests, +// }).handle(res); +// } + +// private async getTest(req: Request, res: Response) { +// const test = await this.testService.getTest(req.params.id); + +// return new SuccessResult({ +// msg: Result.transformRequestOnMsg(req), +// data: test, +// }).handle(res); +// } + +// private async createTest(req: Request, res: Response) { +// const test = await this.testService.createTest(new TestEntity(req.body)); + +// return new SuccessResult({ +// msg: Result.transformRequestOnMsg(req), +// data: test, +// }).handle(res); +// } + +// private async updateTest(req: Request, res: Response) { +// const test = await this.testService.updateTest( +// req.params.id, +// new TestEntity(req.body) +// ); + +// return new SuccessResult({ +// msg: Result.transformRequestOnMsg(req), +// data: test, +// }).handle(res); +// } + +// private async deleteTest(req: Request, res: Response) { +// await this.testService.deleteTest(req.params.id); + +// return new SuccessResult({ +// msg: Result.transformRequestOnMsg(req), +// }).handle(res); +// } +// } + +// export default TestController; diff --git a/backend/src/controllers/user.controllers.js b/backend/src/controllers/user.controllers.js new file mode 100644 index 0000000000..b87601d54d --- /dev/null +++ b/backend/src/controllers/user.controllers.js @@ -0,0 +1,175 @@ +import fs from 'fs' +import path from 'path' +import bcrypt from 'bcryptjs' + +export const getAll = (req, res) => { + try { + + const data = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')) + + if (!data || data.length === 0) { + console.log("Empty users") + return res.status(200).json({}) + } + + const filterData = data.map(user => ({ + fullName: user.fullName, + username: user.username, + birth_date: user.birth_date, + gender: user.gender, + photo: user.photo, + })) + + res.status(200).json(filterData) + + } catch (error) { + console.log("Error in getAll:", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +export const getUserById = (req, res) => { + try { + + const users = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')) + const user = users.find(element => String(element.id) == String(req.params.userId)) + + // USER NOT REGISTERED + if (!user){ + console.log("User not found") + return res.status(404).json({ + error: "User not found" + }) + } + + res.status(200).json({ + fullName: user.fullName, + username: user.username, + birth_date: user.birth_date, + gender: user.gender, + photo: user.photo + }) + + } catch (error) { + console.log("Error in getUserById:", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +export const updateUser = async (req, res) => { + try { + + let data = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')) + + const userIndex = data.findIndex(element => String(element.id) == String(req.params.userId)) + // USER NOT REGISTERED + if (userIndex === -1){ + console.log("User not found") + return res.status(404).json({ + error: "User not found" + }) + } + + const user = data[userIndex]; + + // CHECK IF PASSWORD IS CORRECT + const isPasswordCorrect = await bcrypt.compare(req.body.password, user.password) + + if (!data || !isPasswordCorrect) { + console.log("Incorrect password") + return res.status(401).json({ + error: "Incorrect password" + }) + } + + // NEW USER IS ALREADY REGISTERED + const isUsernameTaken = data.some(user => + user.username === req.body.username && String(user.id) !== String(req.params.userId) + ); + + if (isUsernameTaken){ + console.log("Username already used") + return res.status(409).json({ + error: "Username already used" + }) + } + + // NEW PASSWORD + let updatedPassword = user.password + if (req.body.newPassword){ + // HASH PASSWORD HERE + const salt = await bcrypt.genSalt(10) + updatedPassword = await bcrypt.hash(req.body.newPassword, salt) + } + + data[userIndex] = { + id: req.params.userId, + fullName: req.body.fullName, + username: req.body.username, + birth_date: req.body.birth_date, + gender: req.body.gender, + photo: req.body.photo, + password: updatedPassword + } + + fs.writeFileSync(path.resolve('./samples/users.json'), JSON.stringify(data, null, 2)) + + res.status(200).json(data) + + } catch (error) { + console.log("Error in updateUserJson:", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} + +export const deleteUser = async (req, res) => { + try { + + let data = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')) + + const userIndex = data.findIndex(element => String(element.id) == String(req.params.userId)) + + // USER NOT REGISTERED + if (userIndex === -1){ + console.log("User not found") + return res.status(404).json({ + error: "User not found" + }); + } + + const password = data[userIndex].password + + // CHECK IF PASSWORD IS CORRECT + const isPasswordCorrect = await bcrypt.compare(req.body.password, password) + + if (!data || !isPasswordCorrect) { + console.log("Incorrect password") + return res.status(401).json({ + error: "Incorrect password" + }) + } + + data.splice(userIndex, 1) + + // Index handling + for (let i = userIndex; i < data.length; i++) { + data[i].id = String(Number(data[i].id)-1); + } + + fs.writeFileSync(path.resolve('./samples/users.json'), JSON.stringify(data, null, 2)) + + res.status(200).json({message: "User deleted successfully", users: data}); + + } catch (error) { + console.log("Error in deleteUser:", error.message) + res.status(500).json({ + error: "Internal Server Error" + }) + } +} \ No newline at end of file diff --git a/backend/src/controllers/video.controller.js b/backend/src/controllers/video.controller.js new file mode 100644 index 0000000000..c90b5c14cd --- /dev/null +++ b/backend/src/controllers/video.controller.js @@ -0,0 +1,48 @@ +const { Router } = require('express'); +const VideoService = require('../services/video.service'); +const { SuccessResult } = require('../utils/result'); + +class VideoController { + constructor(router, videoService) { + this.router = router; + this.videoService = videoService; + this.prefix = '/videos'; + this.initRoutes(); + } + + initRoutes() { + this.router.get(`${this.prefix}/:id`, this.getVideo.bind(this)); + this.router.post(`${this.prefix}/:id/visualizacao`, this.registerView.bind(this)); + } + + async getVideo(req, res) { + try { + const videoId = req.params.id; + const video = await this.videoService.getVideo(videoId); + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: video, // usa o objeto retornado direto + code: 200, // código de sucesso + }).handle(res); + } catch (error) { + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } + + async registerView(req, res) { + try { + const videoId = req.params.id; + const { userId } = req.body; + const result = await this.videoService.registerView(videoId, userId); + new SuccessResult({ + msg: `${req.method} ${req.originalUrl}`, + data: result.data, + code: result.code, + }).handle(res); + } catch (error) { + res.status(error.status || 500).json({ msg: error.msg || error.message, msgCode: error.msgCode }); + } + } +} + +module.exports = VideoController; diff --git a/backend/src/database/db.js b/backend/src/database/db.js new file mode 100644 index 0000000000..a59e564433 --- /dev/null +++ b/backend/src/database/db.js @@ -0,0 +1,12 @@ +import sqlite3 from 'sqlite3'; +import path from 'path'; +import { open } from 'sqlite'; + +const initDB = async () => { + return open({ + filename: path.resolve('database', 'database.sqlite'), + driver: sqlite3.Database + }) +} + +export default initDB; \ No newline at end of file diff --git a/backend/src/database/index.js b/backend/src/database/index.js new file mode 100644 index 0000000000..fde7ac2f28 --- /dev/null +++ b/backend/src/database/index.js @@ -0,0 +1,88 @@ +const sqlite3 = require('sqlite3').verbose(); +const { open } = require('sqlite'); +const path = require('path'); + +class Database { + static async getInstance() { + if (!Database.instance) { + Database.instance = new Database(); + await Database.instance._init(); + } + return Database.instance; + } + + async _init() { + const dbPath = process.env.ENV === 'TEST' + ? ':memory:' + : path.resolve(process.cwd(), 'database.sqlite'); + this.db = await open({ + filename: dbPath, + driver: sqlite3.Database, + }); + await this.db.exec('PRAGMA foreign_keys = ON;'); + await this.initialize(); + } + + async initialize() { + await this.db.exec(` + CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, + nome TEXT + ); + CREATE TABLE IF NOT EXISTS videos ( + id TEXT PRIMARY KEY, + videoId TEXT, + titulo TEXT, + duracao TEXT, + views INTEGER, + likes INTEGER, + videoLink TEXT + ); + CREATE TABLE IF NOT EXISTS histories ( + id TEXT PRIMARY KEY, + userId TEXT, + videoId TEXT, + ultimaVisualizacao TEXT, + FOREIGN KEY(userId) REFERENCES users(id) + ); + CREATE TABLE IF NOT EXISTS lists ( + id TEXT PRIMARY KEY, + userId TEXT, + videoIds TEXT, + titulo TEXT + ); + `); + } + + static async reset() { + const instance = await Database.getInstance(); + await instance.db.exec(` + DROP TABLE IF EXISTS videos; + DROP TABLE IF EXISTS histories; + DROP TABLE IF EXISTS users; + `); + await instance.initialize(); + } + + async seed() { + await this.db.exec(` + INSERT OR IGNORE INTO users (id, nome) VALUES + ('1', 'User 1'), + ('2', 'User 2'), + ('3', 'User 3'); + `); + await this.db.exec(` + INSERT OR IGNORE INTO videos (id, videoId, titulo, duracao, views, likes, videoLink) VALUES + ('${Date.now()}-101', '101', 'Stranger Things - Piloto', '45 minutos', 0, 0, 'https://youtube.com/watch?v=101'), + ('${Date.now()}-102', '102', 'Breaking Bad - Piloto', '60 minutos', 0, 0, 'https://youtube.com/watch?v=102'); + `); + const now = new Date().toISOString(); + await this.db.exec(` + INSERT OR IGNORE INTO histories (id, userId, videoId, ultimaVisualizacao) VALUES + ('${Date.now()}-h1', '1', '101', '${now}'), + ('${Date.now()}-h2', '1', '102', '${now}'); + `); + } +} + +module.exports = Database; diff --git a/backend/src/database/migrations.js b/backend/src/database/migrations.js new file mode 100644 index 0000000000..98f514b635 --- /dev/null +++ b/backend/src/database/migrations.js @@ -0,0 +1,17 @@ +import initDB from "./db"; + +const createTables = async () => { + const db = await initDB(); + await db.exec(` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + fullName TEXT NOT NULL, + username TEXT UNIQUE NOT NULL, + birth_date TEXT NOT NULL, + gender TEXT NOT NULL, + photo TEXT NOT NULL, + password TEXT NOT NULL + ) + `); + console.log("Tables created successfully"); +} \ No newline at end of file diff --git a/backend/src/database/users.json b/backend/src/database/users.json new file mode 100644 index 0000000000..3ef3c4462e --- /dev/null +++ b/backend/src/database/users.json @@ -0,0 +1,51 @@ +[ + { + "user": "Sistema", + "top10": [ + "howls-moving-castle", + "the-hangover", + "superbad", + "la-la-land", + "the-notebook", + "a-quiet-place", + "it", + "jumanji", + "mad-max", + "alien" + ], + "terror": [ + "A Freira", + "Sorria" + ], + "acao": [ + "Homem Aranha, de volta ao lar", + "The Batman" + ], + "geral": [ + "black-panther", + "alien", + "mad-max", + "jumanji" + ], + "id": 1 + }, + { + "user": "Ykaro", + "Séries Curtidas": [ + "mad-max", + "black-panther" + ], + "historicoDeVisualizacao": [ + "O Incrível Hulk", + "Homem-Formiga", + "Invocação do mal" + ], + "id": "1" + }, + { + "user": "João", + "Séries Curtidas": [], + "historicoDeVisualizacao": [], + "id": "2" + } +] \ No newline at end of file diff --git a/backend/src/di/index.js b/backend/src/di/index.js new file mode 100644 index 0000000000..5b8631d25b --- /dev/null +++ b/backend/src/di/index.js @@ -0,0 +1,17 @@ +import OtherRepository from '../repositories/other.repository'; +import TestRepository from '../repositories/test.repository'; +import TestService from '../services/test.service'; +import Injector from './injector'; + +export const di = new Injector(); + +// Test +di.registerRepository(TestRepository, new TestRepository()); +di.registerRepository(OtherRepository, new OtherRepository()); +di.registerService( + TestService, + new TestService( + di.getRepository(TestRepository), + di.getRepository(OtherRepository) + ) +); diff --git a/backend/src/di/injector.js b/backend/src/di/injector.js new file mode 100644 index 0000000000..f432b07064 --- /dev/null +++ b/backend/src/di/injector.js @@ -0,0 +1,24 @@ +class Injector { + constructor() { + this.services = new Map(); + this.repositories = new Map(); + } + + registerService(serviceType, service) { + this.services.set(serviceType, service); + } + + getService(serviceType) { + return this.services.get(serviceType); + } + + registerRepository(repositoryType, repository) { + this.repositories.set(repositoryType, repository); + } + + getRepository(repositoryType) { + return this.repositories.get(repositoryType); + } +} + +module.exports = Injector; diff --git a/backend/src/entities/base.entity.js b/backend/src/entities/base.entity.js new file mode 100644 index 0000000000..3c53854f94 --- /dev/null +++ b/backend/src/entities/base.entity.js @@ -0,0 +1,3 @@ +class BaseEntity {} + +module.exports = BaseEntity; diff --git a/backend/src/entities/history.item.entity.js b/backend/src/entities/history.item.entity.js new file mode 100644 index 0000000000..8f4076e1a5 --- /dev/null +++ b/backend/src/entities/history.item.entity.js @@ -0,0 +1,12 @@ +const BaseEntity = require('./base.entity'); + +class HistoryItemEntity extends BaseEntity { + constructor({ userId, videoId, ultimaVisualizacao }) { + super(); + this.userId = userId; + this.videoId = videoId; + this.ultimaVisualizacao = ultimaVisualizacao; + } +} + +module.exports = HistoryItemEntity; diff --git a/backend/src/entities/list.entity.js b/backend/src/entities/list.entity.js new file mode 100644 index 0000000000..3314e0248e --- /dev/null +++ b/backend/src/entities/list.entity.js @@ -0,0 +1,13 @@ +const BaseEntity = require('./base.entity'); + +class ListEntity extends BaseEntity { + constructor({ videoIds, titulo, id, userId }) { + super(); + this.id = id; + this.userId = userId; + this.videoIds = videoIds; + this.titulo = titulo; + } +} + +module.exports = ListEntity; diff --git a/backend/src/entities/test.entity.js b/backend/src/entities/test.entity.js new file mode 100644 index 0000000000..cc27563531 --- /dev/null +++ b/backend/src/entities/test.entity.js @@ -0,0 +1,10 @@ +const BaseEntity = require('./base.entity'); + +class TestEntity extends BaseEntity { + constructor(data) { + super(data.id || ''); + this.name = data.name; + } +} + +module.exports = TestEntity; diff --git a/backend/src/entities/video.entity.js b/backend/src/entities/video.entity.js new file mode 100644 index 0000000000..24aaace047 --- /dev/null +++ b/backend/src/entities/video.entity.js @@ -0,0 +1,14 @@ +const BaseEntity = require('./base.entity'); + +class VideoEntity extends BaseEntity { + constructor({ videoId, titulo, duracao, views, videoLink = '' }) { + super(); + this.videoId = videoId; + this.titulo = titulo; + this.duracao = duracao; + this.views = views; + this.videoLink = videoLink; + } +} + +module.exports = VideoEntity; diff --git a/backend/src/env.js b/backend/src/env.js new file mode 100644 index 0000000000..4a59d83602 --- /dev/null +++ b/backend/src/env.js @@ -0,0 +1,4 @@ +module.exports = class Env { + static ENV = process.env.ENV || 'DEV'; + static PORT = process.env.PORT || 5001; +}; diff --git a/backend/src/index.js b/backend/src/index.js new file mode 100644 index 0000000000..f57744c738 --- /dev/null +++ b/backend/src/index.js @@ -0,0 +1,27 @@ +import express from 'express' +import dotenv from 'dotenv' +import cookieParser from 'cookie-parser' +import cors from 'cors'; + +const app = express() +dotenv.config() +app.use(express.json()) +app.use(cookieParser()) +app.use(cors({ + origin: "http://localhost:3000" + })) + +// Route imports +import userRoutes from './routes/users.routes.js' +import authRoutes from './routes/auth.routes.js' +import filmesRoutes from './routes/filmes.routes.js' +import likeRoutes from './routes/like.routes.js'; +import recomendationsRoutes from './routes/recomendations.routes.js'; + +app.use('/user', likeRoutes); +app.use('/user', recomendationsRoutes); +app.use('/users', userRoutes) +app.use('/auth', authRoutes) +app.use('/filmes', filmesRoutes) + +app.listen(4000); \ No newline at end of file diff --git a/backend/src/models/base.model.js b/backend/src/models/base.model.js new file mode 100644 index 0000000000..314dd15554 --- /dev/null +++ b/backend/src/models/base.model.js @@ -0,0 +1,7 @@ +class BaseModel { + constructor(id) { + this.id = id; + } +} + +module.exports = BaseModel; diff --git a/backend/src/models/history.model.js b/backend/src/models/history.model.js new file mode 100644 index 0000000000..fcda1f39a4 --- /dev/null +++ b/backend/src/models/history.model.js @@ -0,0 +1,10 @@ +const HistoryItemEntity = require('../entities/history.item.entity'); + +class HistoryModel { + constructor(item) { + this.videoId = item.videoId; + this.ultimaVisualizacao = item.ultimaVisualizacao; + } +} + +module.exports = HistoryModel; diff --git a/backend/src/models/list.model.js b/backend/src/models/list.model.js new file mode 100644 index 0000000000..e7aa30fa09 --- /dev/null +++ b/backend/src/models/list.model.js @@ -0,0 +1,12 @@ +const ListItemEntity = require('../entities/list.item.entity'); + +class ListModel { + constructor(lista) { + this.id = lista.id; + this.userId = lista.userId; + this.videoIds = lista.videoIds; + this.titulo = lista.titulo; + } +} + +module.exports = ListModel; diff --git a/backend/src/models/test.model.js b/backend/src/models/test.model.js new file mode 100644 index 0000000000..6362838345 --- /dev/null +++ b/backend/src/models/test.model.js @@ -0,0 +1,25 @@ +const BaseModel = require('./base.model'); + +class TestModel extends BaseModel { + constructor(data) { + super(data.id || ''); + this.name = data.name; + } +} + +module.exports = TestModel; + + + + + +// import BaseModel from './base.model'; + +// export default class TestModel extends BaseModel { +// name: string; + +// constructor(data: TestModel) { +// super(data.id || ''); +// this.name = data.name; +// } +// } diff --git a/backend/src/models/userService.js b/backend/src/models/userService.js new file mode 100644 index 0000000000..a6d5794d7f --- /dev/null +++ b/backend/src/models/userService.js @@ -0,0 +1,52 @@ +import initDB from "../database/db.js"; + +const addUser = async (userData) => { + const db = await initDB(); + const { fullName, username, birth_date, gender, photo, password } = userData; + + const result = await db.run(` + INSERT INTO users (fullName, username, birth_date, gender, photo, password) + VALUES (?, ?, ?, ?, ?, ?) + `, [fullName, username, birth_date, gender, photo, password]); + + return result.lastID; // Retorna o ID do novo usuário inserido +}; + +export { addUser }; + +const getAllUsers = async () => { + const db = await initDB(); + const users = await db.all('SELECT * FROM users'); + return users; +}; + +const getUserById = async (id) => { + const db = await initDB(); + const user = await db.get('SELECT * FROM users WHERE id = ?', [id]); + return user; +}; + +export { getAllUsers, getUserById }; + +const updateUser = async (id, userData) => { + const db = await initDB(); + const { fullName, username, birth_date, gender, photo, password } = userData; + + await db.run(` + UPDATE users + SET fullName = ?, username = ?, birth_date = ?, gender = ?, photo = ?, password = ? + WHERE id = ? + `, [fullName, username, birth_date, gender, photo, password, id]); + + return { id, ...userData }; // Retorna os dados atualizados +}; + +export { updateUser }; + +const deleteUser = async (id) => { + const db = await initDB(); + await db.run('DELETE FROM users WHERE id = ?', [id]); + return { message: `User with ID ${id} deleted successfully` }; +}; + +export { deleteUser }; \ No newline at end of file diff --git a/backend/src/models/video.model.js b/backend/src/models/video.model.js new file mode 100644 index 0000000000..b0a5e91cb7 --- /dev/null +++ b/backend/src/models/video.model.js @@ -0,0 +1,13 @@ +const VideoEntity = require('../entities/video.entity'); + +class VideoModel { + constructor(video) { + this.videoId = video.videoId; + this.titulo = video.titulo; + this.duracao = video.duracao; + this.views = video.views; + this.videoLink = video.videoLink; + } +} + +module.exports = VideoModel; diff --git a/backend/src/repositories/base.repository.js b/backend/src/repositories/base.repository.js new file mode 100644 index 0000000000..37f3264515 --- /dev/null +++ b/backend/src/repositories/base.repository.js @@ -0,0 +1,65 @@ +const Database = require('../database'); +const { v4: uuidv4 } = require('uuid'); +const { HttpInternalServerError } = require('../utils/errors/http.error'); + +class BaseRepository { + constructor(tableName) { + this.tableName = tableName; + } + + async init() { + const database = await Database.getInstance(); + this.db = database.db; + } + + async add(data) { + try { + const newId = uuidv4(); + data.id = newId; + const keys = Object.keys(data); + const placeholders = keys.map(() => '?').join(', '); + const values = keys.map(key => data[key]); + const sql = `INSERT INTO ${this.tableName} (${keys.join(', ')}) VALUES (${placeholders})`; + await this.db.run(sql, values); + return data; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao adicionar registro' }); + } + } + + async updateById(id, data) { + try { + const keys = Object.keys(data); + if (keys.length === 0) return null; + const setClause = keys.map(key => `${key} = ?`).join(', '); + const values = keys.map(key => data[key]); + values.push(id); + const sql = `UPDATE ${this.tableName} SET ${setClause} WHERE id = ?`; + await this.db.run(sql, values); + const row = await this.db.get(`SELECT * FROM ${this.tableName} WHERE id = ?`, id); + return row; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao atualizar registro' }); + } + } + + async findOne(query, params) { + try { + const row = await this.db.get(query, params); + return row; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao buscar registro' }); + } + } + + async findAll(query, params = []) { + try { + const rows = await this.db.all(query, params); + return rows; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao buscar registros' }); + } + } +} + +module.exports = BaseRepository; diff --git a/backend/src/repositories/history.repository.js b/backend/src/repositories/history.repository.js new file mode 100644 index 0000000000..3c24e3b688 --- /dev/null +++ b/backend/src/repositories/history.repository.js @@ -0,0 +1,34 @@ +const BaseRepository = require('./base.repository'); +const { HttpInternalServerError } = require('../utils/errors/http.error'); + +class HistoryRepository extends BaseRepository { + constructor() { + super('histories'); + } + + async init() { + await super.init(); + } + + async getHistoryByUserId(userId) { + try { + const sql = `SELECT * FROM histories WHERE userId = ?`; + const rows = await this.db.all(sql, [userId]); + return rows; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao buscar histórico' }); + } + } + + async getHistoryItem(userId, videoId) { + try { + const sql = `SELECT * FROM histories WHERE userId = ? AND videoId = ?`; + const row = await this.db.get(sql, [userId, videoId]); + return row; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao buscar item de histórico' }); + } + } +} + +module.exports = HistoryRepository; diff --git a/backend/src/repositories/list.repository.js b/backend/src/repositories/list.repository.js new file mode 100644 index 0000000000..8d623e07a0 --- /dev/null +++ b/backend/src/repositories/list.repository.js @@ -0,0 +1,54 @@ +const BaseRepository = require('./base.repository'); +const { HttpInternalServerError } = require('../utils/errors/http.error'); + +class ListRepository extends BaseRepository { + constructor() { + super('lists'); + } + + async init() { + await super.init(); + } + + async addList(list) { + try { + const sql = `INSERT INTO lists (titulo, videoIds, userId, id) VALUES (?, ?, ?, ?)`; + const result = await this.db.run(sql, [list.titulo, list.videoIds, list.userId, list.id]); + return result; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao adicionar lista' }); + } + } + + async getListById(id) { + try { + const sql = `SELECT * FROM lists WHERE id = ?`; + const result = await this.db.get(sql, [id]); + return result; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao buscar lista' }); + } + } + + async updateList(list) { + try { + const sql = `UPDATE lists SET titulo = ?, videoIds = ?, userId = ? WHERE id = ?`; + const result = await this.db.run(sql, [list.titulo, list.videoIds, list.userId, list.id]); + return result; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao atualizar lista' }); + } + } + + async deleteList(id) { + try { + const sql = `DELETE FROM lists WHERE id = ?`; + const result = await this.db.run(sql, [id]); + return result; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao deletar lista' }); + } + } +} + +module.exports = ListRepository; diff --git a/backend/src/repositories/other.repository.js b/backend/src/repositories/other.repository.js new file mode 100644 index 0000000000..3312d7f284 --- /dev/null +++ b/backend/src/repositories/other.repository.js @@ -0,0 +1,34 @@ +const BaseRepository = require('./base.repository'); +const TestEntity = require('../entities/test.entity'); + +class OtherRepository extends BaseRepository { + constructor() { + super('tests'); + } + + async getTests() { + return await this.findAll(); + } +} + +module.exports = OtherRepository; + + + + + + +// import TestEntity from '../entities/test.entity'; +// import BaseRepository from './base.repository'; + +// class OtherRepository extends BaseRepository { +// constructor() { +// super('tests'); +// } + +// public async getTests(): Promise { +// return await this.findAll(); +// } +// } + +// export default OtherRepository; diff --git a/backend/src/repositories/video.repository.js b/backend/src/repositories/video.repository.js new file mode 100644 index 0000000000..9c2607f844 --- /dev/null +++ b/backend/src/repositories/video.repository.js @@ -0,0 +1,24 @@ +const BaseRepository = require('./base.repository'); +const { HttpInternalServerError } = require('../utils/errors/http.error'); + +class VideoRepository extends BaseRepository { + constructor() { + super('videos'); + } + + async init() { + await super.init(); + } + + async getVideoByVideoId(videoId) { + try { + const sql = `SELECT * FROM videos WHERE videoId = ?`; + const row = await this.db.get(sql, [videoId]); + return row; + } catch (e) { + throw new HttpInternalServerError({ msg: 'Erro ao buscar vídeo' }); + } + } +} + +module.exports = VideoRepository; diff --git a/backend/src/routes/auth.routes.js b/backend/src/routes/auth.routes.js new file mode 100644 index 0000000000..969de0d0f7 --- /dev/null +++ b/backend/src/routes/auth.routes.js @@ -0,0 +1,13 @@ +import express from "express"; +import { signup, login, logout} from '../controllers/auth.controllers.js'; + +const router = express.Router(); + +// POST +router.post('/signup', signup) +router.post('/login', login); + +// DELETE +router.delete('/logout', logout); + +export default router \ No newline at end of file diff --git a/backend/src/routes/filmes.routes.js b/backend/src/routes/filmes.routes.js new file mode 100644 index 0000000000..ef6c1dcad7 --- /dev/null +++ b/backend/src/routes/filmes.routes.js @@ -0,0 +1,21 @@ +import express from 'express'; +import { getAcaoJson,getSuspenseJson, getAventuraJson,getTerrorJson,getDramaJson, getComediaJson,getRomanceJson, getDiretorJson} from '../controllers/filmes.controllers.js'; + +const router = express.Router() + + +//GET +router.get('/acao', getAcaoJson) +router.get('/aventura', getAventuraJson) +router.get('/terror', getTerrorJson) +router.get('/drama', getDramaJson) +router.get('/comedia', getComediaJson) +router.get('/romance', getRomanceJson) +router.get('/suspense', getSuspenseJson) + + + + + + +export default router diff --git a/backend/src/routes/history.routes.js b/backend/src/routes/history.routes.js new file mode 100644 index 0000000000..f7651437a4 --- /dev/null +++ b/backend/src/routes/history.routes.js @@ -0,0 +1,10 @@ +const { Router } = require('express'); +const HistoryController = require('../controllers/history.controller'); +const { di } = require('../di'); +const HistoryService = require('../services/history.service'); + +const router = Router(); +const historyService = di.getService(HistoryService); +new HistoryController(router, historyService); + +module.exports = router; diff --git a/backend/src/routes/index.js b/backend/src/routes/index.js new file mode 100644 index 0000000000..158a41e9db --- /dev/null +++ b/backend/src/routes/index.js @@ -0,0 +1,44 @@ +<<<<<<< HEAD +const { Router } = require('express'); +const { di } = require('../di'); +const TestController = require('../controllers/test.controller'); +const TestService = require('../services/test.service'); + +const router = Router(); +const prefix = '/api'; + +module.exports = (app) => { + app.use( + prefix, + new TestController(router, di.getService(TestService)).router + ); +}; + + + + +// import { Express, Router } from 'express'; +// import { di } from '../di'; +// import TestController from '../controllers/test.controller'; +// import TestService from '../services/test.service'; + +// const router = Router(); +// const prefix = '/api'; + +// export default (app: Express) => { +// app.use( +// prefix, +// new TestController(router, di.getService(TestService)).router +// ); +// }; +======= +module.exports = (app) => { + const historyRoutes = require('./history.routes'); + const videoRoutes = require('./video.routes'); + const listRoutes = require('./list.routes'); + + app.use('/api', historyRoutes); + app.use('/api', videoRoutes); + app.use('/api', listRoutes); +}; +>>>>>>> main diff --git a/backend/src/routes/like.routes.js b/backend/src/routes/like.routes.js new file mode 100644 index 0000000000..733f5fffc7 --- /dev/null +++ b/backend/src/routes/like.routes.js @@ -0,0 +1,10 @@ +import express from 'express'; +import { seriesCurtidas, curtir, descurtir } from '../controllers/like.controllers.js'; + +const router = express.Router(); + +router.get('/seriesCurtidas/:userid', seriesCurtidas); +router.put('/curtir/:userid', curtir); +router.put('/descurtir/:userid', descurtir); + +export default router; diff --git a/backend/src/routes/list.routes.js b/backend/src/routes/list.routes.js new file mode 100644 index 0000000000..6965febbfa --- /dev/null +++ b/backend/src/routes/list.routes.js @@ -0,0 +1,10 @@ +const { Router } = require('express'); +const ListController = require('../controllers/list.controller'); +const { di } = require('../di'); +const ListService = require('../services/list.service'); + +const router = Router(); +const listService = di.getService(ListService); +new ListController(router, listService); + +module.exports = router; diff --git a/backend/src/routes/recomendations.routes.js b/backend/src/routes/recomendations.routes.js new file mode 100644 index 0000000000..4949027603 --- /dev/null +++ b/backend/src/routes/recomendations.routes.js @@ -0,0 +1,10 @@ +import express from 'express'; +import { top10, recomendacaoGenero, recomendacaoGeral } from '../controllers/recomendations.controllers.js'; + +const router = express.Router(); + +router.get('/top10/Sistema', top10); +router.get('/Sistema/series/:generoid', recomendacaoGenero); +router.get('/Sistema/series', recomendacaoGeral); + +export default router; diff --git a/backend/src/routes/users.routes.js b/backend/src/routes/users.routes.js new file mode 100644 index 0000000000..2c151b983e --- /dev/null +++ b/backend/src/routes/users.routes.js @@ -0,0 +1,18 @@ +import express from "express"; +import { getAll, getUserById, updateUser, deleteUser } from '../controllers/user.controllers.js'; + +const router = express.Router(); + +/* */ + +// GET +router.get('/', getAll); +router.get('/:userId', getUserById); + +// PUT +router.put('/:userId', updateUser); + +// DELETE +router.delete('/:userId', deleteUser); + +export default router \ No newline at end of file diff --git a/backend/src/routes/video.routes.js b/backend/src/routes/video.routes.js new file mode 100644 index 0000000000..5e36ae3a50 --- /dev/null +++ b/backend/src/routes/video.routes.js @@ -0,0 +1,10 @@ +const { Router } = require('express'); +const VideoController = require('../controllers/video.controller'); +const { di } = require('../di'); +const VideoService = require('../services/video.service'); + +const router = Router(); +const videoService = di.getService(VideoService); +new VideoController(router, videoService); + +module.exports = router; diff --git a/backend/src/samples/users.json b/backend/src/samples/users.json new file mode 100644 index 0000000000..4f88fdc672 --- /dev/null +++ b/backend/src/samples/users.json @@ -0,0 +1,20 @@ +[ + { + "id": "1", + "fullName": "Felipe Henrique", + "username": "felipehenrique2", + "birth_date": "20/05/1994", + "gender": "M", + "photo": "felipe_image.jpg", + "password": "$2b$10$cxnUWvdJ6RoWi2JNeWGf5OrVAmQhioEm1euJso8q68U6yXV5q7zL." + }, + { + "id": "2", + "fullName": "Ester Marques", + "username": "estermarques", + "birth_date": "10/05/2004", + "gender": "F", + "photo": "ester_photo.jpg", + "password": "$2b$10$26Nm4t7RSBgCSfTQpVRtUuAheoQgoAb.yl29IYbsqL5BsCdLBGBM6" + } +] \ No newline at end of file diff --git a/backend/src/services/history.service.js b/backend/src/services/history.service.js new file mode 100644 index 0000000000..3f223034a7 --- /dev/null +++ b/backend/src/services/history.service.js @@ -0,0 +1,44 @@ +const HistoryItemEntity = require('../entities/history.item.entity'); +const { HttpNotFoundError } = require('../utils/errors/http.error'); + +class HistoryService { + constructor(historyRepository) { + this.historyRepository = historyRepository; + this.validUsers = new Set(['1', '2', '3']); + } + + async getHistory(userId) { + if (!this.validUsers.has(userId)) { + throw new HttpNotFoundError({ msg: 'Usuário não encontrado', msgCode: 'user_not_found' }); + } + const history = await this.historyRepository.getHistoryByUserId(userId); + return { code: 200, data: history }; + } + + async addOrUpdateHistory(userId, videoData) { + if (!this.validUsers.has(userId)) { + throw new HttpNotFoundError({ msg: 'Usuário não encontrado', msgCode: 'user_not_found' }); + } + const now = new Date().toISOString(); + const existing = await this.historyRepository.getHistoryItem(userId, videoData.videoId); + let msg, code; + if (existing) { + await this.historyRepository.updateById(existing.id, { ultimaVisualizacao: now }); + msg = 'Data de visualização atualizada'; + code = 200; + } else { + const newItem = new HistoryItemEntity({ + userId, + videoId: videoData.videoId, + ultimaVisualizacao: now, + }); + await this.historyRepository.add(newItem); + msg = 'Vídeo adicionado ao histórico'; + code = 201; + } + const history = await this.historyRepository.getHistoryByUserId(userId); + return { code, msg, data: history }; + } +} + +module.exports = HistoryService; diff --git a/backend/src/services/list.service.js b/backend/src/services/list.service.js new file mode 100644 index 0000000000..23128786b9 --- /dev/null +++ b/backend/src/services/list.service.js @@ -0,0 +1,91 @@ +const { HttpBadRequestError, HttpNotFoundError } = require('../utils/errors/http.error'); +const ListEntity = require('../entities/list.entity'); +const DatabaseUtils = require('../utils/database'); + +class ListService { + constructor(listRepository) { + this.listRepository = listRepository; + } + + async addList(titulo, userId) { + if (!titulo || !userId) { + throw new HttpBadRequestError('Título e userId são obrigatórios'); + } + + const listData = { + id: DatabaseUtils.generateUUID(), + videoIds: JSON.stringify([]), + titulo, + userId + } + + const list = new ListEntity(listData); + return await this.listRepository.addList(list); + } + + async addVideoToList(videoId, id) { + if (!videoId || !id) { + throw new HttpBadRequestError('videoId e id são obrigatórios'); + } + + const list = await this.listRepository.getListById(id); + + if (!list) { + throw new HttpBadRequestError('Lista não encontrada'); + } + + const videoIds = JSON.parse(list.videoIds); + + if (videoIds.includes(videoId)) { + throw new HttpBadRequestError('Vídeo já está na lista'); + } + + videoIds.push(videoId); + + const updatedList = { + ...list, + videoIds: JSON.stringify(videoIds), + } + + return await this.listRepository.updateList(updatedList); + } + async removeVideoFromList(videoId, id) { + if (!videoId || !id) { + throw new HttpBadRequestError('videoId e id são obrigatórios'); + } + + const list = await this.listRepository.getListById(id); + + if (!list) { + throw new HttpNotFoundError('Lista não encontrada'); + } + + const videoIds = JSON.parse(list.videoIds); + + if (!videoIds.includes(videoId)) { + throw new HttpNotFoundError('Vídeo não encontrado na lista'); + } + videoIds.pop(videoId); + const updatedList = { + ...list, + videoIds: JSON.stringify(videoIds), + } + return await this.listRepository.updateList(updatedList); + } + + async deleteList(id) { + if (!id) { + throw new HttpBadRequestError('id é obrigatório'); + } + + const list = await this.listRepository.getListById(id); + + if (!list) { + throw new HttpNotFoundError('Lista não encontrada'); + } + + return await this.listRepository.deleteList(id); + } +} + +module.exports = ListService; diff --git a/backend/src/services/test.service.js b/backend/src/services/test.service.js new file mode 100644 index 0000000000..6f3fe40fc5 --- /dev/null +++ b/backend/src/services/test.service.js @@ -0,0 +1,151 @@ +const TestEntity = require('../entities/test.entity'); +const TestModel = require('../models/test.model'); +const OtherRepository = require('../repositories/other.repository'); +const TestRepository = require('../repositories/test.repository'); +const { HttpNotFoundError } = require('../utils/errors/http.error'); + +class TestServiceMessageCode { + static test_not_found = 'test_not_found'; +} + +class TestService { + constructor(testRepository, otherRepository) { + this.testRepository = testRepository; + this.otherRepository = otherRepository; + } + + async getTests() { + const testsEntity = await this.testRepository.getTests(); + const testsModel = testsEntity.map((test) => new TestModel(test)); + return testsModel; + } + + async getOtherTests() { + const testsEntity = await this.otherRepository.getTests(); + const testsModel = testsEntity.map((test) => new TestModel(test)); + return testsModel; + } + + async getTest(id) { + const testEntity = await this.testRepository.getTest(id); + + if (!testEntity) { + throw new HttpNotFoundError({ + msg: 'Test not found', + msgCode: TestServiceMessageCode.test_not_found, + }); + } + + const testModel = new TestModel(testEntity); + return testModel; + } + + async createTest(data) { + const testEntity = await this.testRepository.createTest(data); + const testModel = new TestModel(testEntity); + return testModel; + } + + async updateTest(id, data) { + const testEntity = await this.testRepository.updateTest(id, data); + + if (!testEntity) { + throw new HttpNotFoundError({ + msg: 'Test not found', + msgCode: TestServiceMessageCode.test_not_found, + }); + } + + const testModel = new TestModel(testEntity); + return testModel; + } + + async deleteTest(id) { + await this.testRepository.deleteTest(id); + } +} + +module.exports = TestService; + + +// import TestEntity from '../entities/test.entity'; +// import TestModel from '../models/test.model'; +// import OtherRepository from '../repositories/other.repository'; +// import TestRepository from '../repositories/test.repository'; +// import { HttpNotFoundError } from '../utils/errors/http.error'; + +// class TestServiceMessageCode { +// public static readonly test_not_found = 'test_not_found'; +// } + +// class TestService { +// private testRepository: TestRepository; +// private otherRepository: OtherRepository; + +// constructor( +// testRepository: TestRepository, +// otherRepository: OtherRepository +// ) { +// this.testRepository = testRepository; +// this.otherRepository = otherRepository; +// } + +// public async getTests(): Promise { +// const testsEntity = await this.testRepository.getTests(); + +// const testsModel = testsEntity.map((test) => new TestModel(test)); + +// return testsModel; +// } + +// public async getOtherTests(): Promise { +// const testsEntity = await this.otherRepository.getTests(); + +// const testsModel = testsEntity.map((test) => new TestModel(test)); + +// return testsModel; +// } + +// public async getTest(id: string): Promise { +// const testEntity = await this.testRepository.getTest(id); + +// if (!testEntity) { +// throw new HttpNotFoundError({ +// msg: 'Test not found', +// msgCode: TestServiceMessageCode.test_not_found, +// }); +// } + +// const testModel = new TestModel(testEntity); + +// return testModel; +// } + +// public async createTest(data: TestEntity): Promise { +// const testEntity = await this.testRepository.createTest(data); +// const testModel = new TestModel(testEntity); + +// return testModel; +// } + +// public async updateTest(id: string, data: TestEntity): Promise { +// const testEntity = await this.testRepository.updateTest(id, data); + +// if (!testEntity) { +// throw new HttpNotFoundError({ +// msg: 'Test not found', +// msgCode: TestServiceMessageCode.test_not_found, +// }); +// } + +// const testModel = new TestModel(testEntity); + +// return testModel; +// } + +// public async deleteTest(id: string): Promise { +// await this.testRepository.deleteTest(id); +// } +// } + +// export default TestService; diff --git a/backend/src/services/video.service.js b/backend/src/services/video.service.js new file mode 100644 index 0000000000..35b0dc91e8 --- /dev/null +++ b/backend/src/services/video.service.js @@ -0,0 +1,31 @@ +const VideoRepository = require('../repositories/video.repository'); +const { HttpNotFoundError } = require('../utils/errors/http.error'); +const VideoEntity = require('../entities/video.entity'); + +class VideoService { + constructor(videoRepository) { + this.videoRepository = videoRepository; + } + + async getVideo(videoId) { + const video = await this.videoRepository.getVideoByVideoId(videoId); + if (!video) { + throw new HttpNotFoundError({ msg: 'Vídeo não encontrado', msgCode: 'video_not_found' }); + } + return new VideoEntity(video); + } + + async registerView(videoId, userId) { + const video = await this.videoRepository.getVideoByVideoId(videoId); + if (!video) { + throw new HttpNotFoundError({ msg: 'Vídeo não encontrado', msgCode: 'video_not_found' }); + } + await this.videoRepository.updateById(video.id, { views: video.views + 1 }); + return { + code: 201, + data: { message: 'Visualização registrada com sucesso', videoId: video.videoId, userId }, + }; + } +} + +module.exports = VideoService; diff --git a/backend/src/utils/database.js b/backend/src/utils/database.js new file mode 100644 index 0000000000..128a685984 --- /dev/null +++ b/backend/src/utils/database.js @@ -0,0 +1,11 @@ +class DatabaseUtils { + static generateUUID() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + const r = Math.random() * 16 | 0; + const v = c === 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } +} + +module.exports = DatabaseUtils; diff --git a/backend/src/utils/errors/http.error.js b/backend/src/utils/errors/http.error.js new file mode 100644 index 0000000000..9d534ad919 --- /dev/null +++ b/backend/src/utils/errors/http.error.js @@ -0,0 +1,157 @@ +class HttpError extends Error { + constructor({ status, msg, msgCode, slug }) { + super(msg); + this.msg = msg; + this.status = status; + this.msgCode = msgCode || 'failure'; + this.slug = slug || 'http-error'; + } + + toString() { + return `[${this.name}]: msg: ${this.msg}, msgCode: ${this.msgCode}, status: ${this.status}, stack: ${this.stack}`; + } +} + +class HttpBadRequestError extends HttpError { + constructor({ msg, msgCode }) { + super({ status: 400, msg, msgCode, slug: 'bad-request' }); + } +} + +class HttpUnauthorizedError extends HttpError { + constructor({ msg, msgCode }) { + super({ status: 401, msg, msgCode, slug: 'unauthorized' }); + } +} + +class HttpForbiddenError extends HttpError { + constructor({ msg, msgCode }) { + super({ status: 403, msg, msgCode, slug: 'forbidden' }); + } +} + +class HttpNotFoundError extends HttpError { + constructor({ msg, msgCode }) { + super({ status: 404, msg, msgCode, slug: 'not-found' }); + } +} + +class HttpInternalServerError extends HttpError { + constructor({ msg, msgCode } = {}) { +<<<<<<< HEAD + super({ + status: 500, + msg: msg || 'Internal Server Error', + msgCode, + slug: 'internal-server', + }); +======= + super({ status: 500, msg: msg || 'Internal Server Error', msgCode, slug: 'internal-server' }); +>>>>>>> main + } +} + +class HttpNotImplementedError extends HttpError { + constructor({ msg, msgCode } = {}) { +<<<<<<< HEAD + super({ + status: 501, + msg: msg || 'Not Implemented Error', + msgCode, + slug: 'not-implemented', + }); +======= + super({ status: 501, msg: msg || 'Not Implemented Error', msgCode, slug: 'not-implemented' }); +>>>>>>> main + } +} + +module.exports = { + HttpError, + HttpBadRequestError, + HttpUnauthorizedError, + HttpForbiddenError, + HttpNotFoundError, + HttpInternalServerError, + HttpNotImplementedError, +}; +<<<<<<< HEAD + + + +// export abstract class HttpError extends Error { +// msg: string; +// status: number; +// msgCode: any; +// slug: string; + +// constructor({ +// status, +// msg, +// msgCode, +// slug, +// }: { +// status: number; +// msg: string; +// msgCode?: any; +// slug?: string; +// }) { +// super(msg); +// this.msg = msg; +// this.status = status; +// this.msgCode = msgCode || 'failure'; +// this.slug = slug || 'http-error'; +// } + +// toString() { +// return `[${this.name}]: msg: ${this.msg}, msgCode: ${this.msgCode}, status: ${this.status}, stack: ${this.stack}`; +// } +// } + +// export class HttpBadRequestError extends HttpError { +// constructor({ msg, msgCode }: { msg: string; msgCode?: any }) { +// super({ status: 400, msg, msgCode, slug: 'bad-request' }); +// } +// } + +// export class HttpUnauthorizedError extends HttpError { +// constructor({ msg, msgCode }: { msg: string; msgCode?: any }) { +// super({ status: 401, msg, msgCode, slug: 'unauthorized' }); +// } +// } + +// export class HttpForbiddenError extends HttpError { +// constructor({ msg, msgCode }: { msg: string; msgCode?: any }) { +// super({ status: 403, msg, msgCode, slug: 'forbidden' }); +// } +// } + +// export class HttpNotFoundError extends HttpError { +// constructor({ msg, msgCode }: { msg: string; msgCode?: any }) { +// super({ status: 404, msg, msgCode, slug: 'not-found' }); +// } +// } + +// export class HttpInternalServerError extends HttpError { +// constructor({ msg, msgCode }: { msg?: string; msgCode?: any } = {}) { +// super({ +// status: 500, +// msg: msg || 'Internal Server Error', +// msgCode, +// slug: 'internal-server', +// }); +// } +// } + +// export class HttpNotImplementedError extends HttpError { +// constructor({ msg, msgCode }: { msg?: string; msgCode?: any } = {}) { +// super({ +// status: 501, +// msg: msg || 'Not Implemented Error', +// msgCode, +// slug: 'not-implemented', +// }); +// } +// } +======= +>>>>>>> main diff --git a/backend/src/utils/generateToken.js b/backend/src/utils/generateToken.js new file mode 100644 index 0000000000..935be7c2a1 --- /dev/null +++ b/backend/src/utils/generateToken.js @@ -0,0 +1,18 @@ +import jwt from 'jsonwebtoken' +import dotenv from 'dotenv' +dotenv.config() + +const generateTokenAndSetCookie = (userId, res) => { + const token = jwt.sign({ userId }, "okasok83847jmfdkmas29-90d", { + expiresIn: '15d', + }); + + res.cookie("jwt", token, { + maxAge: 15 * 24 * 60 * 60 * 1000, // MS + httpOnly: true, // prevent XSS attacks cross-site scripting attacks + sameSite: "strict", // CSRF attacks cross-site requesst fogery attacks + secure: process.env.NODE_ENV !== 'development' + }); +} + +export default generateTokenAndSetCookie; \ No newline at end of file diff --git a/backend/src/utils/result.js b/backend/src/utils/result.js new file mode 100644 index 0000000000..aae6bc051d --- /dev/null +++ b/backend/src/utils/result.js @@ -0,0 +1,135 @@ +class Result { + constructor({ msg, msgCode, code }) { + this.msg = msg; + this.msgCode = msgCode; + this.code = code; + } + + static transformRequestOnMsg(req) { + return `${req.method} ${req.originalUrl}`; + } +} + +class SuccessResult extends Result { + constructor({ msg, msgCode, code, data }) { +<<<<<<< HEAD + super({ + msg, + msgCode: msgCode || 'success', + code: code || 200, + }); +======= + super({ msg, msgCode: msgCode || 'success', code: code || 200 }); +>>>>>>> main + this.data = data; + } + + handle(res) { +<<<<<<< HEAD + return res.status(this.code).send(this); + } +} + +class FailureResult extends Result { + constructor({ msg, msgCode, code }) { + super({ + msg, + msgCode: msgCode || 'failure', + code: code || 500, + }); +======= + return res.status(this.code).json({ + msg: this.msg, + msgCode: this.msgCode, + code: this.code, + data: this.data, + }); + } +} + + +class FailureResult extends Result { + constructor({ msg, msgCode, code }) { + super({ msg, msgCode: msgCode || 'failure', code: code || 500 }); +>>>>>>> main + } + + handle(res) { + return res.status(this.code).send(this); + } +} + +<<<<<<< HEAD +module.exports = { SuccessResult, FailureResult }; + + + +// import { Request, Response } from 'express'; + +// export abstract class Result { +// msg: string; +// msgCode: any; +// code: number; + +// constructor({ +// msg, +// msgCode, +// code, +// }: { +// msg: string; +// msgCode: any; +// code: number; +// }) { +// this.msg = msg; +// this.msgCode = msgCode; +// this.code = code; +// } + +// static transformRequestOnMsg(req: Request) { +// return `${req.method} ${req?.originalUrl}`; +// } +// } + +// export class SuccessResult extends Result { +// data?: any; + +// constructor({ +// msg, +// msgCode, +// code, +// data, +// }: { +// msg: string; +// msgCode?: any; +// code?: number; +// data?: any; +// }) { +// super({ msg, msgCode: msgCode || 'success', code: code || 200 }); +// this.data = data; +// } + +// handle(res: Response) { +// return res.status(this.code).send(this); +// } +// } + +// export class FailureResult extends Result { +// constructor({ +// msg, +// msgCode, +// code, +// }: { +// msg: string; +// msgCode?: any; +// code?: number; +// }) { +// super({ msg, msgCode: msgCode || 'failure', code: code || 500 }); +// } + +// handle(res: Response) { +// return res.status(this.code).send(this); +// } +// } +======= +module.exports = { Result, SuccessResult, FailureResult }; +>>>>>>> main diff --git a/backend/tests/controllers/likeTest.step.js b/backend/tests/controllers/likeTest.step.js new file mode 100644 index 0000000000..27f209d157 --- /dev/null +++ b/backend/tests/controllers/likeTest.step.js @@ -0,0 +1,111 @@ +import fs from 'fs' +import app from "../../src/app"; +const supertest = require('supertest'); +const request = supertest(app); +const { loadFeature, defineFeature } = require('jest-cucumber'); +const feature = loadFeature('tests/features/like.feature'); + +let server; +let response; +let userData; +let usersData; + +beforeAll((done) => { + server = app.listen(4000, () => { + done(); + }); +}); + +afterAll(async () => { + server.close(() => {}); +}); + + +defineFeature(feature, (test) => { + + test('Obter a lista de séries curtidas pelo usuário', ({ given, when, then, and }) => { + + given(/^O usuário "(.*)" está logado no sistema$/, (username) => { + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData.user).toEqual(username); + }); + + when(/^Uma requisição "(.*)" é enviada para "(.*)"$/, async (method, route) => { + response = await request + .get(route); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^O JSON da resposta contém o item "(.*)"$/, (serie) => { + expect(response.body).toContain(serie); + }); + }); + + test('Adicionar item a lista de "Séries Curtidas"', ({ given, and, when, then }) => { + + given(/^A lista "(.*)" do usuário "(.*)" contém o item "(.*)"$/, (list, username, item) => { + + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData[list]).toContain(item); + }); + + + when(/^Uma requisição "(.*)" é enviada para "(.*)" com o item "(.*)"$/, async (method, route, serie) => { + response = await request + .put(route) + .send({ serie }); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^A lista "(.*)" do usuário "(.*)" contém os itens "(.*)" e "(.*)"$/, (list, username, item1, item2) => { + + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData[list]).toEqual(expect.arrayContaining([item1, item2])); + request.put("/user/descurtir/Ykaro").send({ item2 }); + + }); + }); + + test('Remover item da lista de "Séries Curtidas"', ({ given, when, then, and }) => { + + given(/^A lista "(.*)" do usuário "(.*)" contém os itens "(.*)" e "(.*)"$/, (list, username, item1, item2) => { + + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData[list]).toEqual(expect.arrayContaining([item1, item2])); + + }); + + when(/^Uma requisição "(.*)" é enviada para "(.*)" com o item "(.*)"$/, async (method, route, serie) => { + response = await request + .put(route) + .send({ serie }); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^A lista "(.*)" do usuário "(.*)" contém o item "(.*)"$/, (list, username, item) => { + + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData[list]).toContain(item); + }); + }); +}); + diff --git a/backend/tests/controllers/recomendationLikeTest.step.js b/backend/tests/controllers/recomendationLikeTest.step.js new file mode 100644 index 0000000000..8744e087c6 --- /dev/null +++ b/backend/tests/controllers/recomendationLikeTest.step.js @@ -0,0 +1,50 @@ +import fs from 'fs' +import app from "../../src/app"; +const supertest = require('supertest'); +const request = supertest(app); +const { loadFeature, defineFeature } = require('jest-cucumber'); +const feature = loadFeature('tests/features/recomendation_like.feature'); + +let server; +let response; +let userData; +let usersData; + +beforeAll((done) => { + server = app.listen(4000, () => { + done(); + }); +}); + +afterAll(async () => { + server.close(() => {}); +}); + + +defineFeature(feature, (test) => { + + test('Exibindo recomendações com base nas curtidas', ({ given, when, then, and }) => { + + given(/^O usuário "(.*)" está logado no sistema$/, (username) => { + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData.user).toEqual(username); + }); + + when(/^Uma requisição "(.*)" é enviada para "(.*)"$/, async (method, route) => { + response = await request + .get(route); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^O JSON da resposta contém a lista "(.*)" com os itens "(.*)", "(.*)", "(.*)", "(.*)", "(.*)", "(.*)", "(.*)", "(.*)", "(.*)", "(.*)"$/, (list, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10) => { + expect(response.body).toContain(item1, item2, item3, item4, item5, item6, item7, item8, item9, item10); + }); + }); + +}); + diff --git a/backend/tests/controllers/recomendationViewTest.step.js b/backend/tests/controllers/recomendationViewTest.step.js new file mode 100644 index 0000000000..525ebfef98 --- /dev/null +++ b/backend/tests/controllers/recomendationViewTest.step.js @@ -0,0 +1,127 @@ +import fs from 'fs' +import app from "../../src/app"; +const supertest = require('supertest'); +const request = supertest(app); +const { loadFeature, defineFeature } = require('jest-cucumber'); +const feature = loadFeature('tests/features/recomendation_view.feature'); + +let server; +let response; +let userData; +let usersData; + +beforeAll((done) => { + server = app.listen(4000, () => { + done(); + }); +}); + +afterAll(async () => { + server.close(() => {}); +}); + +defineFeature(feature, (test) => { + + test('Exibindo recomendações para o usuário com histórico de visualizações (gênero 1) (Service)', ({ given, when, then, and }) => { + + given(/^A lista "(.*)" do usuário "(.*)" contém os itens "(.*)", "(.*)"$/, (list, username, item1, item2) => { + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData).toBeDefined(); + expect(userData.user).toEqual(username); + + // Verifica se a lista existe + expect(userData).toHaveProperty(list); + const userList = userData[list]; + + // Verifica se a lista é um array + expect(Array.isArray(userList)).toBe(true); + + // Verifica se os itens estão na lista + expect(userList).toContain(item1); + expect(userList).toContain(item2); + }); + + when(/^Uma requisição "(.*)" é enviada para "(.*)"$/, async (method, route) => { + response = await request + .get(route); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^O JSON da resposta contém os itens "(.*)", "(.*)"$/, (item1, item2) => { + expect(response.body).toContain(item1, item2); + }); + }); + + test('Exibindo recomendações para o usuário com histórico de visualizações (gênero 2) (Service)', ({ given, when, then, and }) => { + + given(/^A lista "(.*)" do usuário "(.*)" contém o item "(.*)"$/, (list, username, item) => { + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData).toBeDefined(); + expect(userData.user).toEqual(username); + + // Verifica se a lista existe + expect(userData).toHaveProperty(list); + const userList = userData[list]; + + // Verifica se a lista é um array + expect(Array.isArray(userList)).toBe(true); + + // Verifica se o item está na lista + expect(userList).toContain(item); + }); + + when(/^Uma requisição "(.*)" é enviada para "(.*)"$/, async (method, route) => { + response = await request + .get(route); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^O JSON da resposta contém os itens "(.*)", "(.*)"$/, (item1, item2) => { + expect(response.body).toContain(item1, item2); + }); + }); + + test('Exibindo recomendações para o usuário sem histórico de visualizações (Service)', ({ given, when, then, and }) => { + + given(/^A lista "(.*)" do usuário "(.*)" está vazia$/, (list, username) => { + usersData = JSON.parse(fs.readFileSync('src/database/users.json', 'utf8')); + userData = usersData.find(user => user.user === username); + + expect(userData).toBeDefined(); + expect(userData.user).toEqual(username); + + // Verifica se a lista existe + expect(userData).toHaveProperty(list); + const userList = userData[list]; + + // Verifica se a lista é um array + expect(Array.isArray(userList)).toBe(true); + + // Verifica se o item está na lista + expect(userList.length).toBe(0); + }); + + when(/^Uma requisição "(.*)" é enviada para "(.*)"$/, async (method, route) => { + response = await request + .get(route); + }); + + then(/^O status da resposta deve ser "(.*)"$/, (statusCode) => { + expect(response.status).toBe(parseInt(statusCode, 10)); + }); + + and(/^O JSON da resposta contém os itens "(.*)", "(.*)"$/, (item1, item2) => { + expect(response.body).toContain(item1, item2); + }); + }); +}); \ No newline at end of file diff --git a/backend/tests/controllers/signup.controller.steps.js b/backend/tests/controllers/signup.controller.steps.js new file mode 100644 index 0000000000..22042fdb62 --- /dev/null +++ b/backend/tests/controllers/signup.controller.steps.js @@ -0,0 +1,42 @@ +const { Given, When, Then } = require('jest-cucumber'); +const request = require('supertest'); +const app = require('../../src/index'); // Certifique-se de que o caminho está correto +const fs = require('fs'); +const path = require('path'); + +// Step: Garantir que não há usuário com o nome de usuário 'LucasHenrique' +Given('não há usuário cadastrado no sistema com usuário {string}', (username) => { + const data = JSON.parse(fs.readFileSync(path.resolve('./samples/users.json'), 'utf-8')); + const user = data.find(u => u.username === username); + if (user) { + // Remover o usuário para garantir que o cadastro funcione no teste + const updatedData = data.filter(u => u.username !== username); + fs.writeFileSync(path.resolve('./samples/users.json'), JSON.stringify(updatedData, null, 2)); + } +}); + +// Step: Fazer a requisição POST para criar o usuário +When('Faço uma Requisição POST para a rota "/users" com os seguintes dados:', async (dataTable) => { + const userData = dataTable.rowsHash(); + // Remover o campo 'Foto' pois não estamos lidando com upload de imagens em testes simples + const { Foto, ...userWithoutFoto } = userData; + + this.response = await request(app) + .post('/users') + .send({ + ...userWithoutFoto, + photo: Foto, + confirmPassword: userData.Senha, // Enviar o campo confirmPassword para testar a verificação de senha + }); +}); + +// Step: Verificar se o usuário foi criado com sucesso +Then('o usuário deve ser cadastrado com sucesso', () => { + const { status, body } = this.response; + expect(status).toBe(201); + expect(body).toHaveProperty('id'); + expect(body.fullName).toBe('Lucas Henrique'); + expect(body.username).toBe('LucasHenrique'); + expect(body.birth_date).toBe('20/02/2004'); + expect(body.gender).toBe('Masculino'); +}); diff --git a/backend/tests/features/Minha_Lista_Service.feature b/backend/tests/features/Minha_Lista_Service.feature new file mode 100644 index 0000000000..0c4c22f231 --- /dev/null +++ b/backend/tests/features/Minha_Lista_Service.feature @@ -0,0 +1,45 @@ +Feature: Serviço de gerenciamento de listas + + As um serviço de gerenciamento de listas + Eu quero permitir a criação, edição e remoção de listas e itens + + Scenario: Criar lista + Given o serviço recebe uma requisição para criar a lista "Favoritos" do usuário "José" + When o serviço processa a requisição + Then o serviço retorna um status 201 + And a lista "Favoritos" é criada com sucesso + + Scenario: Falha ao criar lista duplicada + Given o serviço recebe uma requisição para criar a lista "Favoritos" do usuário "José" + And a lista "Favoritos" já existe + When o serviço processa a requisição + Then o serviço retorna um status 409 + And uma mensagem de erro "Lista já existente" é retornada + + Scenario: Deletar lista + Given o serviço recebe uma requisição para deletar a lista "Favoritos" do usuário "José" + And a lista "Favoritos" existe + When o serviço processa a requisição + Then o serviço retorna um status 200 + And a lista "Favoritos" é removida com sucesso + + Scenario: Adicionar item na lista + Given o serviço recebe uma requisição para adicionar o item "The Batman" na lista "Favoritos" do usuário "José" + And o item "The Batman" não está na lista + When o serviço processa a requisição + Then o serviço retorna um status 201 + And o item "The Batman" é adicionado com sucesso + + Scenario: Falha ao adicionar item duplicado na lista + Given o serviço recebe uma requisição para adicionar o item "The Batman" na lista "Favoritos" do usuário "José" + And o item "The Batman" já está na lista + When o serviço processa a requisição + Then o serviço retorna um status 409 + And uma mensagem de erro "Item já existente na lista" é retornada + + Scenario: Remover item da lista + Given o serviço recebe uma requisição para remover o item "The Batman" da lista "Favoritos" do usuário "José" + And o item "The Batman" existe na lista + When o serviço processa a requisição + Then o serviço retorna um status 200 + And o item "The Batman" é removido com sucesso \ No newline at end of file diff --git a/backend/tests/features/history-api.feature b/backend/tests/features/history-api.feature new file mode 100644 index 0000000000..9a7243ea0f --- /dev/null +++ b/backend/tests/features/history-api.feature @@ -0,0 +1,47 @@ +Feature: Histórico de Visualizações (Serviço) + + Scenario: Obter histórico com sucesso (não vazio) + Given existe um usuário com id "1" cadastrado no sistema + And esse usuário possui os vídeos "101" e "102" no histórico + When faço uma requisição GET para "/users/1/history" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém os vídeos "101" e "102" + + Scenario: Obter histórico vazio + Given existe um usuário com id "2" cadastrado no sistema + And esse usuário não possui nenhum vídeo no histórico + When faço uma requisição GET para "/users/2/history" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém uma lista vazia + + Scenario: Obter histórico de usuário inexistente + Given não existe um usuário com id "999" no sistema + When faço uma requisição GET para "/users/999/history" + Then o status da resposta é "404 Not Found" + And o corpo da resposta (JSON) indica "Usuário não encontrado" + + Scenario: Adicionar vídeo ao histórico + Given existe um usuário com id "3" cadastrado no sistema + And esse usuário não possui o vídeo "201" no histórico + When faço uma requisição PUT para "/users/3/history" com o corpo: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta deve ser "201 Created" + And o corpo da resposta (JSON) contém "Vídeo adicionado ao histórico" + And agora o histórico do usuário com id "3" possui o vídeo "201" + + Scenario: Atualizar histórico para um usuário + Given existe um usuário com id "3" que já possui o vídeo "201" no histórico + When faço uma requisição PUT para "/users/3/history" com o corpo: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta deve ser "200 OK" + And o corpo da resposta (JSON) contém "Data de visualização atualizada" diff --git a/backend/tests/features/history-service.feature b/backend/tests/features/history-service.feature new file mode 100644 index 0000000000..09ea6b5111 --- /dev/null +++ b/backend/tests/features/history-service.feature @@ -0,0 +1,37 @@ +Feature: History Service + + Scenario: Retornar todo o histórico para um usuário + Given o método getHistory do HistoryService retorna um array com os itens de vídeo com videoIds "101", "203" e "402" para o usuário "1" + When o método getHistory do HistoryService for chamado com o id do usuário "1" + Then o array retornado deve conter a lista com os ids "101", "203" e "402" + + Scenario: Retornar item do histórico por videoId para um usuário + Given o método getHistoryItem chamado com "1" para o usuário "1" do HistoryService retorna um item com videoId "101" + When o método getHistoryItem do HistoryService for chamado com o id "101" + Then o item retornado deve ter videoId "101" + + Scenario: Adicionar histórico para um usuário + Given existe um usuário com id "3" cadastrado no sistema + And esse usuário não possui o vídeo "201" no histórico + When o método addOrUpdateHistory do HistoryService for chamado com o id "3" e os dados: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta deve ser "201 Created" + And o corpo da resposta (JSON) contém "Vídeo adicionado ao histórico" + And agora o histórico do usuário com id "3" possui o id "201" + + Scenario: Atualizar histórico para um usuário + Given existe um usuário com id "3" que já possui o vídeo "201" no histórico + When o método addOrUpdateHistory do HistoryService for chamado com o id "3" e os dados: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta deve ser "200 OK" + And o corpo da resposta (JSON) contém "Data de visualização atualizada" diff --git a/backend/tests/features/history.feature b/backend/tests/features/history.feature new file mode 100644 index 0000000000..45ca898d51 --- /dev/null +++ b/backend/tests/features/history.feature @@ -0,0 +1,47 @@ +Feature: Histórico de Visualizações (Serviço) + + Scenario: Obter histórico com sucesso (não vazio) + Given existe um usuário com id "1" cadastrado no sistema + And esse usuário possui os vídeos "101" e "102" no histórico + When faço uma requisição GET para "/users/1/history" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém os vídeos "101" e "102" + + Scenario: Obter histórico vazio + Given existe um usuário com id "2" cadastrado no sistema + And esse usuário não possui nenhum vídeo no histórico + When faço uma requisição GET para "/users/2/history" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém uma lista vazia + + Scenario: Obter histórico de usuário inexistente + Given não existe um usuário com id "999" no sistema + When faço uma requisição GET para "/users/999/history" + Then o status da resposta é "404 Not Found" + And o corpo da resposta (JSON) indica "Usuário não encontrado" + + Scenario: Adicionar vídeo ao histórico + Given existe um usuário com id "3" cadastrado no sistema + And esse usuário não possui o vídeo "201" no histórico + When faço uma requisição PUT para "/users/3/history" com o corpo: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta deve ser "201 Created" + And o corpo da resposta (JSON) contém "Vídeo adicionado ao histórico" + And agora o histórico do usuário com id "3" possui o vídeo "201" + + Scenario: Atualizar data de visualização de um vídeo já existente + Given existe um usuário com id "3" que já possui o vídeo "201" no histórico + When faço uma requisição PUT para "/users/3/history" com o corpo: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta deve ser "200 OK" + And o corpo da resposta (JSON) contém "Data de visualização atualizada" diff --git a/backend/tests/features/like.feature b/backend/tests/features/like.feature new file mode 100644 index 0000000000..ecb456e520 --- /dev/null +++ b/backend/tests/features/like.feature @@ -0,0 +1,24 @@ +Feature: Curtir + As a usuário do sistema + I want to informar à plataforma que gostei de um conteúdo + So that i can receber recomendações semelhantes à aquele conteúdo + +#Apenas os cenários de serviço referentes à feature foram colocados aqui + +Scenario: Obter a lista de séries curtidas pelo usuário + Given O usuário "Ykaro" está logado no sistema + When Uma requisição "GET" é enviada para "/user/seriesCurtidas/Ykaro" + Then O status da resposta deve ser "200" + And O JSON da resposta contém o item "Breaking Bad" + +Scenario: Adicionar item a lista de "Séries Curtidas" + Given A lista "Séries Curtidas" do usuário "Ykaro" contém o item "Breaking Bad" + When Uma requisição "put" é enviada para "/user/curtir/Ykaro" com o item "The Boys" + Then O status da resposta deve ser "200" + And A lista "Séries Curtidas" do usuário "Ykaro" contém os itens "Breaking Bad" e "The Boys" + +Scenario: Remover item da lista de "Séries Curtidas" + Given A lista "Séries Curtidas" do usuário "Ykaro" contém os itens "Breaking Bad" e "The Boys" + When Uma requisição "put" é enviada para "/user/descurtir/Ykaro" com o item "The Boys" + Then O status da resposta deve ser "200" + And A lista "Séries Curtidas" do usuário "Ykaro" contém o item "Breaking Bad" diff --git a/backend/tests/features/recomendation_like.feature b/backend/tests/features/recomendation_like.feature new file mode 100644 index 0000000000..ae29b1031a --- /dev/null +++ b/backend/tests/features/recomendation_like.feature @@ -0,0 +1,12 @@ +Feature: Recomendações com base nas curtidas (Top 10) - Ykaro dos Santos Albuquerque + As a usuário da plataforma + I want to receber recomendções + So that i can receber conteúdo de qualidade + +#Apenas o cenário de seriço relacionado à feature foi colocado aqui + +Scenario: Exibindo recomendações com base nas curtidas + Given O usuário "Ykaro" está logado no sistema + When Uma requisição "GET" é enviada para "/user/top10/Sistema" + Then O status da resposta deve ser "200" + And O JSON da resposta contém a lista "top10" com os itens "The Boys", "Breaking Bad", "The Last of Us", "Marley e Eu", "Vingadores: Guerra Infinita", "The Walking Dead", "The Batman", "Ainda estou aqui", "La La Land", "Valente" diff --git a/backend/tests/features/recomendation_view.feature b/backend/tests/features/recomendation_view.feature new file mode 100644 index 0000000000..49a7ccf44a --- /dev/null +++ b/backend/tests/features/recomendation_view.feature @@ -0,0 +1,26 @@ +Feature: Recomendação com base nas visualizações do usuário + As a usuário da plataforma + I want to receber recomendções que sejam compatíveis com os meus gostos + So that i can aproveitar as recomendações da plataforma + +# Apenas os cenários de serviço referentes à feature foram colocados aqui + +Scenario: Exibindo recomendações para o usuário com histórico de visualizações (gênero 1) (Service) + Given A lista "historicoDeVisualizacao" do usuário "Ykaro" contém os itens "O Incrível Hulk", "Homem-Formiga" + When Uma requisição "GET" é enviada para "/user/Sistema/series/acao" + Then O status da resposta deve ser "200" + And O JSON da resposta contém os itens "Homem Aranha, de volta ao lar", "The Batman" + +Scenario: Exibindo recomendações para o usuário com histórico de visualizações (gênero 2) (Service) + Given A lista "historicoDeVisualizacao" do usuário "Ykaro" contém o item "Invocação do mal" + When Uma requisição "GET" é enviada para "/user/Sistema/series/terror" + Then O status da resposta deve ser "200" + And O JSON da resposta contém os itens "A Freira", "Sorria" + +Scenario: Exibindo recomendações para o usuário sem histórico de visualizações (Service) + Given A lista "historicoDeVisualizacao" do usuário "João" está vazia + When Uma requisição "GET" é enviada para "/user/Sistema/series" + Then O status da resposta deve ser "200" + And O JSON da resposta contém os itens "Moana 2", "Zootopia" + + diff --git a/backend/tests/features/user_register_service.feature b/backend/tests/features/user_register_service.feature new file mode 100644 index 0000000000..90106ef409 --- /dev/null +++ b/backend/tests/features/user_register_service.feature @@ -0,0 +1,121 @@ +Feature: Cadastro e manutenção de usuários + As a usuário do sistema + I want to criar, atualizar, e deletar minha informação pessoal + so that eu possa gerenciar os detalhes da minha conta de forma segura e os manter atualizados + +Scenario: Cadastro de usuário bem-sucedido +Given não há usuário cadastrado no sistema com usuário “LucasHenrique” +When Faço uma Requisição POST para a rota "/users" com os seguintes dados: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Senha | X | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +Then O status da resposta deve ser 201 +And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +And o usuário “LucasHenrique” foi devidamente armazenado no sistema. + +Scenario: Cadastro de usuário malsucedido (email já cadastrado) +Given há um usuário cadastrado no sistema com usuário/email “LucasHenrique” +When faço uma Requisição POST para a rota "/users" com os seguintes dados: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Senha | X | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +Then O status da resposta deve ser 409 +And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| error | Email já cadastrado | +| code | 409 | +And nenhum novo usuário foi cadastrado no sistema + +Scenario: Exibição de usuário bem-sucedido +Given há um usuário cadastrado no sistema com id "2", nome completo "Lucas Henrique", usuário/email "LucasHenrique", data de nascimento "20/02/2004", gênero "Masculino" e foto "minha-foto.jpg" +When faço uma Requisição GET para a rota "/users/2" +Then o status da resposta deve ser 200 +And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| Id | 2 | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | + +Scenario: Exibição de todos os usuários cadastrados +Given que há os seguintes usuários cadastrados no sistema: +| Id | Nome completo | Usuário/Email | Data de nascimento | Gênero | Foto | +| 1 | Ana Clara | AnaClara | 15/03/1995 | Feminino | ana-foto.jpg | +| 2 | Lucas Henrique | LucasHenrique | 20/02/2004 | Masculino | minha-foto.jpg | +| 3 | João Pedro | JoaoPedro | 10/11/1987 | Masculino | joao-foto.jpg | +When faço uma requisição GET para a rota "/users" +Then o status da resposta deve ser 200 +And a resposta JSON deve conter a lista de usuários com os seguintes dados: +| Id | Nome completo | Usuário/Email | Data de nascimento | Gênero | Foto | +| 1 | Ana Clara | AnaClara | 15/03/1995 | Feminino | ana-foto.jpg | +| 2 | Lucas Henrique | LucasHenrique | 20/02/2004 | Masculino | minha-foto.jpg | +| 3 | João Pedro | JoaoPedro | 10/11/1987 | Masculino | joao-foto.jpg | + + +Scenario: Exibição de usuário malsucedido +Given não há usuário no sistema com id "2" +When faço uma Requisição GET para a rota "/users/2" +Then o status da resposta deve ser 404 +And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| error | Usuário não encontrado | +| code | 404 | + +Scenario: Alteração cadastral bem-sucedida +Given há um usuário cadastrado no sistema com id "2", nome completo "Lucas Henrique", email "lucashenrique", senha "X", data de nascimento "20/02/2004", gênero "Masculino" e foto "minha-foto.jpg" +When faço uma Requisição PUT para a rota "/users/2" com nome completo "Lucas Henrique Silva", email "lucashenrique@gmail.com", senha "X", data de nascimento "20/02/2004", gênero "Masculino", foto "minha-foto-nova.jpg" +Then o status da resposta deve ser 200 And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| Nome completo | Lucas Henrique Silva | +| Usuário/Email | lucashenrique@gmail.com | +| Senha | X | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto-nova.jpg | + +Scenario: Alteração cadastral malsucedida (senha errada) +Given há um usuário cadastrado no sistema com id "2", nome completo "Lucas Henrique", email "lucashenrique", senha "X", data de nascimento "20/02/2004", gênero "Masculino" e foto "minha-foto.jpg" +When faço uma Requisição PUT para a rota "/users/2" com nome completo "Lucas Henrique Silva", email "lucashenrique@gmail.com", senha "X", data de nascimento "20/02/2004", gênero "Masculino", foto "minha-foto-nova.jpg" +Then o status da resposta deve ser 401 And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| error | Senha incorreta | +| code | 401 | + +Scenario: Alteração cadastral malsucedida (email já cadastrado) +Given há um usuário cadastrado no sistema com id "2", nome completo "Lucas Henrique", email "lucashenrique", senha "X", data de nascimento "20/02/2004", gênero "Masculino" e foto "minha-foto.jpg" +And há um outro usuário cadastrado no sistema com id "3", email "felipegomes" e nome completo "Felipe Gomes" +When faço uma Requisição PUT para a rota "/users/2" com nome completo "Lucas Henrique Silva", email "felipegomes@gmail.com", senha "X", data de nascimento "20/02/2004", gênero "Masculino", foto "minha-foto-nova.jpg" +Then o status da resposta deve ser 409 And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| error | Email já cadastrado | +| code | 409 | + +Scenario: Exclusão de usuário bem-sucedido +Given há um usuário cadastrado no sistema com id "2", nome completo "Lucas Henrique", email "lucashenrique", data de nascimento "20/02/2004", gênero "Masculino" e foto "minha-foto.jpg" +When faço uma Requisição DELETE para a rota "/users/2" com senha "X" Then o status da resposta deve ser 200 And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| message | Usuário excluído | + +Scenario: Exclusão de usuário malsucedido +Given não há usuário cadastrado no sistema com id "2" +When faço uma Requisição DELETE para a rota "/users/2" com senha "X" Then o status da resposta deve ser 404 +And a resposta JSON deve conter os seguintes dados: +| Campo | Valor | +| error | Usuário não encontrado | +| code | 404 | diff --git a/backend/tests/features/video-api.feature b/backend/tests/features/video-api.feature new file mode 100644 index 0000000000..d727f8ad69 --- /dev/null +++ b/backend/tests/features/video-api.feature @@ -0,0 +1,42 @@ +Feature: Sistema de Visibilidade de Vídeos (Serviço) + + Scenario: Obter dados de vídeo com sucesso + Given existe um vídeo com id "101" no sistema + And esse vídeo tem título "Stranger Things - Piloto" + And duração "45 minutos" + When faço uma requisição GET para "/videos/101" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém: + """ + { + "videoId": "101", + "titulo": "Stranger Things - Piloto", + "duracao": "45 minutos", + "videoLink": "https://youtube.com/watch?v=101" + } + """ + + Scenario: Vídeo não encontrado + Given não existe um vídeo com id "999" + When faço uma requisição GET para "/videos/999" + Then o status da resposta é "404 Not Found" + And o corpo da resposta (JSON) indica "Vídeo não encontrado" + + Scenario: Registrar visualização de vídeo com sucesso + Given existe um vídeo com id "101" no sistema + And existe um usuário com id "1" + When faço uma requisição POST para "/videos/101/visualizacao" com o corpo: + """ + { + "userId": "1" + } + """ + Then o status da resposta é "201 Created" + And o corpo da resposta (JSON) contém: + """ + { + "message": "Visualização registrada com sucesso", + "videoId": "101", + "userId": "1" + } + """ diff --git a/backend/tests/features/video-service.feature b/backend/tests/features/video-service.feature new file mode 100644 index 0000000000..ee714c13e8 --- /dev/null +++ b/backend/tests/features/video-service.feature @@ -0,0 +1,6 @@ +Feature: Video Service + + Scenario: Retornar vídeo por videoId + Given o método getVideo do VideoService retorna um vídeo com videoId "101", título "Stranger Things - Piloto" e duração "45 minutos" + When o método getVideo do VideoService for chamado com o id "101" + Then o vídeo retornado deve ter videoId "101", título "Stranger Things - Piloto" e duração "45 minutos" diff --git a/backend/tests/features/video.feature b/backend/tests/features/video.feature new file mode 100644 index 0000000000..5af52ef480 --- /dev/null +++ b/backend/tests/features/video.feature @@ -0,0 +1,41 @@ +Feature: Sistema de Visibilidade de Vídeos (Serviço) + + Scenario: Obter dados de vídeo com sucesso + Given existe um vídeo com id "101" no sistema + And esse vídeo tem título "Stranger Things - Piloto" + And duração "45 minutos" + When faço uma requisição GET para "/videos/101" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém: + """ + { + "videoId": "101", + "titulo": "Stranger Things - Piloto", + "duracao": "45 minutos" + } + """ + + Scenario: Vídeo não encontrado + Given não existe um vídeo com id "999" + When faço uma requisição GET para "/videos/999" + Then o status da resposta é "404 Not Found" + And o corpo da resposta (JSON) indica "Vídeo não encontrado" + + Scenario: Registrar visualização de vídeo com sucesso + Given existe um vídeo com id "101" no sistema + And existe um usuário com id "1" + When faço uma requisição POST para "/videos/101/visualizacao" com o corpo: + """ + { + "userId": "1" + } + """ + Then o status da resposta é "201 Created" + And o corpo da resposta (JSON) contém: + """ + { + "message": "Visualização registrada com sucesso", + "videoId": "101", + "userId": "1" + } + """ diff --git a/backend/tests/integration/like.integration.step.js b/backend/tests/integration/like.integration.step.js new file mode 100644 index 0000000000..707ca1c703 --- /dev/null +++ b/backend/tests/integration/like.integration.step.js @@ -0,0 +1,42 @@ +import request from 'supertest'; +import fs from 'fs'; +import path from 'path'; +import app from '../../src/app.js'; + +const filePath = path.resolve('src/database/users.json'); + +describe('Teste de Integração - Likes', () => { + let originalData; + + beforeAll(() => { + originalData = fs.readFileSync(filePath, 'utf-8'); + }); + + afterAll(() => { + fs.writeFileSync(filePath, originalData, 'utf-8'); + }); + + test('Deve curtir uma série com sucesso e atualizar o arquivo JSON', async () => { + const userId = 'Ykaro'; + const serie = 'The Boys'; + + const response = await request(app) + .put(`/user/curtir/${userId}`) + .send({ serie }) + .expect(200); + + expect(response.body).toEqual({ + message: 'Série curtida com sucesso!', + user: expect.objectContaining({ + user: userId, + 'Séries Curtidas': expect.arrayContaining([serie]), + }), + }); + + const updatedData = JSON.parse(fs.readFileSync(filePath, 'utf-8')); + const user = updatedData.find((u) => u.user === userId); + + expect(user).toBeDefined(); + expect(user["Séries Curtidas"]).toContain(serie); + }); +}); diff --git a/backend/tests/steps/history.api.test.js b/backend/tests/steps/history.api.test.js new file mode 100644 index 0000000000..71953d2a5e --- /dev/null +++ b/backend/tests/steps/history.api.test.js @@ -0,0 +1,111 @@ +const { loadFeature, defineFeature } = require('jest-cucumber'); +const supertest = require('supertest'); +const app = require('../../src/app'); +const Database = require('../../src/database'); +const { di } = require('../../src/di'); +const HistoryRepository = require('../../src/repositories/history.repository'); + +const feature = loadFeature('tests/features/history-api.feature'); + +beforeAll(async () => { + await Database.reset(); + const dbInstance = await Database.getInstance(); + await dbInstance.seed(); + await di.getRepository(HistoryRepository).init(); +}); + +defineFeature(feature, (test) => { + let response; + + test('Obter histórico com sucesso (não vazio)', ({ given, when, then }) => { + given('existe um usuário com id "1" cadastrado no sistema', () => {}); + // Repita o given para a condição "And" se necessário: + given('esse usuário possui os vídeos "101" e "102" no histórico', () => {}); + + when('faço uma requisição GET para "/users/1/history"', async () => { + response = await supertest(app).get('/api/users/1/history'); + }); + + then('o status da resposta é "200 OK"', () => { + expect(response.status).toBe(200); + }); + + then('o corpo da resposta (JSON) contém os vídeos "101" e "102"', () => { + expect(response.body.data).toEqual(expect.arrayContaining(['101', '102'])); + }); + }); + + test('Obter histórico vazio', ({ given, when, then }) => { + given('existe um usuário com id "2" cadastrado no sistema', () => {}); + given('esse usuário não possui nenhum vídeo no histórico', () => {}); + + when('faço uma requisição GET para "/users/2/history"', async () => { + response = await supertest(app).get('/api/users/2/history'); + }); + + then('o status da resposta é "200 OK"', () => { + expect(response.status).toBe(200); + }); + + then('o corpo da resposta (JSON) contém uma lista vazia', () => { + expect(response.body.data).toEqual([]); + }); + }); + + test('Obter histórico de usuário inexistente', ({ given, when, then }) => { + given('não existe um usuário com id "999" no sistema', () => {}); + + when('faço uma requisição GET para "/users/999/history"', async () => { + response = await supertest(app).get('/api/users/999/history'); + }); + + then('o status da resposta é "404 Not Found"', () => { + expect(response.status).toBe(404); + }); + + then('o corpo da resposta (JSON) indica "Usuário não encontrado"', () => { + expect(response.body.msg).toBe('Usuário não encontrado'); + }); + }); + + test('Adicionar vídeo ao histórico', ({ given, when, then }) => { + given('existe um usuário com id "3" cadastrado no sistema', () => {}); + given('esse usuário não possui o vídeo "201" no histórico', () => {}); + + when('faço uma requisição PUT para "/users/3/history" com o corpo:', async (docString) => { + const payload = JSON.parse(docString); + response = await supertest(app).put('/api/users/3/history').send(payload); + }); + + then('o status da resposta deve ser "201 Created"', () => { + expect(response.status).toBe(201); + }); + + then('o corpo da resposta (JSON) contém "Vídeo adicionado ao histórico"', () => { + expect(response.body.msg).toBe('Vídeo adicionado ao histórico'); + }); + + then('agora o histórico do usuário com id "3" possui o vídeo "201"', async () => { + const check = await supertest(app).get('/api/users/3/history'); + expect(check.status).toBe(200); + expect(check.body.data).toContain('201'); + }); + }); + + test('Atualizar histórico para um usuário', ({ given, when, then }) => { + given('existe um usuário com id "3" que já possui o vídeo "201" no histórico', () => {}); + + when('faço uma requisição PUT para "/users/3/history" com o corpo:', async (docString) => { + const payload = JSON.parse(docString); + response = await supertest(app).put('/api/users/3/history').send(payload); + }); + + then('o status da resposta deve ser "200 OK"', () => { + expect(response.status).toBe(200); + }); + + then('o corpo da resposta (JSON) contém "Data de visualização atualizada"', () => { + expect(response.body.msg).toBe('Data de visualização atualizada'); + }); + }); +}); diff --git a/backend/tests/steps/history.service.test.js b/backend/tests/steps/history.service.test.js new file mode 100644 index 0000000000..5a2f2f5962 --- /dev/null +++ b/backend/tests/steps/history.service.test.js @@ -0,0 +1,103 @@ +const { loadFeature, defineFeature } = require('jest-cucumber'); +const HistoryRepository = require('../../src/repositories/history.repository'); +const HistoryService = require('../../src/services/history.service'); +const HistoryItemEntity = require('../../src/entities/history.item.entity'); + +const feature = loadFeature('tests/features/history-service.feature'); + +defineFeature(feature, test => { + let mockHistoryRepository; + let historyService; + let serviceResult; + + beforeEach(() => { + mockHistoryRepository = { + getHistoryByUserId: jest.fn(), + getHistoryItem: jest.fn(), + add: jest.fn(), + updateById: jest.fn(), + }; + historyService = new HistoryService(mockHistoryRepository); + }); + + test('Retornar todo o histórico para um usuário', ({ given, when, then }) => { + given(/^o método getHistory do HistoryService retorna um array com os itens de vídeo com videoIds "101", "203" e "402" para o usuário "1"$/, () => { + mockHistoryRepository.getHistoryByUserId.mockResolvedValue([ + { videoId: '101', ultimaVisualizacao: new Date().toISOString() }, + { videoId: '203', ultimaVisualizacao: new Date().toISOString() }, + { videoId: '402', ultimaVisualizacao: new Date().toISOString() }, + ]); + }); + + when(/^o método getHistory do HistoryService for chamado com o id do usuário "1"$/, async () => { + serviceResult = await historyService.getHistory('1'); + }); + + then(/^o array retornado deve conter a lista com os ids "101", "203" e "402"$/, () => { + // Como o serviço retorna { code, data }, usamos serviceResult.data + const ids = serviceResult.data.map(item => item.videoId); + expect(ids).toEqual(expect.arrayContaining(['101', '203', '402'])); + }); + }); + + test('Retornar item do histórico por videoId para um usuário', ({ given, when, then }) => { + given(/^o método getHistoryItem chamado com "1" para o usuário "1" do HistoryService retorna um item com videoId "101"$/, () => { + const item = new HistoryItemEntity({ userId: '1', videoId: '101', ultimaVisualizacao: new Date().toISOString() }); + mockHistoryRepository.getHistoryItem.mockResolvedValue(item); + }); + + when(/^o método getHistoryItem do HistoryService for chamado com o id "101"$/, async () => { + serviceResult = await mockHistoryRepository.getHistoryItem('1', '101'); + }); + + then(/^o item retornado deve ter videoId "101"$/, () => { + expect(serviceResult.videoId).toBe('101'); + }); + }); + + test('Adicionar histórico para um usuário', ({ given, when, then }) => { + given('existe um usuário com id "3" cadastrado no sistema', () => {}); + given('esse usuário não possui o vídeo "201" no histórico', () => { + mockHistoryRepository.getHistoryItem.mockResolvedValue(null); + // Simula o add – retorna o item recebido + mockHistoryRepository.add.mockImplementation(async (data) => data); + // Após a inserção, simula getHistoryByUserId incluindo o novo item + mockHistoryRepository.getHistoryByUserId.mockResolvedValue([{ videoId: '201', ultimaVisualizacao: new Date().toISOString() }]); + }); + when(/^o método addOrUpdateHistory do HistoryService for chamado com o id "3" e os dados:$/, async docString => { + const videoData = JSON.parse(docString); + serviceResult = await historyService.addOrUpdateHistory('3', videoData); + }); + then(/^o status da resposta deve ser "201 Created"$/, () => { + expect(serviceResult.code).toBe(201); + }); + then(/^o corpo da resposta \(JSON\) contém "Vídeo adicionado ao histórico"$/, () => { + expect(serviceResult.msg).toBe('Vídeo adicionado ao histórico'); + }); + then(/^agora o histórico do usuário com id "3" possui o id "201"$/, () => { + const ids = serviceResult.data.map(item => item.videoId); + expect(ids).toContain('201'); + }); + }); + + test('Atualizar histórico para um usuário', ({ given, when, then }) => { + given('existe um usuário com id "3" que já possui o vídeo "201" no histórico', () => { + const item = new HistoryItemEntity({ userId: '3', videoId: '201', ultimaVisualizacao: new Date('2020-01-01').toISOString() }); + mockHistoryRepository.getHistoryItem.mockResolvedValue(item); + // Simula o update – retorna o item atualizado + mockHistoryRepository.updateById.mockImplementation(async (id, data) => ({ ...item, ...data })); + // Após a atualização, simula getHistoryByUserId retornando o item atualizado + mockHistoryRepository.getHistoryByUserId.mockResolvedValue([{ videoId: '201', ultimaVisualizacao: new Date().toISOString() }]); + }); + when(/^o método addOrUpdateHistory do HistoryService for chamado com o id "3" e os dados:$/, async docString => { + const videoData = JSON.parse(docString); + serviceResult = await historyService.addOrUpdateHistory('3', videoData); + }); + then(/^o status da resposta deve ser "200 OK"$/, () => { + expect(serviceResult.code).toBe(200); + }); + then(/^o corpo da resposta \(JSON\) contém "Data de visualização atualizada"$/, () => { + expect(serviceResult.msg).toBe('Data de visualização atualizada'); + }); + }); +}); diff --git a/backend/tests/steps/video.api.test.js b/backend/tests/steps/video.api.test.js new file mode 100644 index 0000000000..97a0da5797 --- /dev/null +++ b/backend/tests/steps/video.api.test.js @@ -0,0 +1,73 @@ +const { loadFeature, defineFeature } = require('jest-cucumber'); +const supertest = require('supertest'); +const app = require('../../src/app'); +const Database = require('../../src/database'); +const { di } = require('../../src/di'); +const VideoRepository = require('../../src/repositories/video.repository'); + +const feature = loadFeature('tests/features/video-api.feature'); + +beforeAll(async () => { + await Database.reset(); + const dbInstance = await Database.getInstance(); + await dbInstance.seed(); + await di.getRepository(VideoRepository).init(); +}); + +defineFeature(feature, (test) => { + let response; + + test('Obter dados de vídeo com sucesso', ({ given, when, then }) => { + given('existe um vídeo com id "101" no sistema', () => {}); + given('esse vídeo tem título "Stranger Things - Piloto"', () => {}); + given('duração "45 minutos"', () => {}); + + when('faço uma requisição GET para "/videos/101"', async () => { + response = await supertest(app).get('/api/videos/101'); + }); + + then('o status da resposta é "200 OK"', () => { + expect(response.status).toBe(200); + }); + + then('o corpo da resposta (JSON) contém:', (docString) => { + const expected = JSON.parse(docString); + expect(response.body.data.videoId).toBe(expected.videoId); + expect(response.body.data.titulo).toBe(expected.titulo); + expect(response.body.data.duracao).toBe(expected.duracao); + // Opcional: teste também o videoLink, se necessário + // expect(response.body.data.videoLink).toBe(expected.videoLink); + }); + }); + + test('Vídeo não encontrado', ({ given, when, then }) => { + given('não existe um vídeo com id "999"', () => {}); + when('faço uma requisição GET para "/videos/999"', async () => { + response = await supertest(app).get('/api/videos/999'); + }); + then('o status da resposta é "404 Not Found"', () => { + expect(response.status).toBe(404); + }); + then('o corpo da resposta (JSON) indica "Vídeo não encontrado"', () => { + expect(response.body.msg).toBe('Vídeo não encontrado'); + }); + }); + + test('Registrar visualização de vídeo com sucesso', ({ given, when, then }) => { + given('existe um vídeo com id "101" no sistema', () => {}); + given('existe um usuário com id "1"', () => {}); + when('faço uma requisição POST para "/videos/101/visualizacao" com o corpo:', async (docString) => { + const payload = JSON.parse(docString); + response = await supertest(app).post('/api/videos/101/visualizacao').send(payload); + }); + then('o status da resposta é "201 Created"', () => { + expect(response.status).toBe(201); + }); + then('o corpo da resposta (JSON) contém:', (docString) => { + const expected = JSON.parse(docString); + expect(response.body.data.message).toBe(expected.message); + expect(response.body.data.videoId).toBe(expected.videoId); + expect(response.body.data.userId).toBe(expected.userId); + }); + }); +}); diff --git a/backend/tests/steps/video.service.test.js b/backend/tests/steps/video.service.test.js new file mode 100644 index 0000000000..764f6c4b3d --- /dev/null +++ b/backend/tests/steps/video.service.test.js @@ -0,0 +1,43 @@ +const { loadFeature, defineFeature } = require('jest-cucumber'); +const VideoService = require('../../src/services/video.service'); +const VideoRepository = require('../../src/repositories/video.repository'); +const db = require('../../src/database'); + +const feature = loadFeature('tests/features/video-service.feature'); + +defineFeature(feature, (test) => { + let serviceResult; + let errorCaught; + let videoService; + + beforeAll(async () => { + // Obtém a instância do banco de dados (que já se conecta, inicializa e configura) + const instance = await db.getInstance(); + // Semeia os dados + await instance.seed(); + // Cria e inicializa o repositório de vídeos + const videoRepo = new VideoRepository(); + await videoRepo.init(); + // Injeta o repositório no serviço + videoService = new VideoService(videoRepo); + }); + + test('Retornar vídeo por videoId', ({ given, when, then }) => { + given('o método getVideo do VideoService retorna um vídeo com videoId "101", título "Stranger Things - Piloto" e duração "45 minutos"', () => { + // Os dados já estão inseridos pelo seed do banco. + }); + when('o método getVideo do VideoService for chamado com o id "101"', async () => { + try { + serviceResult = await videoService.getVideo('101'); + } catch (err) { + errorCaught = err; + } + }); + then('o vídeo retornado deve ter videoId "101", título "Stranger Things - Piloto" e duração "45 minutos"', () => { + if (errorCaught) throw errorCaught; + expect(serviceResult.videoId).toBe('101'); + expect(serviceResult.titulo).toBe('Stranger Things - Piloto'); + expect(serviceResult.duracao).toBe('45 minutos'); + }); + }); +}); diff --git a/backend/tests/tsconfig.json b/backend/tests/tsconfig.json new file mode 100644 index 0000000000..da0e614b80 --- /dev/null +++ b/backend/tests/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "allowJs": true, + "moduleResolution": "node", + "target": "ESNext", + "module": "ESNext", + "outDir": "dist", + "strict": true, + "esModuleInterop": true, + "strictPropertyInitialization": false, + "experimentalDecorators": true + } + } \ No newline at end of file diff --git a/backend/tests/unit/controllers-like.step.js b/backend/tests/unit/controllers-like.step.js new file mode 100644 index 0000000000..e16603d5a6 --- /dev/null +++ b/backend/tests/unit/controllers-like.step.js @@ -0,0 +1,32 @@ +import { curtir } from '../../src/controllers/like.controllers'; +import fs from 'fs'; + +jest.mock('fs'); +jest.mock('path', () => ({ + resolve: jest.fn(() => './src/database/users.json'), +})); + +test('Deve curtir uma série com sucesso', async () => { + const req = { + params: { userid: 'Ykaro' }, + body: { serie: 'The Boys' } + }; + + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn() + }; + + const mockUserData = [{ user: 'Ykaro', 'Séries Curtidas': [] }]; + + fs.readFileSync.mockReturnValue(JSON.stringify(mockUserData)); + fs.writeFileSync.mockImplementation(() => {}); + + await curtir(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + message: 'Série curtida com sucesso!', + user: { user: 'Ykaro', 'Séries Curtidas': ['The Boys'] }, + }); +}); diff --git a/backend/tests/utils/.gitkeep b/backend/tests/utils/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cadastro_usuarios.feature b/cadastro_usuarios.feature new file mode 100644 index 0000000000..97c1acc515 --- /dev/null +++ b/cadastro_usuarios.feature @@ -0,0 +1,61 @@ +Feature: Cadastro e manutenção de usuários + As a usuário do sistema + I want to criar, atualizar, e deletar minha informação pessoal + so that eu possa gerenciar os detalhes da minha conta de forma segura e os manter atualizados +Scenario: Cadastro de usuário bem-sucedido +Given o usuário “LucasHenrique” está na página de “Cadastro de usuários” +And o usuário “LucasHenrique” não está cadastrado no sistema +When o usuário preenche “Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto” com, respectivamente, +“Lucas Henrique”, “LucasHenrique”,”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” +Then o usuário recebe um aviso de cadastro bem-sucedido +And o usuário com os dados “Lucas Henrique”, “LucasHenrique”, “X”, “20/02/2004”, “Masculino”, “minha-foto.jpg”, respectivamente, +“Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto”, é devidamente armazenado no sistema + +Scenario: Alteração de dados cadastrais bem-sucedido +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And possui como “Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto”, respectivamente, +“Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” +When o usuário altera sua “Data de nascimento”, de “20/02/2004” por “20/02/1994” +And insere sua “Senha” corretamente, “X” +Then ele recebe um aviso de alteração cadastral bem-sucedido +And o usuário pode ver que seus dados “Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto” são, +respectivamente, “Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/1994”, “Masculino”, “minha-foto.jpg” + +Scenario: Alteração de dados cadastrais malsucedido +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And possui como “Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto”, respectivamente, +“Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” +When o usuário altera sua “Data de nascimento”, de “20/02/2004” por “20/02/1994” +And insere sua “Senha” incorretamente, “Y” +Then ele recebe um aviso de alteração cadastral malsucedido +And o usuário pode ver que seus dados “Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto” são, +respectivamente, “Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” + +Scenario: Remoção de usuário bem-sucedido +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And possui como “Nome completo”, “Usuário/Email”, “Senha”, “Data de nascimento”, “Gênero” e “Foto”, respectivamente, +“Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” +When seleciona em “deletar minha conta” +Then o sistema pede ao usuário para inserir sua senha +When o usuário insere corretamente sua “Senha”, “X” +Then ele recebe um aviso de remoção cadastral bem-sucedido +And o usuário é redirecionado para página de “Autenticação” +And o usuário de dados “Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” não está mais +cadastrado no sistema +And todos os registros de listas criadas e séries assistidas pelo usuário não estão mais gravados no sistema + +Scenario: Exibir um usuário que está cadastrado no sistema +Given um um usuário existente no banco de dados com id "2" +When faço uma Requisição GET para a rota "/users/2" +Then o status da resposta deve ser 200 +And a resposta JSON deve conter "2", “Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” + +Scenario: Cadastrar um usuário que não está cadastrado no sistema +Given não há usuário cadastrado no sistema com id "2" e usuário “LucasHenrique” +When Faço uma Requisição POST para a rota "/users/2" com id "2", nome completo “Lucas Henrique”, usuário/email “LucasHenrique”, +senha ”X”, data de nascimento “20/02/2004”, gênero “Masculino”, foto “minha-foto.jpg” +Then O status da resposta deve ser 200 +And a resposta JSON deve ser "2", “Lucas Henrique”, “LucasHenrique”, ”X”, “20/02/2004”, “Masculino”, “minha-foto.jpg” +And o usuário com id "2", nome completo “Lucas Henrique”, usuário/email “LucasHenrique”, senha ”X”, data de nascimento “20/02/2004”, + gênero “Masculino”, foto “minha-foto.jpg” foi devidamente armazenado no sistema. + diff --git a/features/Exibir_historico_de_videos_series_assistidos.feature b/features/Exibir_historico_de_videos_series_assistidos.feature new file mode 100644 index 0000000000..666a0de2c5 --- /dev/null +++ b/features/Exibir_historico_de_videos_series_assistidos.feature @@ -0,0 +1,40 @@ +Feature: Exibir Histórico de Vídeos/Séries Assistidos e Assistidos Recentemente + + As a usuário + I want Quero ver meu histórico de vídeos e séries assistidos + So that eu possa acompanhar o que assisti e ver os itens assistidos recentemente + + + Scenario: Registrar visualização ao acessar a página do vídeo + Given o usuário está logado na plataforma + And o usuário está na página inicial + When o usuário seleciona e acessa o vídeo "Breaking Bad - Episódio 1" + Then o usuário é redirecionado para a página de visualização do vídeo "Breaking Bad - Episódio 1" + And "Breaking Bad - Episódio 1" aparece no topo do histórico de visualizações do usuário + + Cenário precisa ser concertado + Scenario: Exibir 2 vídeos por página com botões de navegação + Given o usuário está logado na plataforma + And o usuário possui 3 vídeos assistidos no histórico + When o usuário acessa a página de histórico de visualizações + Then o sistema exibe os primeiros 2 vídeos (1 a 2) na primeira página + + When o usuário clica no botão "Próximo" + Then o sistema exibe o último 1 vídeo na segunda página + + When o usuário clica no botão "anterior" + Then o sistema exibe os últimos 2 vídeos (1 a 2) na primeira página + + Scenario: Exibir mensagem padrão para usuários sem histórico de visualizações + Given o usuário está logado na plataforma + And o usuário nunca assistiu a nenhum vídeo/série + When o usuário acessa a página de histórico de visualizações + Then o sistema exibe a mensagem "Você ainda não assistiu a nenhum vídeo." + And o sistema recomenda o vídeo mais assistido da plataforma "Stranger Things - Episódio Piloto" + + Scenario: Atualizar histórico ao assistir novamente um vídeo + Given o usuário está logado na plataforma + And o usuário já assistiu ao vídeo "Game of Thrones - Temporada 1, Episódio 1" no dia 01/01/2024 + When o usuário clica novamente no vídeo "Game of Thrones - Temporada 1, Episódio 1" + Then o usuário é redirecionado para a página de visualização do vídeo "Game of Thrones - Temporada 1, Episódio 1" + And "Game of Thrones - Temporada 1, Episódio 1" aparece no topo da lista de histórico de visualizações do usuário \ No newline at end of file diff --git a/features/Exibir_historico_de_videos_series_assistidos_GUI.feature b/features/Exibir_historico_de_videos_series_assistidos_GUI.feature new file mode 100644 index 0000000000..a3ff719131 --- /dev/null +++ b/features/Exibir_historico_de_videos_series_assistidos_GUI.feature @@ -0,0 +1,47 @@ +Feature: Exibir Histórico de Vídeos/Séries Assistidos e Assistidos Recentemente + + As a usuário + I want Quero ver meu histórico de vídeos e séries assistidos + So that eu possa acompanhar o que assisti e ver os itens assistidos recentemente + + Scenario: Registrar primeira visualização + Given o usuário "Lucas Sales" está na página "Inicial" + And "Lucas Sales" não possui nenhum vídeo em seu histórico + When "Lucas Sales" seleciona o vídeo "Breaking Bad - Episódio 1" para assistir + Then "Lucas Sales" acessa a página "Histórico" + And "Lucas Sales" vê "Breaking Bad - Episódio 1" no histórico + + Scenario: Paginação do histórico (2 vídeos por página) + Given o usuário "Lucas Sales" está na página "Histórico" + And "Lucas Sales" possui os seguintes 3 vídeos em seu histórico: + | ID | Título | + | 1 | "Breaking Bad - Episódio 1" | + | 2 | "Breaking Bad - Episódio 2" | + | 3 | "Stranger Things - Episódio 1" | + When a página "Histórico" é exibida + Then o sistema mostra os 2 primeiros vídeos: + | "Breaking Bad - Episódio 1" | + | "Breaking Bad - Episódio 2" | + + When "Lucas Sales" seleciona a opção "Próximo" + Then o sistema mostra: + | "Stranger Things - Episódio 1" | + + When "Lucas Sales" seleciona a opção "Anterior" + Then o sistema mostra novamente: + | "Breaking Bad - Episódio 1" | + | "Breaking Bad - Episódio 2" | + + Scenario: Exibir mensagem para usuário sem histórico + Given o usuário "Lucas Sales" está na página "Histórico" + And "Lucas Sales" não possui nenhum vídeo em seu histórico + When a página "Histórico" é carregada + Then o sistema exibe a mensagem "Você ainda não assistiu a nenhum vídeo." + And o sistema recomenda o vídeo "Stranger Things - Episódio Piloto" + + Scenario: Reassistir um vídeo já visto + Given o usuário "Lucas Sales" está na página "Inicial" + And "Lucas Sales" já assistiu o vídeo "Game of Thrones - T1 E1" + When "Lucas Sales" seleciona novamente o vídeo "Game of Thrones - T1 E1" + Then o sistema atualiza a data de visualização de "Game of Thrones - T1 E1" + And o vídeo "Game of Thrones - T1 E1" permanece no histórico de "Lucas Sales" \ No newline at end of file diff --git a/features/Exibir_historico_de_videos_series_assistidos_servico.feature b/features/Exibir_historico_de_videos_series_assistidos_servico.feature new file mode 100644 index 0000000000..2051c60673 --- /dev/null +++ b/features/Exibir_historico_de_videos_series_assistidos_servico.feature @@ -0,0 +1,66 @@ +Feature: Histórico de Visualizações (Serviço) + + Scenario: Obter histórico com sucesso (não vazio) + Given existe um usuário com id "1" cadastrado no sistema + And esse usuário possui os vídeos "101" e "102" no histórico + When faço uma requisição GET para "/users/1/history" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém os vídeos "101" e "102" + + Scenario: Obter histórico vazio + Given existe um usuário com id "2" cadastrado no sistema + And esse usuário não possui nenhum vídeo no histórico + When faço uma requisição GET para "/users/2/history" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém uma lista vazia + + Scenario: Obter histórico de usuário inexistente + Given não existe um usuário com id "999" no sistema + When faço uma requisição GET para "/users/999/history" + Then o status da resposta é "404 Not Found" + And o corpo da resposta (JSON) indica "Usuário não encontrado" + + Scenario: Adicionar vídeo ao histórico + Given existe um usuário com id "3" cadastrado no sistema + And esse usuário não possui o vídeo "201" no histórico + When faço uma requisição POST para "/users/3/history" + And o corpo (JSON) contém: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta é "201 Created" + And o corpo da resposta (JSON) contém: + """ + { + "message": "Vídeo adicionado ao histórico", + "history": [ + { "videoId": "201", "titulo": "The Office - Episódio 3", "ultimaVisualizacao": "nova-data" } + ] + } + """ + And agora o histórico do usuário com id "3" possui o vídeo "201" + + Scenario: Atualizar data de visualização de um vídeo já existente + Given existe um usuário com id "3" que já possui o vídeo "201" no histórico + When faço uma requisição POST para "/users/3/history" + And o corpo (JSON) contém: + """ + { + "videoId": "201", + "titulo": "The Office - Episódio 3" + } + """ + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém: + """ + { + "message": "Data de visualização atualizada", + "history": [ + { "videoId": "201", "titulo": "The Office - Episódio 3", "ultimaVisualizacao": "nova-data" } + ] + } + """ + And a data de visualização do vídeo "201" foi atualizada no banco de dados diff --git a/features/Minha_Lista.feature b/features/Minha_Lista.feature new file mode 100644 index 0000000000..0b7522f521 --- /dev/null +++ b/features/Minha_Lista.feature @@ -0,0 +1,47 @@ +Feature: Minhas listas +As a usuário do sistema +I want to criar, deletar e editar listas (adição e remoção de itens) +So that eu possa organizar meus conteúdos de forma personalizada + +Scenario: Criar lista +Given o usuário "José" está na página "Minhas listas" +And o usuário "José" não tem a lista "Favoritos" cadastrada +When o usuário "José" adiciona a lista "Favoritos" +Then o usuário "José" ainda está na página "Minhas listas" +And a lista "Favoritos" é exibida na página + +Scenario: Falha ao criar lista duplicada +Given o usuário "José" está na página "Minhas listas" +And o usuário "José" tem a lista "Favoritos" cadastrada +When o usuário "José" tenta adicionar a lista "Favoritos" +Then o usuário "José" recebe uma mensagem de erro informando que a lista já existe + +Scenario: Deletar lista +Given o usuário "José" está na página "Minhas listas" +And o usuário "José" tem a lista "Favoritos" cadastrada +When o usuário "José" remove a lista "Favoritos" +Then o usuário "José" ainda está na página "Minhas listas" +And a lista "Favoritos" não é mais exibida + +Scenario: Adicionar item na lista +Given o usuário "José" tem a lista "Favoritos" cadastrada +And o usuário "José" está na página "Favoritos" +And o usuário "José" não tem o item "The Batman" na lista "Favoritos" +When o usuário "José" tenta adicionar o item "The Batman" +Then o usuário "José" ainda está na página "Favoritos" +And o item "The Batman" é exibido na lista + +Scenario: Falha ao adicionar item duplicado na lista +Given o usuário "José" tem a lista "Favoritos" cadastrada +And o usuário "José" está na página "Favoritos" +And o usuário "José" tem o item "The Batman" na lista "Favoritos" +When o usuário "José" tenta adicionar o item "The Batman" +Then o usuário "José" recebe uma mensagem de erro informando que o item já existe na lista + +Scenario: Remover item da lista +Given o usuário "José" tem a lista "Favoritos" cadastrada +And o usuário "José" está na página "Favoritos" +And o usuário "José" tem o item "The Batman" na lista "Favoritos" +When o usuário "José" tenta remover o item "The Batman" +Then o usuário "José" ainda está na página "Favoritos" +And o item "The Batman" não é mais exibido na lista \ No newline at end of file diff --git a/features/Sistema_de_visibilidade_de_videos.feature b/features/Sistema_de_visibilidade_de_videos.feature new file mode 100644 index 0000000000..cace8771b0 --- /dev/null +++ b/features/Sistema_de_visibilidade_de_videos.feature @@ -0,0 +1,32 @@ +Feature: Sistema de Visibilidade de Vídeos + + As um usuário + I want visualizar vídeos com uma interface de reprodução e interações + So that eu possa assistir, curtir ou compartilhar os vídeos facilmente. + + Scenario: Compartilhar vídeo copiando a URL + Given o usuário está na página de visualização do vídeo "Stranger Things - Episódio Piloto" + When o usuário clica no botão "Compartilhar" + And o usuário clica em "Copiar URL" + Then a URL da página de visualização do vídeo "Stranger Things - Episódio Piloto" é copiada para a área de transferência + And o usuário vê uma confirmação "URL copiada com sucesso!" + + Scenario: Registrar visualização no histórico ao acessar a página de visualização + Given o usuário está logado na plataforma + And o usuário possui o vídeo "The Office - Episódio 3" no banco de dados + When o usuário clica no vídeo "The Office - Episódio 3" + Then o usuário é redirecionado para a página de visualização do vídeo "The Office - Episódio 3" + And "The Office - Episódio 3" aparece no topo da lista de histórico de visualizações do usuário + + Scenario: Acessar a página de visualização sem estar logado + Given o usuário não está logado na plataforma + When o usuário tenta acessar a página de visualização do vídeo "Friends - Episódio 5" diretamente via URL + Then o sistema redireciona o usuário para a página de login + And exibe a mensagem "Por favor, faça login para acessar o vídeo." + + Cenário precisa ser concertado + Scenario: Exibir elementos essenciais na página de visualização + Given o usuário está logado na plataforma + And o usuário está na página inicial + When o usuário clica no vídeo "Breaking Bad - Temporada 1, Episódio 1" + Then a página de visualização do vídeo "Breaking Bad - Temporada 1, Episódio 1" é exibida \ No newline at end of file diff --git a/features/Sistema_de_visibilidade_de_videos_GUI.feature b/features/Sistema_de_visibilidade_de_videos_GUI.feature new file mode 100644 index 0000000000..d174264522 --- /dev/null +++ b/features/Sistema_de_visibilidade_de_videos_GUI.feature @@ -0,0 +1,19 @@ +Feature: Sistema de Visibilidade de Vídeos + + As um usuário + I want visualizar vídeos com uma interface de reprodução e interações + So that eu possa assistir, curtir ou compartilhar os vídeos facilmente. + + Scenario: Compartilhar vídeo copiando a URL + Given o usuário "Lucas Sales" está na página do vídeo "Stranger Things - Piloto" + When "Lucas Sales" seleciona a opção "Compartilhar" + And "Lucas Sales" seleciona a opção "Copiar URL" + Then o sistema coloca a URL de "Stranger Things - Piloto" na área de transferência + And exibe a mensagem "URL copiada com sucesso!" + + Scenario: Registrar visualização no histórico + Given o usuário "Lucas Sales" está na página "Inicial" + And "Lucas Sales" não possui o vídeo "The Office - Episódio 3" em seu histórico + When "Lucas Sales" seleciona o vídeo "The Office - Episódio 3" + Then "Lucas Sales" acessa a página "Histórico" + And "Lucas Sales" vê "The Office - Episódio 3" no histórico diff --git a/features/Sistema_de_visibilidade_de_videos_servico.feature b/features/Sistema_de_visibilidade_de_videos_servico.feature new file mode 100644 index 0000000000..e7725b8f53 --- /dev/null +++ b/features/Sistema_de_visibilidade_de_videos_servico.feature @@ -0,0 +1,42 @@ +Feature: Sistema de Visibilidade de Vídeos (Serviço) + + Scenario: Obter dados de vídeo com sucesso + Given existe um vídeo com id "101" no sistema + And esse vídeo tem título "Stranger Things - Piloto" + And duração "45 minutos" + When faço uma requisição GET para "/videos/101" + Then o status da resposta é "200 OK" + And o corpo da resposta (JSON) contém: + """ + { + "videoId": "101", + "titulo": "Stranger Things - Piloto", + "duracao": "45 minutos" + } + """ + + Scenario: Vídeo não encontrado + Given não existe um vídeo com id "999" + When faço uma requisição GET para "/videos/999" + Then o status da resposta é "404 Not Found" + And o corpo da resposta (JSON) indica "Vídeo não encontrado" + + Scenario: Registrar visualização de vídeo com sucesso + Given existe um vídeo com id "101" no sistema + And existe um usuário com id "1" + When faço uma requisição POST para "/videos/101/visualizacao" + And o body contém: + """ + { + "userId": "1" + } + """ + Then o status da resposta é "201 Created" + And o corpo da resposta (JSON) contém: + """ + { + "message": "Visualização registrada com sucesso", + "videoId": "101", + "userId": "1" + } + """ diff --git a/features/like.feature b/features/like.feature new file mode 100644 index 0000000000..aab40d1d11 --- /dev/null +++ b/features/like.feature @@ -0,0 +1,38 @@ +Feature: Curtir + As a usuário do sistema + I want to informar à plataforma que gostei de um conteúdo + So that i can receber recomendações semelhantes à aquele conteúdo + +Scenario: Clicar no botão de curtida + Given A lista “Séries Curtidas” está vazia + And O usuário “Ykaro” está na tela de visualização da série “The Boys” + When O usuário “Ykaro” clica no botão “curtir” + And O usuário “Ykaro” acessa a lista “séries curtidas” + Then “The boys” está na lista “séries curtidas” + +Scenario: Clicar novamente no botão de curtida + Given A série “The Boys” está na lista “séries curtidas” + And O usuário “Ykaro” está na tela de visualização da série “The Boys” + When O usuário “Ykaro” clica no botão “curtir” + And O usuário “Ykaro” acessa a lista “séries curtidas” + Then A lista “séries curtidas” está vazia + +Scenario: Obter a lista de séries curtidas pelo usuário + Given O usuário "Ykaro" está logado no sistema + When Uma requisição “GET” é enviada para “/usuario/seriesCurtidas” + Then O status da resposta deve ser “200” + And O JSON da resposta contém a lista “séries curtidas” + And A lista contém o item “Breaking Bad” + +Scenario: Adicionar item a lista de “Séries Curtidas” + Given A lista ‘séries curtidas’ do usuário ‘Ykaro’ contém o item “Breaking Bad” + And O usuário curtiu a série “The Boys” + When Uma requisição “PUT” é enviada para “/usuario/seriesCurtidas/theBoys” com o item “The Boys” + Then O status da resposta deve ser “200” + And A lista “séries curtidas” contém os itens “Breaking Bad” e “The Boys” + +Scenario: Remover item da lista de “Séries Curtidas” + Given A lista “Séries curtidas” do usuário “Ykaro” contém os itens “Breaking Bad” e “The Boys” + When Uma requisição “PUT” é enviada para “/usuario/seriesCurtidas/theBoys” com o item “The Boys” + Then O status da resposta deve ser “200” + And A lista “séries curtidas” do usuário “Ykaro” contém o item “Breaking Bad” diff --git a/features/recomendation_like.feature b/features/recomendation_like.feature new file mode 100644 index 0000000000..ee0ef6c6c9 --- /dev/null +++ b/features/recomendation_like.feature @@ -0,0 +1,15 @@ +Feature: Recomendações com base nas curtidas (Top 10) - Ykaro dos Santos Albuquerque + As a usuário da plataforma + I want to receber recomendções + So that i can receber conteúdo de qualidade + +Scenario: Visualização da lista “Em alta” + Given O usuário “Ykaro” está na “Página Inicial” + When O usuário “Ykaro” acessa a lista “Em Alta” + Then Estão presentes na lista “The Boys”, “Breaking Bad”, “The Last of Us”, “Marley e Eu”, “Vingadores: Guerra Infinita”, “The Walking Dead”, “The Batman”, “Ainda estou aqui”, “La La Land”, “Valente” + +Scenario: Exibindo recomendações com base nas curtidas + Given O usuário "Ykaro" está logado no sistema + When Uma requisição “GET” é enviada para “/user/top10/Sistema” + Then O status da resposta deve ser “200” + And O JSON da resposta contém a lista “Top 10” com os itens “The Boys”, “Breaking Bad”, “The Last of Us”, “Marley e Eu”, “Vingadores: Guerra Infinita”, “The Walking Dead”, “The Batman”, “Ainda estou aqui”, “La La Land”, “Valente” diff --git a/features/recomendation_views.feature b/features/recomendation_views.feature new file mode 100644 index 0000000000..5c316c5f45 --- /dev/null +++ b/features/recomendation_views.feature @@ -0,0 +1,37 @@ +Feature: Recomendação com base nas visualizações do usuário + As a usuário da plataforma + I want to receber recomendções que sejam compatíveis com os meus gostos + So that i can aproveitar as recomendações da plataforma + +Scenario: Exibir recomendações para um usuário com histórico de visualizações (GUI) + Given O usuário “Ykaro” assistiu aos filmes “Capitão América”, “Homem de Ferro” e “Anabelle” + When O usuário “Ykaro” acessa a “Página Inicial” + Then Está presente uma lista de título “For You” em que “Homem Aranha, de volta ao lar”, “The Batman”, “The boys” e “Deadpool 2” aparecem disponíveis + And Está presente outra lista de título “For You” em que “Anabelle 3”, “A Freira”, “Sorriso” e “A substância” aparecem disponíveis + +Scenario: Exibir recomendações para um usuário sem histórico de visualizações (GUI) + Given O usuário “Ykaro” não assistiu a nenhum filme na plataforma + When O usuário “Ykaro” acessa a “página inicial” + Then É exibida a mensagem “Seja bem-vindo à plataforma, atualmente as suas recomendações estão usando métricas gerais dos nossos usuários. Elas vão ficar cada vez mais a sua cara com o passar do tempo :)” + And Está presente uma lista de título “For You” em que “Moana 2”, “Zootopia”, “The Soul”, “Valente” e “Enrolados” aparecem disponíveis + And Está presente outra lista de título “For You” em que “Robin-Hood - A origem”, “Uncharted - Fora do mapa”, “Os caçadores da arca perdida” e “Dungeons & Dragons - Honra entre rebeldes” aparecem disponíveis + +Scenario: Exibindo recomendações para o usuário com histórico de visualizações (gênero 1) (Service) + Given A lista “historicoDeVisualizacao” do usuário “Ykaro” contém os itens “O incrível Hulk”, “Homem-Formiga” + When Uma requisição “GET” é enviada para “/sistema/series/acao” + Then O status da resposta deve ser “200” + And O JSON da resposta contém os itens “Homem Aranha, de volta ao lar”, “The Batman” + +Scenario: Exibindo recomendações para o usuário com histórico de visualizações (gênero 2) (Service) + Given A lista “historicoDeVisualizacao” do usuário “Ykaro” contém o item “Invocação do mal” + When Uma requisição “GET” é enviada para “/sistema/series/terror” + Then O status da resposta deve ser “200” + And O JSON da resposta contém os itens “A Freira”, “Sorria” + +Scenario: Exibindo recomendações para o usuário sem histórico de visualizações (Service) + Given A lista “historicoDeVisualizacao” do usuário está vazia + When Uma requisição “GET” é enviada para “/sistena/series/” + Then O status da resposta deve ser “200” + And O JSON da resposta contém os itens “Moana 2”, “Zootopia” + + diff --git a/features/user_register_gui.feature b/features/user_register_gui.feature new file mode 100644 index 0000000000..35af4e986d --- /dev/null +++ b/features/user_register_gui.feature @@ -0,0 +1,88 @@ +Feature: Cadastro e manutenção de usuários + As a usuário do sistema + I want to criar, atualizar, e deletar minha informação pessoal + so that eu possa gerenciar os detalhes da minha conta de forma segura e os manter atualizados + +Scenario: Cadastro de usuário bem-sucedido +Given o usuário está na página de “Cadastro de usuários” +And o usuário “LucasHenrique” não fez cadastro +When o usuário preenche os seguintes dados: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Senha | X | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +Then o usuário recebe um aviso de cadastro bem-sucedido +And o usuário é redirecionado para a página de “Login” + +Scenario: Alteração de dados cadastrais bem-sucedido +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And vê que os seus dados são: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +When o usuário altera sua “Data de nascimento”, de “20/02/2004” por “20/02/1994” +And insere sua senha correta: “X” +Then ele recebe um aviso de alteração cadastral bem-sucedida +And o usuário pode ver que seus dados são: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/1994 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | + +Scenario: Alteração de dados cadastrais malsucedido +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And vê que seus dados são: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +When o usuário altera sua “Data de nascimento”, de “20/02/2004” por “20/02/1994” +And insere sua senha incorreta: “Y” +Then ele recebe um aviso de alteração cadastral malsucedida +And o usuário pode ver que seus dados agora são: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | + +Scenario: Remoção de usuário bem-sucedido +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And vê que seus dados são: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +When seleciona em “deletar minha conta” +Then o sistema pede ao usuário para inserir sua senha +When insere sua senha correta: “X” +Then ele recebe um aviso de remoção cadastral bem-sucedido +And o usuário é redirecionado para página de “Autenticação” + +Scenario: Remoção de usuário malsucedida +Given o usuário “LucasHenrique” está na página de “Dados cadastrais” +And vê que seus dados são: +| Campo | Valor | +| Nome completo | Lucas Henrique | +| Usuário/Email | LucasHenrique | +| Data de nascimento | 20/02/2004 | +| Gênero | Masculino | +| Foto | minha-foto.jpg | +When seleciona em “deletar minha conta” +Then o sistema pede ao usuário para inserir sua senha +When insere sua senha incorreta: “Y” +Then ele recebe um aviso de senha incorreta +And o usuário é solicitado para inserir a senha novamente \ No newline at end of file diff --git a/frontend/.cypress-cucumber-preprocessorrc.json b/frontend/.cypress-cucumber-preprocessorrc.json new file mode 100644 index 0000000000..21129d910b --- /dev/null +++ b/frontend/.cypress-cucumber-preprocessorrc.json @@ -0,0 +1,7 @@ +{ + "nonGlobalStepDefinitions": true, + "stepDefinitions": [ + "cypress/e2e/features/[filepath]/*.ts", + "cypress/e2e/features/common-step-definitions/*.ts" + ] +} diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000000..e66a4d53e4 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1 @@ +VITE_API_URL=http://127.0.0.1:5001/api \ No newline at end of file diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs new file mode 100644 index 0000000000..ff09c7e520 --- /dev/null +++ b/frontend/.eslintrc.cjs @@ -0,0 +1,15 @@ +module.exports = { + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", + ], + parser: "@typescript-eslint/parser", + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": "warn", + "@typescript-eslint/no-explicit-any": "off", + }, +}; diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000000..cb6857f740 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,39 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +coverage +cypress-coverage +.nyc_output +cypress/screenshots +cypress/videos +cypress/downloads + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Env +.env +.env.local +.env.test +.env.testing +.env.development +.env.staging +.env.production \ No newline at end of file diff --git a/frontend/.husky/pre-commit b/frontend/.husky/pre-commit new file mode 100755 index 0000000000..0a86f55779 --- /dev/null +++ b/frontend/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run lint diff --git a/frontend/.husky/pre-push b/frontend/.husky/pre-push new file mode 100755 index 0000000000..9c7ed53f5b --- /dev/null +++ b/frontend/.husky/pre-push @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run test \ No newline at end of file diff --git a/frontend/.nycrc.json b/frontend/.nycrc.json new file mode 100644 index 0000000000..357c3464d5 --- /dev/null +++ b/frontend/.nycrc.json @@ -0,0 +1,8 @@ +{ + "all": true, + "extends": "@istanbuljs/nyc-config-typescript", + "check-coverage": true, + "report-dir": "./cypress-coverage", + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["cypress/**/*.*", "**/*.d.ts", "**/*.cy.tsx", "**/*.cy.ts"] +} diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000000..9b8f64044e --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,105 @@ +# ESS Front-end React + +This is the Front-end base project in React for the Software and Systems Engineering discipline, offered by the Informatics Center (CIn) of the Federal University of Pernambuco (UFPE). + +## Table of Contents + +1. [Getting Started](##getting-started) +2. [Running the tests](#running-the-tests) +3. [Scripts](#scripts) +4. [Dependencies](#dependencies) +5. [Architecture](#architecture) + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. + +### Prerequisites + +To run this project, you'll need to have the following software installed on your system: + +- Node.js +- npm (Node Package Manager) + +### Installing + +Clone the repository and install the dependencies by running the following command in the project directory: + +``` +npm install +``` + +### First time running ? + +Run the follow scripts + +``` +chmod +x .husky/pre-commit +chmod +x .husky/pre-push +``` + +``` +npm run +``` + +### Environment + +This project uses `.env` files to manage environment variables. You can create a `.env.development` file in the project directory and set the environment variables in the file (iou can create it from .`env.example`). The `env` script in the `package.json` file uses the `env-cmd` package to load the environment variables from the `.env.development` file. + +### Running the App + +To start the app, run the following command: + +``` +npm run dev +``` + +This command will run the React app in development with Vite.js script + +## Running the tests + +There are two types of tests configured in the base project: unit tests using Vitest with React Testing Library and E2E acceptance tests using Cypress with Cucumber. It's interesting to create the **.env.testing** at the root of the project the same way it was created to run in development, changing the necessary values. + +To run unit tests + +``` +npm run test +``` + +To run E2E tests in **interactive mode** + +``` +npm run cy:e2e-interactive +``` + +To run E2E tests in **headless mode** + +``` +npm run cy:e2e-headless +``` + +**Note:** To run E2E tests that test flows that involve connecting to the back-end, such as login, the back-end must be running. Remember to provide a valid URL for the backend in the **.env.testing** file. + +## Scripts + +The following scripts are available in the `package.json` file: + +- `dev`: Runs the app in development mode. +- `build`: Compiles the TypeScript code. +- `test`: Runs the Vitest tests for the project. +- `prettier`: Formats the code using Prettier. +- `lint`: Lints the code using ESLint. + +## Dependencies + +The following dependencies are used in the project: + +- [vite](https://github.com/microsoft/TypeScript): Vite is a new breed of frontend build tooling that significantly improves the frontend development experience. +- [react](https://github.com/facebook/react): React is a JavaScript library for building user interfaces. +- [react-router-dom](https://github.com/remix-run/react-router): React Router is a lightweight, fully-featured routing library for the React JavaScript library. +- [react-hook-form](https://github.com/react-hook-form/react-hook-form): React Hook Form is a library for React that simplifies form validation and input data handling. +- [zod](https://github.com/colinhacks/zod): Zod is a TypeScript-first schema declaration and validation library. I'm using the term "schema" to broadly refer to any data type, from a simple string to a complex nested object. + +## Architecture + +To understand and learn more details about the structure of the project, click [here](./docs/architecture-pattern.md) to be redirected to the README that contains this information. diff --git a/frontend/cinevideo/.gitignore b/frontend/cinevideo/.gitignore new file mode 100644 index 0000000000..f650315f30 --- /dev/null +++ b/frontend/cinevideo/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/frontend/cinevideo/app/filme/[id]/page.tsx b/frontend/cinevideo/app/filme/[id]/page.tsx new file mode 100644 index 0000000000..cc12ecc7d6 --- /dev/null +++ b/frontend/cinevideo/app/filme/[id]/page.tsx @@ -0,0 +1,133 @@ +"use client" + +import { getMovieById } from "@/lib/movie-data" +import Link from "next/link" +import Image from "next/image" +import { useState, useEffect } from "react" +import { Heart, ArrowLeft, Play } from "lucide-react" + +export default function MoviePage({ params }: { params: { id: string } }) { + const userId = "Ykaro" + const movieId = params.id + const movie = getMovieById(movieId) + const [likedMovies, setLikedMovies] = useState([]) // IDs dos filmes curtidos + const [liked, setLiked] = useState(false) // Estado do botão curtir + + // Buscar os filmes curtidos ao carregar a página + useEffect(() => { + const fetchLikedMovies = async () => { + try { + const response = await fetch(`http://localhost:4000/user/seriesCurtidas/${userId}`) + if (!response.ok) { + throw new Error("Erro ao buscar filmes curtidos") + } + const data = await response.json() + setLikedMovies(data) // Atualiza a lista de IDs dos filmes curtidos + } catch (error) { + console.error("Erro ao buscar filmes curtidos:", error) + } + } + + fetchLikedMovies() + }, [userId]) + + // Verificar se o filme atual está curtido após o estado `likedMovies` ser atualizado + useEffect(() => { + setLiked(likedMovies.includes(movieId)) // Se o ID do filme estiver na lista, define `liked` como true + }, [likedMovies, movieId]) + + // Função para curtir/descurtir um filme + const toggleLike = async () => { + try { + const url = liked + ? `http://localhost:4000/user/descurtir/${userId}` + : `http://localhost:4000/user/curtir/${userId}` + + const response = await fetch(url, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ serie: movieId }) + }) + + if (!response.ok) { + throw new Error("Erro ao atualizar curtida") + } + + setLiked(!liked) // Atualiza o estado local se a requisição for bem-sucedida + setLikedMovies((prev) => + liked ? prev.filter((id) => id !== movieId) : [...prev, movieId] + ) + } catch (error) { + console.error("Erro ao curtir/descurtir o filme:", error) + } + } + + if (!movie) { + return ( +
+

Filme não encontrado

+ Voltar para a página inicial +
+ ) + } + + return ( +
+
+
+ + + Voltar para a página inicial + +
+ +
+
+
+ {`${movie.title} +
+
+ +
+

{movie.title}

+
+ {movie.year} + {movie.duration} +
+ + {movie.rating}/5 +
+
+ +
+

Sinopse:

+

{movie.description}

+
+ +
+ + + +
+
+
+
+
+ ) +} diff --git a/frontend/cinevideo/app/genero/[id]/loading.tsx b/frontend/cinevideo/app/genero/[id]/loading.tsx new file mode 100644 index 0000000000..cec067bd4f --- /dev/null +++ b/frontend/cinevideo/app/genero/[id]/loading.tsx @@ -0,0 +1,4 @@ +export default function Loading() { + return null +} + diff --git a/frontend/cinevideo/app/genero/[id]/page.tsx b/frontend/cinevideo/app/genero/[id]/page.tsx new file mode 100644 index 0000000000..60e1b466f7 --- /dev/null +++ b/frontend/cinevideo/app/genero/[id]/page.tsx @@ -0,0 +1,89 @@ +"use client" + +import { getMoviesByGenre } from "@/lib/movie-data" +import { MovieCard } from "@/components/cinevideo" +import Link from "next/link" +import { Search } from "lucide-react" + +export default function GenrePage({ params }: { params: { id: string } }) { + // Obtém o ID do gênero da URL + const genreId = params.id + // Busca filmes do gênero selecionado + const movies = getMoviesByGenre(genreId) + + // Mapeamento de IDs de gênero para nomes em português + const genreNames: Record = { + acao: "Ação", + aventura: "Aventura", + terror: "Terror", + suspense: "Suspense", + romance: "Romance", + comedia: "Comédia", + drama: "Drama", + } + + const genreName = genreNames[genreId] || genreId + + return ( +
+ {/* Cabeçalho */} +
+
+
+ +

CINevídeo

+ +
+ +
+
+ { + if (e.key === "Enter" && e.currentTarget.value) { + window.location.href = `/?search=${encodeURIComponent(e.currentTarget.value)}` + } + }} + /> + +
+
+
+
+ + {/* Seção de filmes do gênero */} +
+
+ + Voltar + +

Filmes de {genreName}

+
+ + {/* Lista de filmes do gênero ou mensagem se não houver filmes */} + {movies.length > 0 ? ( +
+ {movies.map((movie) => ( + + ))} +
+ ) : ( +

Nenhum filme encontrado neste gênero.

+ )} +
+
+ ) +} + diff --git a/frontend/cinevideo/app/globals.css b/frontend/cinevideo/app/globals.css new file mode 100644 index 0000000000..38de6137ae --- /dev/null +++ b/frontend/cinevideo/app/globals.css @@ -0,0 +1,49 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 100% 47.8%; + --primary-foreground: 0 0% 100%; + --secondary: 0 50% 63.5%; + --secondary-foreground: 0 0% 100%; + --muted: 0 0% 85.1%; + --muted-foreground: 0 0% 0%; + --accent: 0 100% 97.6%; + --accent-foreground: 0 0% 0%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --radius: 0; +} + +.dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 100% 47.8%; + --primary-foreground: 0 0% 100%; + --secondary: 0 50% 63.5%; + --secondary-foreground: 0 0% 100%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 100% 97.6%; + --accent-foreground: 0 0% 0%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; +} + diff --git a/frontend/cinevideo/app/homepage/page.tsx b/frontend/cinevideo/app/homepage/page.tsx new file mode 100644 index 0000000000..7bc545986e --- /dev/null +++ b/frontend/cinevideo/app/homepage/page.tsx @@ -0,0 +1,7 @@ +"use client"; + +import CineVideo from "@/components/cinevideo"; + +export default function CinevideoPage() { + return ; +} \ No newline at end of file diff --git a/frontend/cinevideo/app/layout.tsx b/frontend/cinevideo/app/layout.tsx new file mode 100644 index 0000000000..9057640dcd --- /dev/null +++ b/frontend/cinevideo/app/layout.tsx @@ -0,0 +1,20 @@ +import type { Metadata } from 'next' +import './globals.css' + +export const metadata: Metadata = { + title: 'Cinevideo', + description: 'Created with v0', + generator: 'v0.dev', +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + {children} + + ) +} diff --git a/frontend/cinevideo/app/likedSeries/page.tsx b/frontend/cinevideo/app/likedSeries/page.tsx new file mode 100644 index 0000000000..1e26c132ac --- /dev/null +++ b/frontend/cinevideo/app/likedSeries/page.tsx @@ -0,0 +1,80 @@ +"use client" + +import { useEffect, useState } from "react" +import { getMovieById } from "@/lib/movie-data" +import Link from "next/link" +import Image from "next/image" +import { ArrowLeft } from "lucide-react" + +export default function LikedMoviesPage() { + const [likedMovies, setLikedMovies] = useState([]) // IDs dos filmes curtidos + const [movieDetails, setMovieDetails] = useState([]) // Detalhes dos filmes + const userId = "Ykaro" + + useEffect(() => { + const fetchLikedMovies = async () => { + try { + const response = await fetch(`http://localhost:4000/user/seriesCurtidas/${userId}`); + if (!response.ok) { + throw new Error("Erro ao buscar filmes curtidos"); + } + const data = await response.json(); + setLikedMovies(data); // Armazenar os IDs dos filmes curtidos + } catch (error) { + console.error("Erro ao buscar filmes curtidos:", error); + } + }; + + fetchLikedMovies(); + }, [userId]) + + useEffect(() => { + const fetchMovieDetails = async () => { + const movies = await Promise.all( + likedMovies.map(async (movieId) => { + const movie = await getMovieById(movieId); + return movie; + }) + ); + setMovieDetails(movies); + }; + + if (likedMovies.length > 0) { + fetchMovieDetails(); + } + }, [likedMovies]); + + return ( +
+
+ + + Voltar para a página inicial + +
+

Filmes Curtidos

+ + {movieDetails.length === 0 ? ( +

Nenhum filme curtido ainda.

+ ) : ( +
+ {movieDetails.map((movie) => ( +
+ +
+ {movie.title} +
+

{movie.title}

+ +
+ ))} +
+ )} +
+ ) +} diff --git a/frontend/cinevideo/app/page.tsx b/frontend/cinevideo/app/page.tsx new file mode 100644 index 0000000000..ba17e374cb --- /dev/null +++ b/frontend/cinevideo/app/page.tsx @@ -0,0 +1,14 @@ +// app/page.tsx +"use client"; +import Login from "./pages/Login"; +import { useRouter } from "next/navigation"; + +export default function Home() { + const router = useRouter(); + + const handleLoginSuccess = () => { + router.push("/"); + }; + + return ; +} \ No newline at end of file diff --git a/frontend/cinevideo/app/pages/Login.tsx b/frontend/cinevideo/app/pages/Login.tsx new file mode 100644 index 0000000000..01b9a162e9 --- /dev/null +++ b/frontend/cinevideo/app/pages/Login.tsx @@ -0,0 +1,69 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import Logo from "@/components/Logo"; +import Button from "@/components/Button"; +import Input from "@/components/Input"; +import AuthContainer from "@/components/AuthContainer"; +import AuthFooter from "@/components/AuthFooter"; + +interface LoginProps { + onLoginSuccess: () => void; +} + +export default function Login({ onLoginSuccess }: LoginProps) { + const router = useRouter(); + + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + + const handleLogin = async () => { + //onLoginSuccess(); + + try { + const response = await fetch("/auth/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, password }), + }); + + if (response.ok) { + // Login bem-sucedido + router.push("/"); + } else { + // Login falhou + const errorData = await response.json(); + setError(errorData.error || "Falha no login"); + } + } catch (error) { + setError("Erro de rede ou servidor"); + } + }; + + return ( +
+ +
+ + {error &&

{error}

} + setUsername(e.target.value)} + /> + setPassword(e.target.value)} + /> + +
+ +
+
+ ); + } \ No newline at end of file diff --git a/frontend/cinevideo/app/pages/Profile_information.tsx b/frontend/cinevideo/app/pages/Profile_information.tsx new file mode 100644 index 0000000000..edd901567b --- /dev/null +++ b/frontend/cinevideo/app/pages/Profile_information.tsx @@ -0,0 +1,32 @@ +import Logo from "@/components/Logo"; +import Button from "@/components/Button"; +import Input from "@/components/Input"; +import AuthContainer from "@/components/AuthContainer"; +import BackButton from "@/components/BackButton"; +import ProfilePicture from "@/components/ProfilePicture"; + +export default function ProfileInformation() { + return ( +
+ +
+
+ + +
+ + + + + + {/* Substitua pelo caminho da imagem */} +
+ + + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/cinevideo/app/pages/Registration.tsx b/frontend/cinevideo/app/pages/Registration.tsx new file mode 100644 index 0000000000..dbd4c440e0 --- /dev/null +++ b/frontend/cinevideo/app/pages/Registration.tsx @@ -0,0 +1,28 @@ +import Logo from "@/components/Logo"; +import Button from "@/components/Button"; +import Input from "@/components/Input"; +import AuthContainer from "@/components/AuthContainer"; +import BackButton from "@/components/BackButton"; + +export default function Registration() { + return ( +
+ +
+
+ + +
+ + + + + + + + +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/cinevideo/app/profile_information/page.tsx b/frontend/cinevideo/app/profile_information/page.tsx new file mode 100644 index 0000000000..5eb5cc4574 --- /dev/null +++ b/frontend/cinevideo/app/profile_information/page.tsx @@ -0,0 +1,7 @@ +"use client"; + +import ProfileInformation from "../pages/Profile_information"; + +export default function ProfileInfRouter() { + return ; +} \ No newline at end of file diff --git a/frontend/cinevideo/app/registration/page.tsx b/frontend/cinevideo/app/registration/page.tsx new file mode 100644 index 0000000000..143df6d4af --- /dev/null +++ b/frontend/cinevideo/app/registration/page.tsx @@ -0,0 +1,7 @@ +"use client"; + +import Registration from "../pages/Registration"; + +export default function RegistrationRouter() { + return ; +} \ No newline at end of file diff --git a/frontend/cinevideo/cinevideo.tsx b/frontend/cinevideo/cinevideo.tsx new file mode 100644 index 0000000000..e7fa48df01 --- /dev/null +++ b/frontend/cinevideo/cinevideo.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { Search } from "lucide-react" +import Image from "next/image" +import Link from "next/link" + +export default function CineVideo() { + return ( +
+ {/* Header */} +
+
+
+

CINevídeo

+

Top 10

+
+ +
+
+ + +
+ +
+ + + + +
+
+
+
+ + {/* Top 10 Movies */} +
+
+ + + + +
+
+ + {/* Recommended Movies */} +
+

Recomendados

+
+ + + + +
+
+
+ ) +} + +function MovieCard({ imageUrl, alt }: { imageUrl: string; alt: string }) { + return ( +
+
+ {alt} +
+
+ ) +} + diff --git a/frontend/cinevideo/components.json b/frontend/cinevideo/components.json new file mode 100644 index 0000000000..d9ef0ae537 --- /dev/null +++ b/frontend/cinevideo/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/frontend/cinevideo/components/AuthContainer.tsx b/frontend/cinevideo/components/AuthContainer.tsx new file mode 100644 index 0000000000..a6af7d8a25 --- /dev/null +++ b/frontend/cinevideo/components/AuthContainer.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +interface AuthContainerProps { + children: React.ReactNode; +} + +export default function AutoContainer({ children }: AuthContainerProps) { + return ( +
+ {children} +
+ ); +} \ No newline at end of file diff --git a/frontend/cinevideo/components/AuthFooter.tsx b/frontend/cinevideo/components/AuthFooter.tsx new file mode 100644 index 0000000000..a3d1731aff --- /dev/null +++ b/frontend/cinevideo/components/AuthFooter.tsx @@ -0,0 +1,12 @@ +import Link from "next/link"; + +export default function AuthFooter() { + return ( +

+ Ainda não tem conta?{" "} + + Cadastre-se + +

+ ); +} \ No newline at end of file diff --git a/frontend/cinevideo/components/BackButton.tsx b/frontend/cinevideo/components/BackButton.tsx new file mode 100644 index 0000000000..bb8e0f918e --- /dev/null +++ b/frontend/cinevideo/components/BackButton.tsx @@ -0,0 +1,12 @@ +import { ArrowLeft } from "lucide-react"; +import { useRouter } from "next/navigation"; + +export default function BackButton() { + const router = useRouter(); + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/cinevideo/components/Button.tsx b/frontend/cinevideo/components/Button.tsx new file mode 100644 index 0000000000..83b541997a --- /dev/null +++ b/frontend/cinevideo/components/Button.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +interface ButtonProps { + children: React.ReactNode; + onClick?: () => void; +} + +export default function Button({ children, onClick }: ButtonProps) { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/cinevideo/components/Input.tsx b/frontend/cinevideo/components/Input.tsx new file mode 100644 index 0000000000..63c01f1808 --- /dev/null +++ b/frontend/cinevideo/components/Input.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +interface InputProps { + type: string; + placeholder: string; + value?: string; + onChange?: (e: React.ChangeEvent) => void; +} + +export default function Input({ type, placeholder }: InputProps) { + return ( + + ); +} \ No newline at end of file diff --git a/frontend/cinevideo/components/Logo.tsx b/frontend/cinevideo/components/Logo.tsx new file mode 100644 index 0000000000..892ea54c35 --- /dev/null +++ b/frontend/cinevideo/components/Logo.tsx @@ -0,0 +1,10 @@ +import { Play } from "lucide-react"; + +export default function Logo() { + return ( +
+ +

CineVideo

+
+ ); +} \ No newline at end of file diff --git a/frontend/cinevideo/components/ProfilePicture.tsx b/frontend/cinevideo/components/ProfilePicture.tsx new file mode 100644 index 0000000000..6c6f144a55 --- /dev/null +++ b/frontend/cinevideo/components/ProfilePicture.tsx @@ -0,0 +1,11 @@ +interface ProfilePictureProps { + imageUrl: string; + } + + export default function ProfilePicture({ imageUrl }: ProfilePictureProps) { + return ( +
+ Foto de perfil +
+ ); + } \ No newline at end of file diff --git a/frontend/cinevideo/components/cinevideo.tsx b/frontend/cinevideo/components/cinevideo.tsx new file mode 100644 index 0000000000..485eb3e59b --- /dev/null +++ b/frontend/cinevideo/components/cinevideo.tsx @@ -0,0 +1,260 @@ +"use client" + +import { Search } from "lucide-react" +import Image from "next/image" +import Link from "next/link" +import { useState, useEffect } from "react" +import { movieData } from "@/lib/movie-data" +import { getMovieById } from "@/lib/movie-data" + +export default function CineVideo() { + // Estado para controlar a exibição do menu de gêneros + const [showGenres, setShowGenres] = useState(false) + const [topTenMovies, setTopTenMovies] = useState([]) // IDs dos filmes do top 10 + const [recomendationsMovies, setRecomendationsMovies] = useState([]) // IDs dos filmes da lista de recomendações + const [movieDetails, setMovieDetails] = useState([]) // Detalhes dos filmes + const [movieDetailsRec, setMovieDetailsRec] = useState([]) // Detalhes dos filmes + // Estado para o termo de pesquisa + const [searchTerm, setSearchTerm] = useState("") + // Estado para controlar se a pesquisa foi submetida + const [isSearching, setIsSearching] = useState(false) + + useEffect(() => { + + // Verificar se há um termo de pesquisa na URL ao carregar a página + if (typeof window !== "undefined") { + const urlParams = new URLSearchParams(window.location.search) + const searchParam = urlParams.get("search") + if (searchParam) { + setSearchTerm(searchParam) + setIsSearching(true) + } + } + + + const fetchTop10 = async () => { + try { + const response = await fetch(`http://localhost:4000/user/top10/Sistema`) + if (!response.ok) { + throw new Error("Erro ao buscar filmes do top 10") + } + const data = await response.json() + setTopTenMovies(data) // Atualiza a lista de IDs dos filmes curtidos + } catch (error) { + console.error("Erro ao buscar filmes do top 10:", error) + } + } + + const fetchRecomendations = async () => { + try { + const response = await fetch(`http://localhost:4000/user/Sistema/series`) + if (!response.ok) { + throw new Error("Erro ao buscar filmes recomendados") + } + const NewData = await response.json() + setRecomendationsMovies(NewData) // Atualiza a lista de IDs dos filmes curtidos + } catch (error) { + console.error("Erro ao buscar filmes recomendados:", error) + } + } + + fetchTop10() + fetchRecomendations() + }, []) + + useEffect(() => { + const fetchMovieDetails = async () => { + const movies = await Promise.all( + topTenMovies.map(async (movieId) => { + const movie = await getMovieById(movieId); + return movie; + }) + ); + setMovieDetails(movies); + }; + + const fetchMovieDetailsRec = async () => { + const movies = await Promise.all( + recomendationsMovies.map(async (movieId) => { + const movie = await getMovieById(movieId); + return movie; + }) + ); + setMovieDetailsRec(movies); + }; + + + + if (topTenMovies.length > 0) { + fetchMovieDetails(); + } + + + if (recomendationsMovies.length > 0) { + fetchMovieDetailsRec(); + } + + + }, [topTenMovies, recomendationsMovies]); + + // Lista de gêneros disponíveis + const genres = [ + { id: "acao", name: "Ação" }, + { id: "aventura", name: "Aventura" }, + { id: "terror", name: "Terror" }, + { id: "suspense", name: "Suspense" }, + { id: "romance", name: "Romance" }, + { id: "comedia", name: "Comédia" }, + ] + + // Função para filtrar filmes com base no termo de pesquisa + const searchResults = movieData.filter( + (movie) => + movie.title.toLowerCase().includes(searchTerm.toLowerCase()) || + movie.description.toLowerCase().includes(searchTerm.toLowerCase()), + ) + + // Seleciona os primeiros 4 filmes para exibir como Top 10 e Recomendados + const top10Movies = movieData.slice(0, 4) + const recommendedMovies = movieData.slice(0, 4) + + return ( +
+ {/* Cabeçalho */} +
+
+
+ +

CINevídeo

+ +

Top 10

+
+ +
+ {/* Barra de pesquisa */} +
+ setSearchTerm(e.target.value)} + onKeyDown={(e) => { + if (e.key === "Enter") { + setIsSearching(true) + } + }} + /> + +
+ + {/* Botões de navegação */} +
+
+ {/* Botão de gênero com dropdown */} + + + {/* Menu dropdown de gêneros */} + {showGenres && ( +
+ {genres.map((genre) => ( + setShowGenres(false)} + > + {genre.name} + + ))} +
+ )} +
+ + + +
+
+
+
+ + {/* Conteúdo principal - Resultados da pesquisa ou seções normais */} + {isSearching && searchTerm ? ( +
+
+ +

Resultados para "{searchTerm}"

+
+ + {searchResults.length > 0 ? ( +
+ {searchResults.map((movie) => ( + + ))} +
+ ) : ( +

Nenhum filme encontrado para "{searchTerm}".

+ )} +
+ ) : ( + <> + {/* Seção Top 10 Filmes */} +
+
+ {movieDetails.map((movie) => ( + + ))} +
+
+ + {/* Seção Filmes Recomendados */} +
+

Recomendados

+
+ {movieDetailsRec.map((movie) => ( + + ))} +
+
+ + )} +
+ ) +} + +// Componente de card de filme reutilizável +export function MovieCard({ movie }: { movie: any }) { + return ( + +
+
+ {/* AQUI É ONDE VOCÊ COLOCA AS IMAGENS DOS FILMES */} + {/* Substitua o valor de 'src' pelo caminho da sua imagem */} + {`${movie.title} +
+
+ + ) +} \ No newline at end of file diff --git a/frontend/cinevideo/components/theme-provider.tsx b/frontend/cinevideo/components/theme-provider.tsx new file mode 100644 index 0000000000..55c2f6eb60 --- /dev/null +++ b/frontend/cinevideo/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/frontend/cinevideo/components/ui/accordion.tsx b/frontend/cinevideo/components/ui/accordion.tsx new file mode 100644 index 0000000000..24c788c2c4 --- /dev/null +++ b/frontend/cinevideo/components/ui/accordion.tsx @@ -0,0 +1,58 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/frontend/cinevideo/components/ui/alert-dialog.tsx b/frontend/cinevideo/components/ui/alert-dialog.tsx new file mode 100644 index 0000000000..25e7b47446 --- /dev/null +++ b/frontend/cinevideo/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/frontend/cinevideo/components/ui/alert.tsx b/frontend/cinevideo/components/ui/alert.tsx new file mode 100644 index 0000000000..41fa7e0561 --- /dev/null +++ b/frontend/cinevideo/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/frontend/cinevideo/components/ui/aspect-ratio.tsx b/frontend/cinevideo/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000000..d6a5226f5e --- /dev/null +++ b/frontend/cinevideo/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/frontend/cinevideo/components/ui/avatar.tsx b/frontend/cinevideo/components/ui/avatar.tsx new file mode 100644 index 0000000000..51e507ba9d --- /dev/null +++ b/frontend/cinevideo/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/frontend/cinevideo/components/ui/badge.tsx b/frontend/cinevideo/components/ui/badge.tsx new file mode 100644 index 0000000000..f000e3ef51 --- /dev/null +++ b/frontend/cinevideo/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/frontend/cinevideo/components/ui/breadcrumb.tsx b/frontend/cinevideo/components/ui/breadcrumb.tsx new file mode 100644 index 0000000000..60e6c96f72 --- /dev/null +++ b/frontend/cinevideo/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>