diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..93f1361 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..fcfe8fb --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,9 @@ +// This configuration only applies to the package manager root. +/** @type {import("eslint").Linter.Config} */ +module.exports = { + ignorePatterns: ["apps/**", "packages/**"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: false, + }, +} diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml new file mode 100644 index 0000000..deed27a --- /dev/null +++ b/.github/workflows/deploy-staging.yml @@ -0,0 +1,71 @@ +# Copyright (c) 2024 Hemi Labs, Inc. +# Use of this source code is governed by the MIT License, +# which can be found in the LICENSE file. + +# GitHub Actions workflow to deploy to the staging environment. +name: "Staging" +on: + push: + branches: [ "master" ] + workflow_dispatch: + +concurrency: + group: "staging-deploy-${{ github.event.number || github.ref }}" + cancel-in-progress: false + +jobs: + # Build and push Docker image + docker: + name: "Docker (${{ matrix.component }})" + uses: hemilabs/actions/.github/workflows/docker.yml@main + permissions: + contents: read + packages: write # Needed to push to the GitHub Container Registry + strategy: + matrix: + component: [ "api", "web" ] + with: + version: "${{ github.sha }}" + context: "." + file: "./Dockerfile.${{ matrix.component }}" + dockerhub: true + tags: | + hemilabs/cryptochords-${{ matrix.component }}:${{ github.sha }} + ghcr.io/hemilabs/cryptochords-${{ matrix.component }}:${{ github.sha }} + secrets: + DOCKERHUB_USERNAME: "${{ secrets.DOCKER_USERNAME }}" + DOCKERHUB_PASSWORD: "${{ secrets.DOCKER_PASSWORD }}" + + deploy-registry: + name: "Deploy registry secret" + uses: hemilabs/actions/.github/workflows/docker-registry-secret.yml@main + permissions: + contents: read + with: + secret-name: "dockerhub-secret" + namespace: "cryptochords" + secrets: + KUBECONFIG: "${{ secrets.VKE_STAGING_KUBECONFIG }}" + KUBECONFIG_CONTEXT: "${{ secrets.VKE_STAGING_KUBECONFIG_CONTEXT }}" + DOCKER_USERNAME: "${{ secrets.DOCKER_USERNAME }}" + DOCKER_PASSWORD: "${{ secrets.DOCKER_PASSWORD }}" + DOCKER_EMAIL: "${{ secrets.DOCKER_EMAIL }}" + + # Deploy to Kubernetes cluster + deploy: + name: "Deploy to staging" + uses: hemilabs/actions/.github/workflows/deploy-kustomize.yml@main + needs: [ "docker", "deploy-registry" ] + permissions: + contents: read + with: + namespace: "cryptochords" + kustomize: "./infrastructure/kustomize/overlays/staging/" + environment-name: "staging" + environment-url: "https://cryptochords.letshamsterdance.xyz/" + image: | + hemilabs/cryptochords-api:${{ github.sha }} + hemilabs/cryptochords-web:${{ github.sha }} + secrets: + KUBECONFIG: "${{ secrets.VKE_STAGING_KUBECONFIG }}" + KUBECONFIG_CONTEXT: "${{ secrets.VKE_STAGING_KUBECONFIG_CONTEXT }}" diff --git a/.github/workflows/deploy-testnet.yml b/.github/workflows/deploy-testnet.yml new file mode 100644 index 0000000..6892ada --- /dev/null +++ b/.github/workflows/deploy-testnet.yml @@ -0,0 +1,106 @@ +# Copyright (c) 2024 Hemi Labs, Inc. +# Use of this source code is governed by the MIT License, +# which can be found in the LICENSE file. + +# GitHub Actions workflow to deploy to the testnet prod environment. +name: "Testnet" +on: + release: + types: [published] + +concurrency: + group: "testnet-deploy-${{ github.event.number || github.ref }}" + cancel-in-progress: false + +jobs: + # Prepare to deploy and release + prepare: + name: "Prepare" + runs-on: "ubuntu-latest" + permissions: + contents: read + outputs: + version: "${{ steps.data.outputs.version }}" + tag: "${{ steps.data.outputs.tag }}" + version_type: "${{ steps.version.outputs.type }}" + steps: + - name: "Checkout repository" + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: "Retrieve build data" + id: "data" + env: + RAW_VERSION: "${{ github.ref_name }}" + run: | + VERSION=$(echo "$RAW_VERSION" | sed -e 's/^v//') + TAG=$(echo "$RAW_VERSION" | sed -E 's/^([^v])/v\1/g') + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + + TYPE=unstable + if echo "$VERSION" | grep -Eq '^[1-9][0-9]*\.[0-9]+\.[0-9]+$'; then + TYPE=stable + fi + echo "Detected that $TAG is $TYPE" + echo "type=$TYPE" >> "$GITHUB_OUTPUT" + + # Build and push Docker image + docker: + name: "Docker (${{ matrix.component }})" + uses: hemilabs/actions/.github/workflows/docker.yml@main + needs: [ "prepare" ] + permissions: + contents: read + packages: write # Needed to push to the GitHub Container Registry + strategy: + matrix: + component: [ "api", "web" ] + with: + version: "${{ needs.prepare.outputs.version }}" + context: "." + file: "./Dockerfile.${{ matrix.component }}" + dockerhub: true + tags: | + hemilabs/cryptochords-${{ matrix.component }}:latest + hemilabs/cryptochords-${{ matrix.component }}:${{ needs.prepare.outputs.tag }} + hemilabs/cryptochords-${{ matrix.component }}:${{ github.sha }} + ghcr.io/hemilabs/cryptochords-${{ matrix.component }}:latest + ghcr.io/hemilabs/cryptochords-${{ matrix.component }}:${{ needs.prepare.outputs.tag }} + ghcr.io/hemilabs/cryptochords-${{ matrix.component }}:${{ github.sha }} + secrets: + DOCKERHUB_USERNAME: "${{ secrets.DOCKER_USERNAME }}" + DOCKERHUB_PASSWORD: "${{ secrets.DOCKER_PASSWORD }}" + + deploy-registry: + name: "Deploy registry secret" + uses: hemilabs/actions/.github/workflows/docker-registry-secret.yml@main + permissions: + contents: read + with: + secret-name: "dockerhub-secret" + namespace: "cryptochords" + secrets: + KUBECONFIG: "${{ secrets.VKE_TESTNET_KUBECONFIG }}" + KUBECONFIG_CONTEXT: "${{ secrets.VKE_TESTNET_KUBECONFIG_CONTEXT }}" + DOCKER_USERNAME: "${{ secrets.DOCKER_USERNAME }}" + DOCKER_PASSWORD: "${{ secrets.DOCKER_PASSWORD }}" + DOCKER_EMAIL: "${{ secrets.DOCKER_EMAIL }}" + + # Deploy to Kubernetes cluster + deploy: + name: "Deploy to testnet" + uses: hemilabs/actions/.github/workflows/deploy-kustomize.yml@main + needs: [ "prepare", "docker", "deploy-registry" ] + permissions: + contents: read + with: + namespace: "cryptochords" + kustomize: "./infrastructure/kustomize/overlays/testnet/" + environment-name: "testnet" + environment-url: "https://cryptochords.hemi.xyz/" + image: | + hemilabs/cryptochords-api:${{ needs.prepare.outputs.tag }} + hemilabs/cryptochords-web:${{ needs.prepare.outputs.tag }} + secrets: + KUBECONFIG: "${{ secrets.VKE_TESTNET_KUBECONFIG }}" + KUBECONFIG_CONTEXT: "${{ secrets.VKE_TESTNET_KUBECONFIG_CONTEXT }}" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9eb0bc --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# Dependencies +node_modules +.pnp +.pnp.js + +# Local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Testing +coverage + +# Turbo +.turbo + +# Vercel +.vercel + +# Build Outputs +.next/ +out/ +build +dist + + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Misc +.DS_Store +*.pem +.vscode/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9d9ab82 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,138 @@ +# Contributing to Crypto Chords + +## Thank you for your interest in contributing! + +The following describes the guidelines to contribute to the Crypto Chords dapp, documentation, API reference and code examples. +The purpose of this document is to create a contribution process that: + +- Encourages new community contributions. +- Encourages contributors to remain involved. +- Avoids unnecessary processes and bureaucracy whenever possible. +- Creates a transparent decision making process that makes it clear how contributors can be involved in - decision making. + +## Table of contents + +How can I contribute? + +1. Fork and Edit +2. About the Code +3. Reporting Bugs +4. Suggesting Enhancements +5. Pull Requests +6. Issue and Pull Request labels + +### 1. Fork and edit + +To contribute to this repository, you will need to fork it, make changes and submit a pull request. This section is a brief guide on how to do this whilst making sure your forked repository stays up to date the with the official one. + +- Fork this repository to your own GitHub account and then clone it to your local device. +- Create a new branch: `git checkout -b MY_BRANCH_NAME` +- Follow the next steps detailed in the README.md file + +### 2. About the Code + +You don't need to set any special code styling, since our repos already includes ESLinting once you clone it. Every time you commit and push your contributions, all code is stylized according to our standards. + +- Start reading our code and you'll get the hang of it. We optimize it for readability. +- This is a monorepo repository managed by [Turborepo](https://turbo.build/repo/docs) +- There is a [API](./apps/api/), a [WEB](./apps/web/) applications and both are following the [Domain Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design) principles. +- There is also a [Shared](./packages/shared/) package that contains code that both apps can use. + +### Documentation Styleguide + +Use [Markdown](https://daringfireball.net/projects/markdown) + +### Git Commit Messages + +- Use the present tense ("Add feature" not "Added feature") +- Use the imperative mood ("Move cursor to..." not "Moves cursor to...") +- Limit the first line to 72 characters or less +- Reference issues and pull requests liberally after the first line + +### 3. Reporting bugs + +First of all make sure that the bug was not already reported by searching on GitHub under `Issues`. +When you are creating a bug report, please include as many details as possible. + +General tips: + +- If you're unable to find an open issue addressing the problem, open a new one. Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behaviour that is not occurring. +- Determine which repository the problem should be reported in. +- Perform a cursory search to see if the problem has already been reported. If it has and the issue is still open, add a comment to the existing issue instead of opening a new one. +- Provide a step-by-step description of the suggested enhancement in as many details as possible. +- Provide specific examples to demonstrate the steps. Include copy/pasteable snippets (when applicable) which you use in those steps, as Markdown code blocks. +- Describe the current behaviour and explain which behaviour you expected to see instead and why. +- Include screenshots and/or animated GIFs which help you demonstrate the steps or point out the part of Crypto Chords which the issue is related to. You can use this tool to record GIFs on macOS and Windows, and [this tool](https://github.com/raiseerco/peek) on Linux. +- Specify the name and version of the OS/Browser/other applicable software you're using. + +### 4. Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for Crypto Chords, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. + +Note: Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +Before submitting an enhancement suggestion, check if there is already a filed one which provides that enhancement. + +First of all, you can validate it with the community, our official channels are: + +- [Discord](https://discord.gg/hemixyz) +- [Twitter](https://twitter.com/hemi_xyz) + +How Do I Submit A (Good) Enhancement Suggestion? + +General tips: + +- Use a clear and descriptive title to identify the suggestion. +- Provide a step-by-step description of the suggested enhancement in as many details as possible. +- Provide specific examples to demonstrate the steps. Include copy/pasteable snippets which you use in those examples, as Markdown code blocks. +- Describe the current behaviour and explain which behaviour you expected to see instead and why. +- Include screenshots and/or animated GIFs which help you demonstrate the steps or point out the part of Crypto Chords which the suggestion is related to. You can use this tool to record GIFs on macOS and Windows, and [this tool](https://github.com/raiseerco/peek) on Linux. +- Explain why this enhancement would be useful to most Crypto Chords users. +- Specify the name and version of the OS/Browser/other applicable software you're using. + +### 5. Pull Requests + +The process described here has several goals: + +- Maintain repository quality +- Fix problems that are important to users +- Engage the community in working toward the best possible product +- Enable a sustainable system for Crypto Chords's maintainers to review contributions + +Please follow these steps to have your contribution considered by the maintainers: + +- Follow the styleguides as stated in step [#2](#2-code-styleguides) +- If the pull request features a UI improvement, please include a screenshot in order to let maintainers to have a glimpse of it. +- After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing. + +`If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.` + +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + +### 6. Issue and Pull Request Labels + +#### Type of Issue and Issue State + +| Label name | Description | +| ------------- | ------------------------------------------------------------------------- | +| `enhancement` | Feature requests. | +| `bug` | Confirmed bugs or reports that are very likely to be bugs. | +| `question` | Questions more than bug reports or feature requests (e.g. how do I do X). | +| `feedback` | General feedback more than bug reports or feature requests. | + +### 6. README Template + +## Description +Describe your project here + +## Prerequisites, Dependencies, Versions +Please add anything here you've built that requires the above specifics + +## Licensing +Please state if license is CC, MIT or ISC or other (please no GPL) + +## Outside Libraries +Link any outside open source libraries used + +## Steps +Tell us how to run it locally diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 0000000..ce725af --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,18 @@ +## -*- docker-image-name: "hemilabs/cryptochords-api:1.0.0" -*- +FROM node:20 + +COPY package*.json ./ + +RUN mkdir -p /usr/src/app + +WORKDIR /usr/src/app + +COPY . . + +RUN npm install + +RUN npm run build + +EXPOSE 3000 + +CMD [ "npm", "run", "start:api" ] diff --git a/Dockerfile.web b/Dockerfile.web new file mode 100644 index 0000000..78bc63d --- /dev/null +++ b/Dockerfile.web @@ -0,0 +1,22 @@ +## -*- docker-image-name: "hemilabs/cryptochords-web:1.0.0" -*- +FROM node:20 as build + +COPY package*.json ./ + +RUN mkdir -p /usr/src/app + +WORKDIR /usr/src/app + +COPY . . + +RUN npm install + +RUN npm run build + +FROM nginx:alpine + +COPY --from=build /usr/src/app/apps/web/dist /usr/share/nginx/html + +COPY --from=build /usr/src/app/apps/web/nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2114a1f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Hemi Labs, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..67fab62 --- /dev/null +++ b/README.md @@ -0,0 +1,102 @@ +# CryptoChords +CryptoChords is a tool for visualizing activities on the [Hemi network](https://hemi.xyz). It features a web application that enables users to explore the network and visualize transactions in a user-friendly manner. + +This document provides instructions on how to set up, run, and contribute to the project. + +## Visual Representation of the Network +CryptoChords provides a visual representation of the Hemi network using a piano keyboard as a metaphor. Every time a new transaction is added to the network, a cube appears on a piano key, representing the transaction. The transaction details are displayed on the screen, and if sound is enabled, the corresponding piano note is played simultaneously. + +There are four types of transactions, each represented by a different color and associated with a different quadrant of the piano keyboard: + +- **Green**: Hemi block creation +- **Blue**: Ethereum transaction +- **Orange**: Bitcoin transaction +- **Purple**: Pop Miner transaction + +A random key within the corresponding quadrant is selected to represent each transaction. + + +## Folder Structure +The project is organized into the following folders: + +- `apps`: Contains the API and the Web application. +- `packages`: Contains the components shared between the apps. +- `infrastucture`: Contains the necessary files to deploy the project. + +## Architecture +This project is a monorepo composed of two applications: the API and the Web application and it is managed by [Turborepo](https://turbo.build/repo/docs). + +Both the API and the Web application follow the Domain-Driven Design (DDD) principles. You can find more information about the architecture in the `readme.md` file of each project. + +## Getting Started + +### Prerequisites + +- Node.js + +### Installation + +1. Clone the repository: + +```bash +git clone git@github.com:hemilabs/CryptoChords.git +``` + +2. Open the project folder: + +```bash +cd CryptoChords +``` + +3. Install the dependencies: + +```bash +npm install +``` + +4. Build the project: + +```bash +npm run build +``` + +### Running the Project + +1. On the root folder, run the following command: + +```bash +npm run dev +``` + +2. Open your browser and access the URL provided in the terminal. Normally, it is `http://localhost:5173/`. + +### Running the Tests + +To run the tests, you can use the following command: + +```bash +npm run test +``` + +### Lint the Code + +```bash +npm run lint +``` + +## Deployment +We have two environments: `stage` and `production`. +The project is automatically deployed using GitHub Actions. + +### Stage +The `stage` environment is automatically deployed when a new tag is created. The tag must follow the pattern `v*.*.*`. For example, `v1.0.0`. + +### Production +The `production` environment is automatically deployed when a Commit is pushed to the `main` branch. + +## Contributing +If you want to contribute to this project and make it better, your help is very welcome. +You can find more information about how to contribute in the [`CONTRIBUTING.md`](./CONTRIBUTING.md) file. + +## License +This project is licensed under the MIT License - see the [`LICENSE`](./LICENSE) file for details. diff --git a/apps/api/.env b/apps/api/.env new file mode 100644 index 0000000..c06ecbe --- /dev/null +++ b/apps/api/.env @@ -0,0 +1,5 @@ +# .env file + +RPC_URL=https://testnet.rpc.hemi.network/rpc +WEBSOCKET_URL=wss://testnet.rpc.hemi.network/wsrpc +USE_WEBSOCKET_NODE_L2=true diff --git a/apps/api/.eslintrc.js b/apps/api/.eslintrc.js new file mode 100644 index 0000000..d961833 --- /dev/null +++ b/apps/api/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended' + ], + ignorePatterns: ['dist', '.eslintrc.js', '**/*.spec.ts'], + parser: '@typescript-eslint/parser', +} diff --git a/apps/api/.gitignore b/apps/api/.gitignore new file mode 100644 index 0000000..b3b8837 --- /dev/null +++ b/apps/api/.gitignore @@ -0,0 +1,5 @@ +*~ +node_modules/ +dist/ +coverage/ +.vscode diff --git a/apps/api/.svg b/apps/api/.svg new file mode 100644 index 0000000..28ee07d --- /dev/null +++ b/apps/api/.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/api/README.md b/apps/api/README.md new file mode 100644 index 0000000..be2074c --- /dev/null +++ b/apps/api/README.md @@ -0,0 +1,101 @@ +# CryptoChords Api + +CryptoChords is an API developed using Domain-Driven Design (DDD) principles to facilitate interaction with the Hemi network. This document provides instructions for setting up, running, and contributing to the project. + +## Architecture + +The API is organized into the following layers: + +- Application +- Domain +- Infrastructure +- Presentation + +## Application + +This layer contains the use cases of the application. It is the entry point of the application and it is responsible for coordinating the domain layer and the infrastructure layer. + +## Domain + +This layer contains the business logic of the application. It is the core of the application and it is responsible for the entities, value objects, and domain services. + +## Infrastructure + +This layer contains the implementation of the application. It is responsible for the database and the external services. + +## Presentation + +This layer contains the user interface of the application. + +## Getting Started + +### Prerequisites + +- Node.js + +### Installation + +1. Install the dependencies: + +```bash +npm install +``` + +2. Build the project: + +```bash +npm run build +``` + +### Running the Project + +1. On the root folder, run the following command: + +```bash +npm run dev +``` + +### Running the Tests + +To run the tests, you can use the following command: + +```bash +npm run test +``` + +### Lint the Code + +```bash +npm run lint +``` + +### Test the Code Coverage + +```bash +npm run test:cov +``` + +### Environment Variables + +The environment variables are defined in the `.env` file. The following variables are used: + +- `RPC_URL`: The RPC (Remote Procedure Call) URL of the Hemi network.[Hemi Docs](https://github.com/hemilabs/infrastructure/blob/main/NETWORK_INFO.md). + +- `WEBSOCKET_URL`: The Websocket URL of the Hemi network.[Hemi Docs](https://github.com/hemilabs/infrastructure/blob/main/NETWORK_INFO.md). + +- `USE_WEBSOCKET_NODE_L2`: A boolean that indicates if the API uses the WebSocket port (default is true).[Hemi Docs](https://github.com/hemilabs/infrastructure/blob/main/NETWORK_INFO.md). + +Example of the .env file + +``` +RPC_URL=https://testnet.rpc.hemi.network/rpc +WEBSOCKET_URL=wss://testnet.rpc.hemi.network/wsrpc +USE_WEBSOCKET_NODE_L2=true +``` + +## Contribution +If you want to contribute to this project and make it better, your help is very welcome. +You can find more information about how to contribute in the [`CONTRIBUTING.md`](../../CONTRIBUTING.md) file. + +## License +This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details. diff --git a/apps/api/nodemon.json b/apps/api/nodemon.json new file mode 100644 index 0000000..be1bc22 --- /dev/null +++ b/apps/api/nodemon.json @@ -0,0 +1,6 @@ +{ + "watch": ["./**/*.ts"], + "ext": "ts", + "ignore": ["./**/*.spec.ts"], + "exec": "ts-node ./src/server.ts" +} diff --git a/apps/api/package-lock.json b/apps/api/package-lock.json new file mode 100644 index 0000000..7e686a7 --- /dev/null +++ b/apps/api/package-lock.json @@ -0,0 +1,4694 @@ +{ + "name": "cryptochords", + "version": "1.0.10", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cryptochords", + "version": "1.0.10", + "license": "ISC", + "dependencies": { + "dotenv": "^16.3.1", + "express": "^4.18.2", + "web3": "^4.3.0" + }, + "devDependencies": { + "@types/dotenv": "^8.2.0", + "@types/express": "^4.17.21", + "@types/node": "^20.11.2", + "@types/ws": "^8.5.10", + "@vitest/coverage-v8": "^1.2.0", + "eslint": "^8.56.0", + "eslint-config-google": "^0.14.0", + "nodemon": "^3.0.2", + "ts-node": "^10.9.2", + "typescript": "^5.3.3", + "vitest": "^1.2.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" + }, + "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/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "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/@esbuild/aix-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", + "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", + "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", + "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", + "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", + "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", + "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", + "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", + "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", + "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", + "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", + "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", + "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", + "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", + "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", + "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", + "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", + "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", + "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", + "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", + "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", + "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", + "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "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.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "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.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "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/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/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "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.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/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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/@rollup/rollup-android-arm-eabi": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz", + "integrity": "sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.5.tgz", + "integrity": "sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.5.tgz", + "integrity": "sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.5.tgz", + "integrity": "sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.5.tgz", + "integrity": "sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.5.tgz", + "integrity": "sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.5.tgz", + "integrity": "sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.5.tgz", + "integrity": "sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz", + "integrity": "sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.5.tgz", + "integrity": "sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.5.tgz", + "integrity": "sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.5.tgz", + "integrity": "sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.5.tgz", + "integrity": "sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scure/base": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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/@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/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-ylSC9GhfRH7m1EUXBXofhgx4lUWmFeQDINW5oLuS+gxWdfUeW4zJdeVTYVkexEW+e2VUvlZR2kGnGGipAWR7kw==", + "deprecated": "This is a stub types definition. dotenv provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "dotenv": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "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.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.2.tgz", + "integrity": "sha512-cZShBaVa+UO1LjWWBPmWRR4+/eY/JR/UIEcDlVsw3okjWEu+rB7/mH6X3B/L+qJVHDLjk9QW/y2upp9wp1yDXA==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitest/coverage-v8": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.2.0.tgz", + "integrity": "sha512-YvX8ULTUm1+zkvkl14IqXYGxE1h13OXKPoDsxazARKlp4YLrP28hHEBdplaU7ZTN/Yn6zy6Z3JadWNRJwcmyrQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.2", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "^1.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.0.tgz", + "integrity": "sha512-H+2bHzhyvgp32o7Pgj2h9RTHN0pgYaoi26Oo3mE+dCi1PAqV31kIIVfTbqMO3Bvshd5mIrJLc73EwSRrbol9Lw==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.2.0", + "@vitest/utils": "1.2.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.0.tgz", + "integrity": "sha512-vaJkDoQaNUTroT70OhM0NPznP7H3WyRwt4LvGwCVYs/llLaqhoSLnlIhUClZpbF5RgAee29KRcNz0FEhYcgxqA==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.2.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.0.tgz", + "integrity": "sha512-P33EE7TrVgB3HDLllrjK/GG6WSnmUtWohbwcQqmm7TAk9AVHpdgf7M3F3qRHKm6vhr7x3eGIln7VH052Smo6Kw==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.0.tgz", + "integrity": "sha512-MNxSAfxUaCeowqyyGwC293yZgk7cECZU9wGb8N1pYQ0yOn/SIr8t0l9XnGRdQZvNV/ZHBYu6GO/W3tj5K3VN1Q==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.0.tgz", + "integrity": "sha512-FyD5bpugsXlwVpTcGLDf3wSPYy8g541fQt14qtzo8mJ4LdEpDKZ9mQy2+qdJm2TZRpjY5JLXihXCgIxiRJgi5g==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "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/abitype": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.7.1.tgz", + "integrity": "sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==", + "peerDependencies": { + "typescript": ">=4.9.4", + "zod": "^3 >=3.19.1" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "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.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "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, + "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-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 + }, + "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/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "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.11.0", + "raw-body": "2.5.1", + "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.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "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/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "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/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "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/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/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.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "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/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "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-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "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/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/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/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=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/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/esbuild": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.11", + "@esbuild/android-arm": "0.19.11", + "@esbuild/android-arm64": "0.19.11", + "@esbuild/android-x64": "0.19.11", + "@esbuild/darwin-arm64": "0.19.11", + "@esbuild/darwin-x64": "0.19.11", + "@esbuild/freebsd-arm64": "0.19.11", + "@esbuild/freebsd-x64": "0.19.11", + "@esbuild/linux-arm": "0.19.11", + "@esbuild/linux-arm64": "0.19.11", + "@esbuild/linux-ia32": "0.19.11", + "@esbuild/linux-loong64": "0.19.11", + "@esbuild/linux-mips64el": "0.19.11", + "@esbuild/linux-ppc64": "0.19.11", + "@esbuild/linux-riscv64": "0.19.11", + "@esbuild/linux-s390x": "0.19.11", + "@esbuild/linux-x64": "0.19.11", + "@esbuild/netbsd-x64": "0.19.11", + "@esbuild/openbsd-x64": "0.19.11", + "@esbuild/sunos-x64": "0.19.11", + "@esbuild/win32-arm64": "0.19.11", + "@esbuild/win32-ia32": "0.19.11", + "@esbuild/win32-x64": "0.19.11" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "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.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "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", + "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.3", + "strip-ansi": "^6.0.1", + "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-config-google": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", + "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": 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.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.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/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": 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, + "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, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.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/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "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" + } + }, + "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 + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "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, + "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.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "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/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": 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-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.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/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "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, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "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-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/magicast": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", + "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "source-map-js": "^1.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/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.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "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/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/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": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/mlly": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", + "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "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==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodemon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz", + "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==", + "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": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "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/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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/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.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "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/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.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "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/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.5.tgz", + "integrity": "sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.5", + "@rollup/rollup-android-arm64": "4.9.5", + "@rollup/rollup-darwin-arm64": "4.9.5", + "@rollup/rollup-darwin-x64": "4.9.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.5", + "@rollup/rollup-linux-arm64-gnu": "4.9.5", + "@rollup/rollup-linux-arm64-musl": "4.9.5", + "@rollup/rollup-linux-riscv64-gnu": "4.9.5", + "@rollup/rollup-linux-x64-gnu": "4.9.5", + "@rollup/rollup-linux-x64-musl": "4.9.5", + "@rollup/rollup-win32-arm64-msvc": "4.9.5", + "@rollup/rollup-win32-ia32-msvc": "4.9.5", + "@rollup/rollup-win32-x64-msvc": "4.9.5", + "fsevents": "~2.3.2" + } + }, + "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/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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "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/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.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "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==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "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/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/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-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "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/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, + "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-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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/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 + }, + "node_modules/tinybench": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", + "integrity": "sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.1.tgz", + "integrity": "sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", + "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "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/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-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, + "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.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz", + "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", + "dev": true + }, + "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": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "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/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.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "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/v8-to-istanbul/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/vite": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.0.tgz", + "integrity": "sha512-ETnQTHeAbbOxl7/pyBck9oAPZZZo+kYnFt1uQDD+hPReOc+wCjXw4r4jHriBRuVDB5isHmPXxrfc1yJnfBERqg==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.0.tgz", + "integrity": "sha512-Ixs5m7BjqvLHXcibkzKRQUvD/XLw0E3rvqaCMlrm/0LMsA0309ZqYvTlPzkhh81VlEyVZXFlwWnkhb6/UMtcaQ==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.2.0", + "@vitest/runner": "1.2.0", + "@vitest/snapshot": "1.2.0", + "@vitest/spy": "1.2.0", + "@vitest/utils": "1.2.0", + "acorn-walk": "^8.3.1", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^1.3.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.1", + "vite": "^5.0.0", + "vite-node": "1.2.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "^1.0.0", + "@vitest/ui": "^1.0.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/web3": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-4.3.0.tgz", + "integrity": "sha512-YiLCsb5wmgJlSxRLzt7Z7H+CmlVVIKD8VaUQaZ+xKVG3Q7CpsO5Z6jmeKnlr6M9c6fDDsDnRM6G8g+nchZehbA==", + "dependencies": { + "web3-core": "^4.3.2", + "web3-errors": "^1.1.4", + "web3-eth": "^4.3.1", + "web3-eth-abi": "^4.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-eth-contract": "^4.1.4", + "web3-eth-ens": "^4.0.8", + "web3-eth-iban": "^4.0.7", + "web3-eth-personal": "^4.0.8", + "web3-net": "^4.0.7", + "web3-providers-http": "^4.1.0", + "web3-providers-ws": "^4.0.7", + "web3-rpc-methods": "^1.1.4", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-core": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-4.3.2.tgz", + "integrity": "sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g==", + "dependencies": { + "web3-errors": "^1.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-eth-iban": "^4.0.7", + "web3-providers-http": "^4.1.0", + "web3-providers-ws": "^4.0.7", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + }, + "optionalDependencies": { + "web3-providers-ipc": "^4.0.7" + } + }, + "node_modules/web3-errors": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.1.4.tgz", + "integrity": "sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==", + "dependencies": { + "web3-types": "^1.3.1" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-4.3.1.tgz", + "integrity": "sha512-zJir3GOXooHQT85JB8SrufE+Voo5TtXdjhf1D8IGXmxM8MrhI8AT+Pgt4siBTupJcu5hF17iGmTP/Nj2XnaibQ==", + "dependencies": { + "setimmediate": "^1.0.5", + "web3-core": "^4.3.0", + "web3-errors": "^1.1.3", + "web3-eth-abi": "^4.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-net": "^4.0.7", + "web3-providers-ws": "^4.0.7", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-abi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-4.1.4.tgz", + "integrity": "sha512-YLOBVVxxxLYKXjaiwZjEWYEnkMmmrm0nswZsvzSsINy/UgbWbzfoiZU+zn4YNWIEhORhx1p37iS3u/dP6VyC2w==", + "dependencies": { + "abitype": "0.7.1", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-accounts": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-4.1.0.tgz", + "integrity": "sha512-UFtAsOANsvihTQ6SSvOKguupmQkResyR9M9JNuOxYpKh7+3W+sTnbLXw2UbOSYIsKlc1mpqqW9bVr1SjqHDpUQ==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "crc-32": "^1.2.2", + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-contract": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-4.1.4.tgz", + "integrity": "sha512-tJ4z6QLgtu8EQu2sXnLA7g427oxmngnbAUh+9kJKbP6Yep/oe+z79PqJv7H3MwqwUNW9T+/FeB2PnSQSyxz6ig==", + "dependencies": { + "web3-core": "^4.3.2", + "web3-errors": "^1.1.4", + "web3-eth": "^4.3.1", + "web3-eth-abi": "^4.1.4", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-ens": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-4.0.8.tgz", + "integrity": "sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw==", + "dependencies": { + "@adraffy/ens-normalize": "^1.8.8", + "web3-core": "^4.3.0", + "web3-errors": "^1.1.3", + "web3-eth": "^4.3.1", + "web3-eth-contract": "^4.1.2", + "web3-net": "^4.0.7", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-iban": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz", + "integrity": "sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==", + "dependencies": { + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-personal": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz", + "integrity": "sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==", + "dependencies": { + "web3-core": "^4.3.0", + "web3-eth": "^4.3.1", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-net": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-4.0.7.tgz", + "integrity": "sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow==", + "dependencies": { + "web3-core": "^4.3.0", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-4.1.0.tgz", + "integrity": "sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg==", + "dependencies": { + "cross-fetch": "^4.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-ipc": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz", + "integrity": "sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==", + "optional": true, + "dependencies": { + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-ws": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz", + "integrity": "sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w==", + "dependencies": { + "@types/ws": "8.5.3", + "isomorphic-ws": "^5.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "ws": "^8.8.1" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-ws/node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/web3-rpc-methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-rpc-methods/-/web3-rpc-methods-1.1.4.tgz", + "integrity": "sha512-LTFNg4LFaeU8K9ecuT8fHDp/LOXyxCneeZjCrRYIW1u82Ly52SrY55FIzMIISGoG/iT5Wh7UiHOB3CQsWLBmbQ==", + "dependencies": { + "web3-core": "^4.3.2", + "web3-types": "^1.3.1", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.3.1.tgz", + "integrity": "sha512-8fXi7h/t95VKRtgU4sxprLPZpsTh3jYDfSghshIDBgUD/OoGe5S+syP24SUzBZYllZ/L+hMr2gdp/0bGJa8pYQ==", + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.1.0.tgz", + "integrity": "sha512-+VJWR6FtCsgwuJr5tvSvQlSEG06586df8h2CxGc9tcNtIDyJKNkSDDWJkdNPvyDhhXFzQYFh8QOGymD1CIP6fw==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.1.4", + "web3-types": "^1.3.1", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-validator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.3.tgz", + "integrity": "sha512-fJbAQh+9LSNWy+l5Ze6HABreml8fra98o5+vS073T35jUcLbRZ0IOjF/ZPJhJNbJDt+jP1vseZsc3z3uX9mxxQ==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "util": "^0.12.5", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "zod": "^3.21.4" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "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" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..aa14c32 --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,36 @@ +{ + "name": "crypto-chords-api", + "version": "1.0.11", + "description": "CryptoChords API", + "main": "src/server.ts", + "scripts": { + "test": "vitest run src", + "test:cov": "vitest run src --coverage", + "test:watch": "vitest watch src", + "dev": "nodemon", + "build": "tsc -p tsconfig.build.json", + "lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0", + "start:api": "ts-node ./src/server.ts" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@typescript-eslint/eslint-plugin": "^6.20.0", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "web3": "^4.3.0", + "@cryptochords/shared": "*" + }, + "devDependencies": { + "@types/dotenv": "^8.2.0", + "@types/express": "^4.17.21", + "@types/node": "^20.11.2", + "@types/ws": "^8.5.10", + "@vitest/coverage-v8": "^1.2.0", + "eslint": "^8.56.0", + "nodemon": "^3.0.2", + "ts-node": "^10.9.2", + "typescript": "^5.3.3", + "vitest": "^1.2.0" + } +} diff --git a/apps/api/src/application/helpers/BroadcastToClients.ts b/apps/api/src/application/helpers/BroadcastToClients.ts new file mode 100644 index 0000000..25fb9bc --- /dev/null +++ b/apps/api/src/application/helpers/BroadcastToClients.ts @@ -0,0 +1,12 @@ +import WebSocket, { WebSocketServer } from 'ws'; +import { L2Block } from '@cryptochords/shared'; + +const BroadcastToClients = (wss: WebSocketServer, l2Block: L2Block): void => { + wss.clients.forEach((client: WebSocket) => { + if (client.readyState === WebSocket.OPEN) { + client.send(JSON.stringify(l2Block.toJSON())); + } + }); +}; + +export default BroadcastToClients diff --git a/apps/api/src/application/helpers/broadcastToClients.spec.ts b/apps/api/src/application/helpers/broadcastToClients.spec.ts new file mode 100644 index 0000000..ff56996 --- /dev/null +++ b/apps/api/src/application/helpers/broadcastToClients.spec.ts @@ -0,0 +1,30 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { describe, it, expect, vi } from 'vitest'; +import WebSocket, { WebSocketServer } from 'ws'; +import BroadcastToClients from './BroadcastToClients'; + +describe('BroadcastToClients', () => { + it('should send a message to all connected clients', () => { + const mockSend = vi.fn(); + const wss: any = new WebSocketServer({ noServer: true }); + wss.clients = new Set([ + { + readyState: WebSocket.OPEN, + send: mockSend, + }, + { + readyState: WebSocket.CLOSED, + send: mockSend, + }, + ]); + + const l2Block = { + toJSON: () => ({ key: 'value' }), + }; + + BroadcastToClients(wss, l2Block as any); + + expect(mockSend).toHaveBeenCalledTimes(1); + expect(mockSend).toHaveBeenCalledWith(JSON.stringify({ key: 'value' })); + }); +}); diff --git a/apps/api/src/application/polling/pollingService.spec.ts b/apps/api/src/application/polling/pollingService.spec.ts new file mode 100644 index 0000000..42682ae --- /dev/null +++ b/apps/api/src/application/polling/pollingService.spec.ts @@ -0,0 +1,49 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { WebSocketServer } from 'ws'; +import { EventEmitter } from 'events'; +import BroadcastToClients from '../helpers/BroadcastToClients'; +import { TxTypesEnum } from '@cryptochords/shared'; +import { PollingService } from './pollingService'; + +vi.mock('ws'); +vi.mock('../helpers/BroadcastToClients', () => ({ + __esModule: true, + default: vi.fn(), +})); + +class MockBlockRepository extends EventEmitter { + execute = vi.fn(); + stop = vi.fn(); +} + +describe('PollingService', () => { + let pollingService: PollingService; + let mockBlockRepository: MockBlockRepository; + let mockWss: WebSocketServer; + + beforeEach(() => { + mockBlockRepository = new MockBlockRepository(); + mockWss = new WebSocketServer({ noServer: true }); + pollingService = new PollingService(mockBlockRepository); + }); + + it('should execute and handle Block and Eth events', () => { + const websocketUrl = 'ws://test.url'; + const mockL2Block = { key: 'Value' }; + + pollingService.execute(mockWss, websocketUrl); + + expect(mockBlockRepository.execute).toHaveBeenCalledWith(websocketUrl); + + mockBlockRepository.emit(TxTypesEnum.Block, mockL2Block); + mockBlockRepository.emit(TxTypesEnum.Eth, mockL2Block); + + expect(BroadcastToClients).toHaveBeenCalledWith(mockWss, mockL2Block); + expect(BroadcastToClients).toHaveBeenCalledTimes(2); + }); + + it('should stop the repository on stop', () => { + pollingService.stop(); + expect(mockBlockRepository.stop).toHaveBeenCalled(); + }); +}); diff --git a/apps/api/src/application/polling/pollingService.ts b/apps/api/src/application/polling/pollingService.ts new file mode 100644 index 0000000..fcd1666 --- /dev/null +++ b/apps/api/src/application/polling/pollingService.ts @@ -0,0 +1,19 @@ +import { WebSocketServer } from "ws"; +import { BlockRepository } from "../../domain/repositories/BlockRepository"; +import BroadcastToClients from "../helpers/BroadcastToClients"; +import { TxTypesEnum } from "@cryptochords/shared"; +import { L2Block } from "@cryptochords/shared"; +export class PollingService { + constructor(private blockRepository: BlockRepository) {} + + execute(wss: WebSocketServer, url: string): void { + this.blockRepository.execute(url) + + this.blockRepository.on(TxTypesEnum.Block, (l2Block: L2Block) => BroadcastToClients(wss, l2Block)); + this.blockRepository.on(TxTypesEnum.Eth, (l2Block: L2Block) => BroadcastToClients(wss, l2Block)); + } + + stop() { + this.blockRepository.stop() + } +} \ No newline at end of file diff --git a/apps/api/src/domain/base/Entity.spec.ts b/apps/api/src/domain/base/Entity.spec.ts new file mode 100644 index 0000000..b572ccc --- /dev/null +++ b/apps/api/src/domain/base/Entity.spec.ts @@ -0,0 +1,29 @@ +import { describe, it, expect } from 'vitest' +import { Entity } from './Entity' +import { ValueObject } from '@cryptochords/shared' + +interface TestProps { + value: string +} + +class TestEntity extends Entity { + static create(props: TestProps): TestEntity { + return new TestEntity(props) + } + + get value(): string { + return this.props.value + } +} + +describe('src/domain/base/Entity', () => { + it('should be defined', () => { + expect(Entity).toBeDefined() + }) + + it('should be an instance of ValueObject', () => { + const entity = TestEntity.create({ value: 'test'}) + + expect(entity).toBeInstanceOf(ValueObject) + }) +}) diff --git a/apps/api/src/domain/base/Entity.ts b/apps/api/src/domain/base/Entity.ts new file mode 100644 index 0000000..96d7630 --- /dev/null +++ b/apps/api/src/domain/base/Entity.ts @@ -0,0 +1,7 @@ +import { ValueObject } from '@cryptochords/shared' + +export class Entity extends ValueObject { + protected constructor(props: T) { + super(props) + } +} diff --git a/apps/api/src/domain/enums/BlockTypesEnum.spec.ts b/apps/api/src/domain/enums/BlockTypesEnum.spec.ts new file mode 100644 index 0000000..68d7a92 --- /dev/null +++ b/apps/api/src/domain/enums/BlockTypesEnum.spec.ts @@ -0,0 +1,16 @@ +import { describe, it, expect } from 'vitest'; +import { BlockTypesEnum } from './BlockTypesEnum'; + +describe('BlockTypesEnum', () => { + it('should have a LEGACY type with value "0"', () => { + expect(BlockTypesEnum.LEGACY).toBe('0'); + }); + + it('should have an EIP2930 type with value "1"', () => { + expect(BlockTypesEnum.EIP2930).toBe('1'); + }); + + it('should have an EIP1559 type with value "2"', () => { + expect(BlockTypesEnum.EIP1559).toBe('2'); + }); +}); diff --git a/apps/api/src/domain/enums/BlockTypesEnum.ts b/apps/api/src/domain/enums/BlockTypesEnum.ts new file mode 100644 index 0000000..89b13d3 --- /dev/null +++ b/apps/api/src/domain/enums/BlockTypesEnum.ts @@ -0,0 +1,5 @@ +export enum BlockTypesEnum { + LEGACY = '0', + EIP2930 = '1', + EIP1559 = '2', +} diff --git a/apps/api/src/domain/errors/InvalidAddressError.spec.ts b/apps/api/src/domain/errors/InvalidAddressError.spec.ts new file mode 100644 index 0000000..858d3a5 --- /dev/null +++ b/apps/api/src/domain/errors/InvalidAddressError.spec.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' +import { DomainError } from '@cryptochords/shared' +import { InvalidAddressError } from './InvalidAddressError' + +describe('src/domain/errors/InvalidAddressError', () => { + it('should be defined', () => { + expect(InvalidAddressError).toBeDefined() + }) + + it('should be an instance of DomainError', () => { + const error = new InvalidAddressError() + + expect(error).toBeInstanceOf(DomainError) + }) + + describe('constructor', () => { + it('should set error code to INVALID_ADDRESS', () => { + const error = new InvalidAddressError() + + expect(error.code).toBe('INVALID_ADDRESS') + }) + }) +}) diff --git a/apps/api/src/domain/errors/InvalidAddressError.ts b/apps/api/src/domain/errors/InvalidAddressError.ts new file mode 100644 index 0000000..8408637 --- /dev/null +++ b/apps/api/src/domain/errors/InvalidAddressError.ts @@ -0,0 +1,7 @@ +import { DomainError } from '@cryptochords/shared' + +export class InvalidAddressError extends DomainError { + constructor() { + super('INVALID_ADDRESS', true) + } +} diff --git a/apps/api/src/domain/repositories/BlockRepository.ts b/apps/api/src/domain/repositories/BlockRepository.ts new file mode 100644 index 0000000..019cb6a --- /dev/null +++ b/apps/api/src/domain/repositories/BlockRepository.ts @@ -0,0 +1,6 @@ +import { EventEmitter } from 'events'; + +export interface BlockRepository extends EventEmitter { + execute(url: string): void + stop(): void +} diff --git a/apps/api/src/infrastructure/repositories/blockPolling.spec.ts b/apps/api/src/infrastructure/repositories/blockPolling.spec.ts new file mode 100644 index 0000000..ba45d8f --- /dev/null +++ b/apps/api/src/infrastructure/repositories/blockPolling.spec.ts @@ -0,0 +1,69 @@ +import Web3 from 'web3'; +import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest'; +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum'; +import { BlockPollingRepository } from './blockPolling'; + +vi.mock('@cryptochords/shared', () => ({ + TxType: { + create: vi.fn().mockReturnValue({}), + }, + TxTypesEnum: { + Block: 'Block', + Eth: 'Eth', + }, + Address: { + create: vi.fn().mockReturnValue({}), + }, + L2Block: { + create: vi.fn().mockReturnValue({}), + }, +})); + +describe('BlockPollingRepository', () => { + let blockPollingRepository: BlockPollingRepository; + let mockWeb3: Web3; + + beforeEach(() => { + blockPollingRepository = new BlockPollingRepository(); + mockWeb3 = new Web3(); + mockWeb3.eth.getBlockNumber = vi.fn().mockResolvedValue(100); + mockWeb3.eth.getBlock = vi.fn().mockResolvedValue({ + hash: '0xhash', + transactions: [], + }); + vi.useFakeTimers(); + + vi.spyOn(blockPollingRepository, 'emit'); + }); + + afterEach(() => { + vi.runOnlyPendingTimers(); + vi.useRealTimers(); + vi.clearAllMocks(); + }); + + it('should start and stop polling correctly', () => { + blockPollingRepository.execute('http://localhost:8545', 1000); + expect(vi.getTimerCount()).toBe(1); + blockPollingRepository.stop(); + expect(vi.getTimerCount()).toBe(0); + }); + + it('should emit "Block" event for new blocks with no transactions', async () => { + await blockPollingRepository.checkNewBlocks(mockWeb3); + expect(blockPollingRepository.emit).toHaveBeenCalledWith('Block', expect.anything()); + }); + + it('should emit "Block" and "Eth" events for new blocks with EIP1559 transactions', async () => { + mockWeb3.eth.getBlock = vi.fn().mockResolvedValue({ + hash: '0xnewhash', + transactions: [ + { type: BlockTypesEnum.EIP1559, from: '0xSomeAddress' }, + ], + }); + + await blockPollingRepository.checkNewBlocks(mockWeb3); + expect(blockPollingRepository.emit).toHaveBeenCalledWith('Block', expect.anything()); + expect(blockPollingRepository.emit).toHaveBeenCalledWith('Eth', expect.anything()); + }); +}); diff --git a/apps/api/src/infrastructure/repositories/blockPolling.ts b/apps/api/src/infrastructure/repositories/blockPolling.ts new file mode 100644 index 0000000..60f144e --- /dev/null +++ b/apps/api/src/infrastructure/repositories/blockPolling.ts @@ -0,0 +1,61 @@ +import { EventEmitter } from "stream"; +import { BlockRepository } from "../../domain/repositories/BlockRepository"; +import Web3 from "web3"; +import { TxTypesEnum } from "@cryptochords/shared"; +import { TxType } from "@cryptochords/shared"; +import { Address } from "@cryptochords/shared"; +import { BlockTypesEnum } from "../../domain/enums/BlockTypesEnum"; +import { L2Block } from "@cryptochords/shared"; + +export class BlockPollingRepository extends EventEmitter implements BlockRepository { + private latestBlockNumber = BigInt(0) + private pollingIntervalId: NodeJS.Timeout | null = null; + + execute(rpcUrl: string, pollingInterval = 5000): void { + const web3 = new Web3(rpcUrl); + this.poll(web3, pollingInterval) + } + + stop() { + if (this.pollingIntervalId) { + clearInterval(this.pollingIntervalId); + this.pollingIntervalId = null; + } + console.log('Polling stopped.'); + } + + async checkNewBlocks(web3: Web3): Promise { + console.log('Checking for new block'); + const currentBlockNumber = await web3.eth.getBlockNumber(); + + if (currentBlockNumber > this.latestBlockNumber) { + this.latestBlockNumber = currentBlockNumber; + + const block = await web3.eth.getBlock(currentBlockNumber, true); + if (block) { + console.log('New block header:', block.hash); + this.emit(TxTypesEnum.Block, L2Block.create({ + txType: TxType.create(TxTypesEnum.Block), + address: Address.create(block.hash ? block.hash.toString() : '') + })); + + if (block.transactions) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + block.transactions.forEach((tx: any) => { + if (tx.type.toString() === BlockTypesEnum.EIP1559) { + console.log('New eth tx from', tx.from); + this.emit(TxTypesEnum.Eth, L2Block.create({ + txType: TxType.create(TxTypesEnum.Eth), + address: Address.create(tx.from) + })); + } + }); + } + } + } + } + + poll(web3: Web3, pollingInterval: number) { + this.pollingIntervalId = setInterval(() => this.checkNewBlocks(web3), pollingInterval); + } +} diff --git a/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts b/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts new file mode 100644 index 0000000..22fdd12 --- /dev/null +++ b/apps/api/src/infrastructure/repositories/blockWebsocket.spec.ts @@ -0,0 +1,79 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum'; +import { BlockWebsocketRepository } from './blockWebsocket'; + +vi.mock('web3', () => ({ + __esModule: true, + default: vi.fn().mockImplementation(() => ({ + eth: { + subscribe: vi.fn().mockImplementation(() => Promise.resolve({ + on: vi.fn((event, callback) => { + if (event === 'data') { + const fakeBlockHeader = { hash: 'fakeHash' }; + callback(fakeBlockHeader); + } + }), + unsubscribe: vi.fn(), + })), + getBlock: vi.fn().mockResolvedValue({ + transactions: [{ type: BlockTypesEnum.EIP1559, from: 'fakeAddress' }], + }), + }, + })), +})); + +vi.mock('@cryptochords/shared', () => ({ + TxType: { + create: vi.fn().mockReturnValue({}), + }, + TxTypesEnum: { + Block: 'Block', + Eth: 'Eth', + }, + Address: { + create: vi.fn().mockReturnValue({}), + }, + L2Block: { + create: vi.fn().mockReturnValue({}), + }, +})); + +describe('BlockWebsocketRepository', () => { + let blockWebsocketRepository: BlockWebsocketRepository; + + beforeEach(() => { + blockWebsocketRepository = new BlockWebsocketRepository(); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should initialize and subscribe to new block headers', async () => { + blockWebsocketRepository.execute('fakeWebsocketUrl'); + await vi.waitFor(() => { + expect(blockWebsocketRepository['subscription']).toBeDefined(); + expect(blockWebsocketRepository['web3']).toBeDefined(); + expect(blockWebsocketRepository['subscription'].on).toHaveBeenCalledWith('data', expect.any(Function)); + }); + }); + + it('should stop subscription', async () => { + blockWebsocketRepository.execute('fakeWebsocketUrl'); + await vi.waitFor(() => expect(blockWebsocketRepository['subscription']).toBeDefined()); + + blockWebsocketRepository.stop(); + expect(blockWebsocketRepository['subscription']).toBeNull(); + }); + + it('should emit events for new block headers and transactions', async () => { + const emitSpy = vi.spyOn(blockWebsocketRepository, 'emit'); + blockWebsocketRepository.execute('fakeWebsocketUrl'); + + await vi.waitFor(() => expect(emitSpy).toHaveBeenCalledWith('Block', expect.anything())); + await vi.waitFor(() => expect(emitSpy).toHaveBeenCalledWith('Eth', expect.anything()), { timeout: 500 }); + + expect(emitSpy).toHaveBeenNthCalledWith(1, 'Block', expect.anything()); + expect(emitSpy).toHaveBeenNthCalledWith(2, 'Eth', expect.anything()); + }); +}); diff --git a/apps/api/src/infrastructure/repositories/blockWebsocket.ts b/apps/api/src/infrastructure/repositories/blockWebsocket.ts new file mode 100644 index 0000000..310d690 --- /dev/null +++ b/apps/api/src/infrastructure/repositories/blockWebsocket.ts @@ -0,0 +1,60 @@ +import Web3, { BlockHeaderOutput } from 'web3'; +import { EventEmitter } from 'events'; +import { TxType } from '@cryptochords/shared'; +import { TxTypesEnum } from '@cryptochords/shared'; +import { Address } from '@cryptochords/shared'; +import { BlockTypesEnum } from '../../domain/enums/BlockTypesEnum'; +import { BlockRepository } from '../../domain/repositories/BlockRepository'; +import { L2Block } from '@cryptochords/shared'; + +export class BlockWebsocketRepository extends EventEmitter implements BlockRepository { + private web3: Web3 | null = null; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private subscription: any | null = null; + + execute(websocketUrl: string): void { + this.web3 = new Web3(websocketUrl); + this.subscribeToNewBlockHeaders(); + } + + stop(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + this.subscription = null; + } + } + + private async subscribeToNewBlockHeaders(): Promise { + if (!this.web3) { + console.error('Web3 is not initialized.'); + return; + } + + this.subscription = await this.web3?.eth.subscribe('newBlockHeaders'); + this.subscription.on('data', async (blockHeader: BlockHeaderOutput) => { + await this.handleNewBlockHeader(this.web3, blockHeader); + }); + } + + private async handleNewBlockHeader(web3: Web3 | null, blockHeader: BlockHeaderOutput): Promise { + console.log('New block header:', blockHeader.hash); + this.emit(TxTypesEnum.Block, L2Block.create({ + txType: TxType.create(TxTypesEnum.Block), + address: Address.create(blockHeader.hash ? blockHeader.hash.toString() : '') + })); + + const block = await web3?.eth.getBlock(blockHeader.hash, true); + if (block && block.transactions) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + block.transactions.forEach((tx: any) => { + if (tx.type.toString() === BlockTypesEnum.EIP1559) { + console.log('New eth tx from', tx.from); + this.emit(TxTypesEnum.Eth, L2Block.create({ + txType: TxType.create(TxTypesEnum.Eth), + address: Address.create(tx.from) + })); + } + }); + } + } +} diff --git a/apps/api/src/presentation/ExpressServer.spec.ts b/apps/api/src/presentation/ExpressServer.spec.ts new file mode 100644 index 0000000..a222963 --- /dev/null +++ b/apps/api/src/presentation/ExpressServer.spec.ts @@ -0,0 +1,59 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; +import { ExpressServer } from './ExpressServer'; +import { PollingRoute } from './routes/PollingRoute'; +import express from 'express'; + +vi.mock('express', () => { + const listenMock = vi.fn((_port, callback) => { + callback(); + return { + close: vi.fn((cb) => cb()), + }; + }); + return { + __esModule: true, + default: vi.fn(() => ({ + listen: listenMock, + })), + }; +}); + +vi.mock('ws', () => ({ + Server: vi.fn().mockImplementation(() => ({ + on: vi.fn(), + })), +})); + +vi.mock('./routes/PollingRoute', () => ({ + PollingRoute: vi.fn().mockImplementation(() => ({ + initialize: vi.fn(), + stop: vi.fn(), + })), +})); + +describe('ExpressServer', () => { + let expressServer: ExpressServer; + let pollingRouteMock: PollingRoute; + + beforeEach(() => { + pollingRouteMock = new PollingRoute(); + expressServer = new ExpressServer(pollingRouteMock); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should start the server correctly', async () => { + expressServer.start(); + expect(express().listen).toHaveBeenCalled(); + expect(pollingRouteMock.initialize).toHaveBeenCalled(); + }); + + it('should stop the server correctly', async () => { + expressServer.start(); + await expressServer.stop(); + + expect(pollingRouteMock.stop).toHaveBeenCalled(); + }); +}); diff --git a/apps/api/src/presentation/ExpressServer.ts b/apps/api/src/presentation/ExpressServer.ts new file mode 100644 index 0000000..ca41794 --- /dev/null +++ b/apps/api/src/presentation/ExpressServer.ts @@ -0,0 +1,54 @@ +import express, { Express } from 'express'; +import { Server as HTTPServer } from 'http'; +import { Server as WebSocketServer } from 'ws'; +import 'dotenv/config'; +import { PollingRoute } from './routes/PollingRoute'; + +export class ExpressServer { + private pollingRoute: PollingRoute; + private api: Express; + private httpServer: HTTPServer | null = null; + private wss: WebSocketServer | null = null; + private port: string | number; + + constructor(pollingRoute: PollingRoute) { + this.pollingRoute = pollingRoute; + this.api = express(); + this.port = process.env['PORT'] || 3000; + } + + public start(): void { + this.httpServer = this.api.listen(this.port, () => { + console.log(`Express server running on port ${this.port}`); + }); + + this.wss = new WebSocketServer({ server: this.httpServer }); + + this.wss.on('connection', (ws) => { + console.log('WebSocket client connected'); + ws.on('message', (message: string) => { + console.log('Message received:', message); + }); + }); + + this.pollingRoute.initialize(this.wss); + } + + async stop(): Promise { + console.info('CryptoChords API | Closing HTTP Server') + + return await new Promise((resolve) => { + this.httpServer?.close((error) => { + if (error) { + console.error( + `CryptoChords API | Error Closing HTTP Server: ${error.message}`) + } else { + this.wss = null; + console.info('CryptoChords API | WSS Server successfully closed'); + } + this.pollingRoute.stop() + resolve() + }) + }) + } +} diff --git a/apps/api/src/presentation/routes/PollingRoute.spec.ts b/apps/api/src/presentation/routes/PollingRoute.spec.ts new file mode 100644 index 0000000..702dda9 --- /dev/null +++ b/apps/api/src/presentation/routes/PollingRoute.spec.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { WebSocketServer } from 'ws'; +import { PollingRoute } from './PollingRoute'; + +vi.mock('../../application/polling/pollingService', () => { + return { + PollingService: vi.fn().mockImplementation(() => ({ + execute: vi.fn(), + stop: vi.fn(), + })), + }; +}); + +vi.mock('ws'); +vi.mock('../../infrastructure/repositories/blockWebsocket', () => ({ + BlockWebsocketRepository: vi.fn().mockImplementation(() => ({})), +})); +vi.mock('../../infrastructure/repositories/blockPolling', () => ({ + BlockPollingRepository: vi.fn().mockImplementation(() => ({})), +})); + +describe('PollingRoute', () => { + let wss: WebSocketServer; + + beforeEach(() => { + vi.clearAllMocks(); + wss = new WebSocketServer({ noServer: true }); + }); + + it('should correctly initialize with WebSocket or Polling based on environment variable', async () => { + process.env['USE_WEBSOCKET_NODE_L2'] = 'true'; + let pollingRoute = new PollingRoute(); + pollingRoute.initialize(wss); + expect(vi.mocked(pollingRoute['pollingService'].execute)).toHaveBeenCalled(); + + vi.clearAllMocks(); + + delete process.env['USE_WEBSOCKET_NODE_L2']; + pollingRoute = new PollingRoute(); + pollingRoute.initialize(wss); + + expect(vi.mocked(pollingRoute['pollingService'].execute)).toHaveBeenCalled(); + }); +}); diff --git a/apps/api/src/presentation/routes/PollingRoute.ts b/apps/api/src/presentation/routes/PollingRoute.ts new file mode 100644 index 0000000..e161af5 --- /dev/null +++ b/apps/api/src/presentation/routes/PollingRoute.ts @@ -0,0 +1,30 @@ +import { WebSocketServer } from 'ws'; +import { BlockWebsocketRepository } from '../../infrastructure/repositories/blockWebsocket'; +import { BlockPollingRepository } from '../../infrastructure/repositories/blockPolling'; +import { PollingService } from '../../application/polling/pollingService'; +import 'dotenv/config'; + +export class PollingRoute { + private pollingService: PollingService; + private url: string; + + constructor() { + if (process.env['USE_WEBSOCKET_NODE_L2'] === 'true') { + this.url = process.env['WEBSOCKET_URL'] as string + const blockWebsocketRepository = new BlockWebsocketRepository(); + this.pollingService = new PollingService(blockWebsocketRepository); + } else { + this.url = process.env['RPC_URL'] as string + const blockPollingRepository = new BlockPollingRepository(); + this.pollingService = new PollingService(blockPollingRepository); + } + } + + public initialize(wss: WebSocketServer): void { + this.pollingService.execute(wss, this.url); + } + + public stop(): void { + this.pollingService.stop() + } +} diff --git a/apps/api/src/server.spec.ts b/apps/api/src/server.spec.ts new file mode 100644 index 0000000..9d29e74 --- /dev/null +++ b/apps/api/src/server.spec.ts @@ -0,0 +1,40 @@ +import { describe, it, expect, beforeAll, afterAll, vi, beforeEach, afterEach } from 'vitest'; +import { ExpressServer } from './presentation/ExpressServer'; + +vi.mock('./presentation/ExpressServer'); + +describe('server', () => { + beforeEach(() => { + vi.spyOn(process, 'exit').mockImplementation(() => { + return undefined as never; + }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + beforeAll(() => { + ExpressServer.prototype.start = vi.fn(); + ExpressServer.prototype.stop = vi.fn(); + }); + + afterAll(() => { + vi.restoreAllMocks(); + }); + + it('should start the server', async () => { + await import('./server'); + expect(ExpressServer.prototype.start).toHaveBeenCalled(); + }); + + it('should stop the server on SIGTERM', async () => { + process.emit('SIGTERM'); + expect(ExpressServer.prototype.stop).toHaveBeenCalled(); + }); + + it('should stop the server on SIGINT', async () => { + process.emit('SIGINT'); + expect(ExpressServer.prototype.stop).toHaveBeenCalled(); + }); +}); diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts new file mode 100644 index 0000000..2163b5d --- /dev/null +++ b/apps/api/src/server.ts @@ -0,0 +1,28 @@ +import 'dotenv/config'; +import { ExpressServer } from './presentation/ExpressServer'; +import { PollingRoute } from './presentation/routes/PollingRoute'; + +// Routes +const pollingRoute = new PollingRoute() + +const server = new ExpressServer(pollingRoute) + +const startServer = async (): Promise => { + await server.start() +} + +const stopServer = async (): Promise => { + await server.stop() +} + +process.on('SIGTERM', async () => { + await stopServer() + process.exit(0); +}) + +process.on('SIGINT', async () => { + await stopServer() + process.exit(0); +}) + +startServer() diff --git a/apps/api/tsconfig.build.json b/apps/api/tsconfig.build.json new file mode 100644 index 0000000..e702d94 --- /dev/null +++ b/apps/api/tsconfig.build.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "src/**/*.spec.ts" + ] + } \ No newline at end of file diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..fc3ad0f --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,104 @@ +{ + "include": [ "src/**/*" ], + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + "rootDir": "./src", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts new file mode 100644 index 0000000..1c0eacf --- /dev/null +++ b/apps/api/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + coverage: { + // instanbul excludes interfaces from coverage + provider: 'istanbul', + include: ['src/**/*.ts'], + }, + }, +}) \ No newline at end of file diff --git a/apps/api/webSocketTest.html b/apps/api/webSocketTest.html new file mode 100644 index 0000000..070df56 --- /dev/null +++ b/apps/api/webSocketTest.html @@ -0,0 +1,29 @@ + + + + + WebSocket Test + + +

Last block: waiting...

+

Last eth tx: waiting...

+

Last piano note: waiting...

+ + + + diff --git a/apps/web/.env b/apps/web/.env new file mode 100644 index 0000000..5ed69e0 --- /dev/null +++ b/apps/web/.env @@ -0,0 +1,12 @@ +VITE_API_WEBSERVICE_URL=ws://localhost:3000 +VITE_USE_API_MOCK=false +VITE_GITHUB_URL=https://github.com/BVM-priv/CryptoChords/ +VITE_CONTRIBUTORS_URL=https://github.com/BVM-priv/CryptoChords/blob/master/CONTRIBUTING.md +VITE_FEEDBACK_URL=https://github.com/BVM-priv/CryptoChords/issues/new +VITE_DISCORD_URL=https://discord.gg/RyhaPp7NvQ +VITE_X_URL=https://x.com/hemi_xyz +VITE_LOGO_URL=https://x.com/hemi_xyz +VITE_EXPLORER_ETH_URL=https://testnet.explorer.hemi.xyz/tx/\${hash} +VITE_EXPLORER_POP_URL=https://testnet.explorer.hemi.xyz/tx/\${hash} +VITE_EXPLORER_BTC_URL=https://testnet.explorer.hemi.xyz/tx/\${hash} +VITE_EXPLORER_BLOCK_URL=https://testnet.explorer.hemi.xyz/block/\${hash} \ No newline at end of file diff --git a/apps/web/.env.production b/apps/web/.env.production new file mode 100644 index 0000000..7ff2ec7 --- /dev/null +++ b/apps/web/.env.production @@ -0,0 +1,2 @@ +VITE_API_WEBSERVICE_URL=wss://\${host}/api +VITE_USE_API_MOCK=false \ No newline at end of file diff --git a/apps/web/.eslintrc.cjs b/apps/web/.eslintrc.cjs new file mode 100644 index 0000000..d6c9537 --- /dev/null +++ b/apps/web/.eslintrc.cjs @@ -0,0 +1,18 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/web/@types/tonejs-instruments.d.ts b/apps/web/@types/tonejs-instruments.d.ts new file mode 100644 index 0000000..2b6286f --- /dev/null +++ b/apps/web/@types/tonejs-instruments.d.ts @@ -0,0 +1,199 @@ +declare module 'tonejs-instrument-bass-electric-mp3' { + import { Sampler } from 'tone' + export default class InstrumentBassElectricMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-bassoon-mp3' { + import { Sampler } from 'tone' + export default class InstrumentBassoonMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-cello-mp3' { + import { Sampler } from 'tone' + export default class InstrumentCelloMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-clarinet-mp3' { + import { Sampler } from 'tone' + export default class InstrumentClarinetMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-contrabass-mp3' { + import { Sampler } from 'tone' + export default class InstrumentContrabassMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-flute-mp3' { + import { Sampler } from 'tone' + export default class InstrumentFluteMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-french-horn-mp3' { + import { Sampler } from 'tone' + export default class InstrumentFrenchHornMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-guitar-acoustic-mp3' { + import { Sampler } from 'tone' + export default class InstrumentGuitarAcousticMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-guitar-electric-mp3' { + import { Sampler } from 'tone' + export default class InstrumentGuitarElectricMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-guitar-nylon-mp3' { + import { Sampler } from 'tone' + export default class InstrumentGuitarNylonMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-harmonium-mp3' { + import { Sampler } from 'tone' + export default class InstrumentHarmoniumMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-harp-mp3' { + import { Sampler } from 'tone' + export default class InstrumentHarpMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-organ-mp3' { + import { Sampler } from 'tone' + export default class InstrumentOrganMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-piano-mp3' { + import { Sampler } from 'tone' + export default class InstrumentPianoMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-saxophone-mp3' { + import { Sampler } from 'tone' + export default class InstrumentSaxophoneMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-trombone-mp3' { + import { Sampler } from 'tone' + export default class InstrumentTromboneMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-trumpet-mp3' { + import { Sampler } from 'tone' + export default class InstrumentTrumpetMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-tuba-mp3' { + import { Sampler } from 'tone' + export default class InstrumentTubaMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-violin-mp3' { + import { Sampler } from 'tone' + export default class InstrumentViolinMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} + +declare module 'tonejs-instrument-xylophone-mp3' { + import { Sampler } from 'tone' + export default class InstrumentXylophoneMp3 extends Sampler { + constructor(options?: { + minify?: boolean + onload?: unknown + }) + } +} diff --git a/apps/web/@types/vite-env.d.ts b/apps/web/@types/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/apps/web/@types/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/web/README.md b/apps/web/README.md new file mode 100644 index 0000000..3f8cbe2 --- /dev/null +++ b/apps/web/README.md @@ -0,0 +1,112 @@ +# CryptoChords Web App +This is the web application of the CryptoChords project. It is a front-end application that allows users to visualize activities on the [Hemi network](https://hemi.xyz). + +## Architecture +It is built using DDD principles with the following layers: +- Application +- Domain +- Infrastructure +- Presentation + +### Application +This layer contains the use cases of the application. It is the entry point of the application and it is responsible for coordinating the domain layer and the infrastructure layer. + +### Domain +This layer contains the business logic of the application. It is the core of the application and it is responsible for the entities, value objects, and domain services. + +### Infrastructure +This layer contains the implementation of the application. It is responsible for the database and the external services. + +### Presentation +This layer contains the user interface of the application. + +#### Presenters +This sublayer contains the presenters of the application. It is responsible for the whole user interface logic. + +#### React +This sublayer contains the React implementation of the application. Its components are responsible for the style and the interface with the presenters, displaying its state and redirecting the user actions. + +## Getting Started + +### Prerequisites + +- Node.js + +### Installation + +1. Install the dependencies: + +```bash +npm install +``` + +2. Build the project: + +```bash +npm run build +``` + +### Running the Project + +1. On the root folder, run the following command: + +```bash +npm run dev +``` + +2. Open your browser and access the URL provided in the terminal. Normally, it is `http://localhost:5173/`. + +### Running the Tests + +To run the tests, you can use the following command: + +```bash +npm run test +``` + +### Lint the Code + +```bash +npm run lint +``` + +### Test the Code Coverage + +```bash +npm run test:cov +``` + +### Environment Variables + +The environment variables are defined in the `.env` file. The following variables are used: + +- `VITE_API_WEBSERVICE_URL`: The URL of the web service API. If the variable contains the value `${host}` it will be replaced by the host of the web app. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files). + +- `VITE_USE_API_MOCK`: A boolean that indicates if the API should be mocked. + +- `VITE_GITHUB_URL`: The URL of the GitHub repository. Used in the header and footer. + +- `VITE_CONTRIBUTORS_URL`: The URL of the contributors file. Used in the header. + +- `VITE_FEEDBACK_URL`: The URL of the feedback form. Used in the header. + +- `VITE_DISCORD_URL`: The URL of the Discord server. Used in the footer and 'Join Comunity' button. + +- `VITE_X_URL`: The URL of the X website. Used in the footer. + +- `VITE_LOGO_URL`: The URL of the logo. If it is not defined, the logo will point to the root of the web app and will not display the pointer cursor. + +- `VITE_EXPLORER_BLOCK_URL`: The URL of the block explorer. It has to contain the string `${hash}` that will be replaced by the block hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files). + +- `VITE_EXPLORER_ETH_URL`: The URL of the ETH transaction explorer. It has to contain the string `${hash}` that will be replaced by the transaction hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files). + +- `VITE_EXPLORER_BTC_URL`: The URL of the BTC transaction explorer. It has to contain the string `${hash}` that will be replaced by the transaction hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files). + +- `VITE_EXPLORER_POP_URL`: The URL of the PoP transaction explorer. It has to contain the string `${hash}` that will be replaced by the transaction hash. Make sure to escape the dollar sign `\${host}` otherwise it will expanded using [dotenv-expand](https://github.com/motdotla/dotenv-expand) as described on [Vite Docs](https://vitejs.dev/guide/env-and-mode#env-files). + +## Contribution +If you want to contribute to this project and make it better, your help is very welcome. +You can find more information about how to contribute in the [`CONTRIBUTING.md`](../../CONTRIBUTING.md) file. + +## License +This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details. \ No newline at end of file diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 0000000..50635c9 --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1,13 @@ + + + + + + + Crypto Chords + + +
+ + + diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf new file mode 100644 index 0000000..84b0e5f --- /dev/null +++ b/apps/web/nginx.conf @@ -0,0 +1,24 @@ +server { + + listen 80; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + location /api { + proxy_pass http://cryptochords-api-service:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + +} \ No newline at end of file diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json new file mode 100644 index 0000000..52b9837 --- /dev/null +++ b/apps/web/package-lock.json @@ -0,0 +1,5061 @@ +{ + "name": "bvm-crypto-chords-webapp", + "version": "1.0.10", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bvm-crypto-chords-webapp", + "version": "1.0.10", + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "uuid": "^9.0.1" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@types/uuid": "^9.0.7", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-istanbul": "^1.2.2", + "autoprefixer": "^10.4.17", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "postcss": "^8.4.33", + "tailwindcss": "^3.4.1", + "typescript": "^5.2.2", + "vite": "^5.0.8", + "vitest": "^1.2.1" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "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.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "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-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", + "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", + "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", + "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", + "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", + "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", + "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", + "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", + "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", + "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", + "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", + "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", + "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", + "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", + "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", + "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", + "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", + "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", + "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", + "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", + "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", + "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", + "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "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.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "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/eslintrc/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/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": 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.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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/@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/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/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "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.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", + "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/@rollup/rollup-android-arm-eabi": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.5.tgz", + "integrity": "sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.5.tgz", + "integrity": "sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.5.tgz", + "integrity": "sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.5.tgz", + "integrity": "sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.5.tgz", + "integrity": "sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.5.tgz", + "integrity": "sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.5.tgz", + "integrity": "sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.5.tgz", + "integrity": "sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.5.tgz", + "integrity": "sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.5.tgz", + "integrity": "sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.5.tgz", + "integrity": "sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.5.tgz", + "integrity": "sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.5.tgz", + "integrity": "sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "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/@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.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.48", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz", + "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.18", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.18.tgz", + "integrity": "sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", + "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.0.tgz", + "integrity": "sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.19.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", + "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.5", + "@babel/plugin-transform-react-jsx-self": "^7.23.3", + "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/@vitest/coverage-istanbul": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-1.2.2.tgz", + "integrity": "sha512-tJybwO8JT4H9ANz0T0/tJ1M5g3BkuHKYF1w5YO3z9sAiHBdGANrxN9c5lomJx1WSnLzCxQR5xxlJ4TLKbzrR3w==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-instrument": "^6.0.1", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.1.6", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "test-exclude": "^6.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "^1.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.2.1.tgz", + "integrity": "sha512-/bqGXcHfyKgFWYwIgFr1QYDaR9e64pRKxgBNWNXPefPFRhgm+K3+a/dS0cUGEreWngets3dlr8w8SBRw2fCfFQ==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.2.1", + "@vitest/utils": "1.2.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.2.1.tgz", + "integrity": "sha512-zc2dP5LQpzNzbpaBt7OeYAvmIsRS1KpZQw4G3WM/yqSV1cQKNKwLGmnm79GyZZjMhQGlRcSFMImLjZaUQvNVZQ==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.2.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.2.1.tgz", + "integrity": "sha512-Tmp/IcYEemKaqAYCS08sh0vORLJkMr0NRV76Gl8sHGxXT5151cITJCET20063wk0Yr/1koQ6dnmP6eEqezmd/Q==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.2.1.tgz", + "integrity": "sha512-vG3a/b7INKH7L49Lbp0IWrG6sw9j4waWAucwnksPB1r1FTJgV7nkBByd9ufzu6VWya/QTvQW4V9FShZbZIB2UQ==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-bsH6WVZYe/J2v3+81M5LDU8kW76xWObKIURpPrOXm2pjBniBu2MERI/XP60GpS4PHU3jyK50LUutOwrx4CyHUg==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "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, + "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-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": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "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": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-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/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.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/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/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/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "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.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001578", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001578.tgz", + "integrity": "sha512-J/jkFgsQ3NEl4w2lCoM9ZPxrD+FoBNJ7uJUpGVjIg/j0OwJosWM36EPDv+Yyi0V4twBk9pPmlFS+PLykgEvUmg==", + "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/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "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/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, + "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/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "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/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/electron-to-chromium": { + "version": "1.4.635", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.635.tgz", + "integrity": "sha512-iu/2D0zolKU3iDGXXxdOzNf72Jnokn+K1IN6Kk4iV6l1Tr2g/qy+mvmtfAiBwZe5S3aB5r92vp+zSZ69scYRrg==", + "dev": true + }, + "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/esbuild": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.11", + "@esbuild/android-arm": "0.19.11", + "@esbuild/android-arm64": "0.19.11", + "@esbuild/android-x64": "0.19.11", + "@esbuild/darwin-arm64": "0.19.11", + "@esbuild/darwin-x64": "0.19.11", + "@esbuild/freebsd-arm64": "0.19.11", + "@esbuild/freebsd-x64": "0.19.11", + "@esbuild/linux-arm": "0.19.11", + "@esbuild/linux-arm64": "0.19.11", + "@esbuild/linux-ia32": "0.19.11", + "@esbuild/linux-loong64": "0.19.11", + "@esbuild/linux-mips64el": "0.19.11", + "@esbuild/linux-ppc64": "0.19.11", + "@esbuild/linux-riscv64": "0.19.11", + "@esbuild/linux-s390x": "0.19.11", + "@esbuild/linux-x64": "0.19.11", + "@esbuild/netbsd-x64": "0.19.11", + "@esbuild/openbsd-x64": "0.19.11", + "@esbuild/sunos-x64": "0.19.11", + "@esbuild/win32-arm64": "0.19.11", + "@esbuild/win32-ia32": "0.19.11", + "@esbuild/win32-x64": "0.19.11" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "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.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "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", + "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.3", + "strip-ansi": "^6.0.1", + "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-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.5.tgz", + "integrity": "sha512-D53FYKJa+fDmZMtriODxvhwrO+IOqrxoEo21gMA0sjHdU6dPVH4OhyFip9ypl8HOF5RV5KdTo+rBQLvnY2cO8w==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": 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.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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/eslint/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/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/eslint/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/eslint/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/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/eslint/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/eslint/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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.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/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": 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, + "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, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.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/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "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 + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "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/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "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/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "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-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/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/glob/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/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/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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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==", + "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.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "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-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, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "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==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "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": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "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-report/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/istanbul-lib-report/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/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.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "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/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "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/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/magicast": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", + "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "source-map-js": "^1.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/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/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.5.0.tgz", + "integrity": "sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "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==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "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/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "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/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dev": true, + "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.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "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/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "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/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "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/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "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/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.5.tgz", + "integrity": "sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.5", + "@rollup/rollup-android-arm64": "4.9.5", + "@rollup/rollup-darwin-arm64": "4.9.5", + "@rollup/rollup-darwin-x64": "4.9.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.5", + "@rollup/rollup-linux-arm64-gnu": "4.9.5", + "@rollup/rollup-linux-arm64-musl": "4.9.5", + "@rollup/rollup-linux-riscv64-gnu": "4.9.5", + "@rollup/rollup-linux-x64-gnu": "4.9.5", + "@rollup/rollup-linux-x64-musl": "4.9.5", + "@rollup/rollup-win32-arm64-msvc": "4.9.5", + "@rollup/rollup-win32-ia32-msvc": "4.9.5", + "@rollup/rollup-win32-x64-msvc": "4.9.5", + "fsevents": "~2.3.2" + } + }, + "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/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "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/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/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-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, + "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/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/string-width-cjs/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/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/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/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-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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/test-exclude/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/test-exclude/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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinybench": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", + "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.1.tgz", + "integrity": "sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.0.tgz", + "integrity": "sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz", + "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "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.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "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/vite": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", + "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.2.1.tgz", + "integrity": "sha512-fNzHmQUSOY+y30naohBvSW7pPn/xn3Ib/uqm+5wAJQJiqQsU0NBR78XdRJb04l4bOFKjpTWld0XAfkKlrDbySg==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.1.tgz", + "integrity": "sha512-TRph8N8rnSDa5M2wKWJCMnztCZS9cDcgVTQ6tsTFTG/odHJ4l5yNVqvbeDJYJRZ6is3uxaEpFs8LL6QM+YFSdA==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.2.1", + "@vitest/runner": "1.2.1", + "@vitest/snapshot": "1.2.1", + "@vitest/spy": "1.2.1", + "@vitest/utils": "1.2.1", + "acorn-walk": "^8.3.2", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^1.3.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.1", + "vite": "^5.0.0", + "vite-node": "1.2.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "^1.0.0", + "@vitest/ui": "^1.0.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/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/wrap-ansi/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/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/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "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/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..190ce08 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,66 @@ +{ + "name": "crypto-chords-web", + "private": true, + "version": "1.0.11", + "description": "CryptoChords Web", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "test": "vitest run", + "test:cov": "vitest run --coverage", + "test:watch": "vitest watch" + }, + "dependencies": { + "@cryptochords/shared": "*", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-select": "^5.8.0", + "tone": "^14.7.77", + "tonejs-instrument-bass-electric-mp3": "^1.1.2", + "tonejs-instrument-bassoon-mp3": "^1.1.2", + "tonejs-instrument-cello-mp3": "^1.1.1", + "tonejs-instrument-clarinet-mp3": "^1.1.2", + "tonejs-instrument-contrabass-mp3": "^1.1.2", + "tonejs-instrument-flute-mp3": "^1.1.2", + "tonejs-instrument-french-horn-mp3": "^1.1.2", + "tonejs-instrument-guitar-acoustic-mp3": "^1.1.2", + "tonejs-instrument-guitar-electric-mp3": "^1.1.1", + "tonejs-instrument-guitar-nylon-mp3": "^1.1.1", + "tonejs-instrument-harmonium-mp3": "^1.1.1", + "tonejs-instrument-harp-mp3": "^1.1.1", + "tonejs-instrument-organ-mp3": "^1.1.1", + "tonejs-instrument-piano-mp3": "^1.1.2", + "tonejs-instrument-saxophone-mp3": "^1.1.2", + "tonejs-instrument-trombone-mp3": "^1.1.2", + "tonejs-instrument-trumpet-mp3": "^1.1.2", + "tonejs-instrument-tuba-mp3": "^1.1.2", + "tonejs-instrument-violin-mp3": "^1.1.1", + "tonejs-instrument-xylophone-mp3": "^1.1.2", + "uuid": "^9.0.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/BVM-priv/CryptoChords/webapp" + }, + "devDependencies": { + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@types/uuid": "^9.0.7", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-istanbul": "^1.2.2", + "autoprefixer": "^10.4.17", + "eslint": "^8.55.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.5", + "postcss": "^8.4.33", + "tailwindcss": "^3.4.1", + "typescript": "^5.2.2", + "vite": "^5.0.8", + "vitest": "^1.2.1" + } +} diff --git a/apps/web/postcss.config.js b/apps/web/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/apps/web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/apps/web/public/image/background/desktop.svg b/apps/web/public/image/background/desktop.svg new file mode 100644 index 0000000..3af3081 --- /dev/null +++ b/apps/web/public/image/background/desktop.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/background/mobile.svg b/apps/web/public/image/background/mobile.svg new file mode 100644 index 0000000..c63f578 --- /dev/null +++ b/apps/web/public/image/background/mobile.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/close-button.svg b/apps/web/public/image/close-button.svg new file mode 100644 index 0000000..f24eff4 --- /dev/null +++ b/apps/web/public/image/close-button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/image/crypto-chords-icon.svg b/apps/web/public/image/crypto-chords-icon.svg new file mode 100644 index 0000000..5e49203 --- /dev/null +++ b/apps/web/public/image/crypto-chords-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/web/public/image/crypto-chords.svg b/apps/web/public/image/crypto-chords.svg new file mode 100644 index 0000000..1b3be7f --- /dev/null +++ b/apps/web/public/image/crypto-chords.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/image/cube/blue.png b/apps/web/public/image/cube/blue.png new file mode 100644 index 0000000..1421605 Binary files /dev/null and b/apps/web/public/image/cube/blue.png differ diff --git a/apps/web/public/image/cube/green.png b/apps/web/public/image/cube/green.png new file mode 100644 index 0000000..3fce5e5 Binary files /dev/null and b/apps/web/public/image/cube/green.png differ diff --git a/apps/web/public/image/cube/orange.png b/apps/web/public/image/cube/orange.png new file mode 100644 index 0000000..bfc1f6d Binary files /dev/null and b/apps/web/public/image/cube/orange.png differ diff --git a/apps/web/public/image/cube/purple.png b/apps/web/public/image/cube/purple.png new file mode 100644 index 0000000..203e76e Binary files /dev/null and b/apps/web/public/image/cube/purple.png differ diff --git a/apps/web/public/image/keyboard/base.svg b/apps/web/public/image/keyboard/base.svg new file mode 100644 index 0000000..b870a3e --- /dev/null +++ b/apps/web/public/image/keyboard/base.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/image/keyboard/keys/black.svg b/apps/web/public/image/keyboard/keys/black.svg new file mode 100644 index 0000000..0e2ed69 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/black.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/blue/black.svg b/apps/web/public/image/keyboard/keys/blue/black.svg new file mode 100644 index 0000000..0e2ed69 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/blue/black.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/blue/white-left.svg b/apps/web/public/image/keyboard/keys/blue/white-left.svg new file mode 100644 index 0000000..e0b2348 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/blue/white-left.svg @@ -0,0 +1,574 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/blue/white-middle.svg b/apps/web/public/image/keyboard/keys/blue/white-middle.svg new file mode 100644 index 0000000..8d28638 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/blue/white-middle.svg @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/blue/white-right.svg b/apps/web/public/image/keyboard/keys/blue/white-right.svg new file mode 100644 index 0000000..afc4b01 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/blue/white-right.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/blue/white.svg b/apps/web/public/image/keyboard/keys/blue/white.svg new file mode 100644 index 0000000..be784c1 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/blue/white.svg @@ -0,0 +1,538 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/green/black.svg b/apps/web/public/image/keyboard/keys/green/black.svg new file mode 100644 index 0000000..0e2ed69 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/green/black.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/green/white-left.svg b/apps/web/public/image/keyboard/keys/green/white-left.svg new file mode 100644 index 0000000..570d31b --- /dev/null +++ b/apps/web/public/image/keyboard/keys/green/white-left.svg @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/green/white-middle.svg b/apps/web/public/image/keyboard/keys/green/white-middle.svg new file mode 100644 index 0000000..996cd14 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/green/white-middle.svg @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/green/white-right.svg b/apps/web/public/image/keyboard/keys/green/white-right.svg new file mode 100644 index 0000000..04ddd5c --- /dev/null +++ b/apps/web/public/image/keyboard/keys/green/white-right.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/green/white.svg b/apps/web/public/image/keyboard/keys/green/white.svg new file mode 100644 index 0000000..cb65a54 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/green/white.svg @@ -0,0 +1,538 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/orange/black.svg b/apps/web/public/image/keyboard/keys/orange/black.svg new file mode 100644 index 0000000..0e2ed69 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/orange/black.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/orange/white-left.svg b/apps/web/public/image/keyboard/keys/orange/white-left.svg new file mode 100644 index 0000000..b691d5c --- /dev/null +++ b/apps/web/public/image/keyboard/keys/orange/white-left.svg @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/orange/white-middle.svg b/apps/web/public/image/keyboard/keys/orange/white-middle.svg new file mode 100644 index 0000000..1962ee6 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/orange/white-middle.svg @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/orange/white-right.svg b/apps/web/public/image/keyboard/keys/orange/white-right.svg new file mode 100644 index 0000000..260045d --- /dev/null +++ b/apps/web/public/image/keyboard/keys/orange/white-right.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/orange/white.svg b/apps/web/public/image/keyboard/keys/orange/white.svg new file mode 100644 index 0000000..367e197 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/orange/white.svg @@ -0,0 +1,785 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/purple/black.svg b/apps/web/public/image/keyboard/keys/purple/black.svg new file mode 100644 index 0000000..0e2ed69 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/purple/black.svg @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/purple/white-left.svg b/apps/web/public/image/keyboard/keys/purple/white-left.svg new file mode 100644 index 0000000..476f235 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/purple/white-left.svg @@ -0,0 +1,790 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/purple/white-middle.svg b/apps/web/public/image/keyboard/keys/purple/white-middle.svg new file mode 100644 index 0000000..09c2ad5 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/purple/white-middle.svg @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/purple/white-right.svg b/apps/web/public/image/keyboard/keys/purple/white-right.svg new file mode 100644 index 0000000..b457014 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/purple/white-right.svg @@ -0,0 +1,519 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/purple/white.svg b/apps/web/public/image/keyboard/keys/purple/white.svg new file mode 100644 index 0000000..1d6b4dc --- /dev/null +++ b/apps/web/public/image/keyboard/keys/purple/white.svg @@ -0,0 +1,539 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/white-left.svg b/apps/web/public/image/keyboard/keys/white-left.svg new file mode 100644 index 0000000..0a08bdb --- /dev/null +++ b/apps/web/public/image/keyboard/keys/white-left.svg @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/white-middle.svg b/apps/web/public/image/keyboard/keys/white-middle.svg new file mode 100644 index 0000000..0a1bccf --- /dev/null +++ b/apps/web/public/image/keyboard/keys/white-middle.svg @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/white-right.svg b/apps/web/public/image/keyboard/keys/white-right.svg new file mode 100644 index 0000000..22184cf --- /dev/null +++ b/apps/web/public/image/keyboard/keys/white-right.svg @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/keyboard/keys/white.svg b/apps/web/public/image/keyboard/keys/white.svg new file mode 100644 index 0000000..46fa353 --- /dev/null +++ b/apps/web/public/image/keyboard/keys/white.svg @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/image/nav-button.svg b/apps/web/public/image/nav-button.svg new file mode 100644 index 0000000..e205b8c --- /dev/null +++ b/apps/web/public/image/nav-button.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/image/social/discord.svg b/apps/web/public/image/social/discord.svg new file mode 100644 index 0000000..83b5438 --- /dev/null +++ b/apps/web/public/image/social/discord.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/public/image/social/linkedin.svg b/apps/web/public/image/social/linkedin.svg new file mode 100644 index 0000000..7d5620c --- /dev/null +++ b/apps/web/public/image/social/linkedin.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/web/public/image/social/x.svg b/apps/web/public/image/social/x.svg new file mode 100644 index 0000000..d587013 --- /dev/null +++ b/apps/web/public/image/social/x.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/apps/web/src/application/ObservableService.ts b/apps/web/src/application/ObservableService.ts new file mode 100644 index 0000000..1c66eaa --- /dev/null +++ b/apps/web/src/application/ObservableService.ts @@ -0,0 +1,22 @@ +import { Observable, EventBusInstance, EventSubscription, Event } from '@cryptochords/shared' +import { Service } from './Service' +import { ServiceResponseEvent } from './events/ServiceResponseEvent' + +export abstract class ObservableService +extends Service +implements Observable>{ + private eventBus = new EventBusInstance() + + public async execute(request: Request): Promise { + const response = this.process(request) + response.then((responseValue: Response) => { + const event = new ServiceResponseEvent(request, responseValue) + this.eventBus.publish(event) + }) + return response + } + + public async listen(listener: EventSubscription>) { + this.eventBus.subscribe(ServiceResponseEvent.eventKey, listener as EventSubscription) + } +} \ No newline at end of file diff --git a/apps/web/src/application/Service.ts b/apps/web/src/application/Service.ts new file mode 100644 index 0000000..48e9b38 --- /dev/null +++ b/apps/web/src/application/Service.ts @@ -0,0 +1,7 @@ +export abstract class Service{ + protected abstract process(request: Request): Promise + + public async execute(request: Request): Promise { + return this.process(request) + } +} \ No newline at end of file diff --git a/apps/web/src/application/events/ServiceResponseEvent.ts b/apps/web/src/application/events/ServiceResponseEvent.ts new file mode 100644 index 0000000..6b7b5ba --- /dev/null +++ b/apps/web/src/application/events/ServiceResponseEvent.ts @@ -0,0 +1,8 @@ +import { Event } from '@cryptochords/shared' + +export class ServiceResponseEvent extends Event { + static eventKey = Symbol('ServiceResponseEvent'); + constructor(public readonly request: Request, public readonly response: Response) { + super(ServiceResponseEvent.eventKey) + } +} diff --git a/apps/web/src/application/services/CreateCube/CreateCubeDtos.ts b/apps/web/src/application/services/CreateCube/CreateCubeDtos.ts new file mode 100644 index 0000000..0e17781 --- /dev/null +++ b/apps/web/src/application/services/CreateCube/CreateCubeDtos.ts @@ -0,0 +1,12 @@ +export interface CreateCubeResponseDto { + id: string + x: number + y: number + color: string + mirrored: boolean +} + +export interface CreateCubeRequestDto { + x: number, + color: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/CreateCube/CreateCubeService.spec.ts b/apps/web/src/application/services/CreateCube/CreateCubeService.spec.ts new file mode 100644 index 0000000..8c2647c --- /dev/null +++ b/apps/web/src/application/services/CreateCube/CreateCubeService.spec.ts @@ -0,0 +1,33 @@ +import { describe, expect, it, vi } from 'vitest' +import { CubeRepository } from '../../../domain/repositories/CubeRepository' +import { CreateCubeService } from './CreateCubeService' + +const cubeRepositoryMock: CubeRepository = { + create: vi.fn().mockResolvedValue({ + uuid: { value: 'some-id' }, + x: { value: 0.5 }, + y: { value: 0 }, + color: { value: 'blue' }, + mirrored: false + }), + delete: vi.fn(), + update: vi.fn(), + list: vi.fn(), +} + +const create: CreateCubeService = new CreateCubeService(cubeRepositoryMock) + +describe('src/application/CreateCube/CreateCubeService', () => { + describe('execute', () => { + it('should return ', async () => { + const response = await create.execute({ color: 'blue', x: 0.5 }) + expect(response).toEqual({ + id: 'some-id', + x: 0.5, + y: 0, + color: 'blue', + mirrored: false + }) + }) + }) +}) diff --git a/apps/web/src/application/services/CreateCube/CreateCubeService.ts b/apps/web/src/application/services/CreateCube/CreateCubeService.ts new file mode 100644 index 0000000..eaf5af3 --- /dev/null +++ b/apps/web/src/application/services/CreateCube/CreateCubeService.ts @@ -0,0 +1,28 @@ +import { Cube } from '../../../domain/entities/Cube' +import { TransactionColorEnum } from '../../../domain/enum/TransactionColorEnum' +import { CubeRepository } from '../../../domain/repositories/CubeRepository' +import { TransactionColor } from '../../../domain/valueObjects/TransactionColor' +import { UnitInterval } from '../../../domain/valueObjects/UnitInterval' +import { ObservableService } from '../../ObservableService' +import { CreateCubeRequestDto, CreateCubeResponseDto } from './CreateCubeDtos' + +export class CreateCubeService extends ObservableService{ + private readonly cubeRepository: CubeRepository + + constructor(cubeRepository: CubeRepository) { + super() + this.cubeRepository = cubeRepository + } + + protected async process(request: CreateCubeRequestDto): Promise { + const cube = Cube.create(TransactionColor.create(request.color as TransactionColorEnum), UnitInterval.create(request.x)) + const createdCube = await this.cubeRepository.create(cube) + return { + id: createdCube.uuid.value, + x: createdCube.x.value, + y: createdCube.y.value, + color: createdCube.color.value, + mirrored: createdCube.mirrored + } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/CreateKeyboard/CreateKeyboardDtos.ts b/apps/web/src/application/services/CreateKeyboard/CreateKeyboardDtos.ts new file mode 100644 index 0000000..a72c53f --- /dev/null +++ b/apps/web/src/application/services/CreateKeyboard/CreateKeyboardDtos.ts @@ -0,0 +1,17 @@ +export interface CreateKeyboardRequestDto { + numberOfKeys: number + initialPitchClass: string + initialOctave: number +} + +export interface CreateKeyboardResponseDto { + keys: { + pitch: { + class: string + octave: number + } + keyShape: string, + x: number + color?: string + }[] +} diff --git a/apps/web/src/application/services/CreateKeyboard/CreateKeyboardService.spec.ts b/apps/web/src/application/services/CreateKeyboard/CreateKeyboardService.spec.ts new file mode 100644 index 0000000..4b51863 --- /dev/null +++ b/apps/web/src/application/services/CreateKeyboard/CreateKeyboardService.spec.ts @@ -0,0 +1,38 @@ +import { beforeAll, describe, expect, it, vi } from 'vitest' +import { CreateKeyboardService } from './CreateKeyboardService' +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' + +describe('src/application/CreateKeyboard/CreateKeyboardService', () => { + let createKeyboardService: CreateKeyboardService + let keyboardRepository: KeyboardRepository + + beforeAll(() => { + keyboardRepository= { + setKeyboard: vi.fn(), + getKeyboard: vi.fn() + } + createKeyboardService = new CreateKeyboardService(keyboardRepository) + }) + + it('should be defined', () => { + expect(createKeyboardService).toBeDefined() + }) + + it('should return a keyboard with 88 keys', async () => { + const response = await createKeyboardService.execute({ + initialOctave: 0, + numberOfKeys: 88, + initialPitchClass: 'A' + }) + expect(response.keys.length).toBe(88) + }) + + it('should call setKeyboard', async () => { + await createKeyboardService.execute({ + initialOctave: 0, + numberOfKeys: 88, + initialPitchClass: 'A' + }) + expect(keyboardRepository.setKeyboard).toHaveBeenCalled() + }) +}) \ No newline at end of file diff --git a/apps/web/src/application/services/CreateKeyboard/CreateKeyboardService.ts b/apps/web/src/application/services/CreateKeyboard/CreateKeyboardService.ts new file mode 100644 index 0000000..00050c7 --- /dev/null +++ b/apps/web/src/application/services/CreateKeyboard/CreateKeyboardService.ts @@ -0,0 +1,36 @@ +import { PitchClassEnum } from '../../../domain/enum/PitchClassEnum' +import { KeyboardFactory } from '../../../domain/factories/KeyboardFactory' +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' +import { ObservableService } from '../../ObservableService' +import { CreateKeyboardRequestDto, CreateKeyboardResponseDto } from './CreateKeyboardDtos' + +export class CreateKeyboardService extends ObservableService{ + private keyboardRepository: KeyboardRepository + + constructor(keyboardRepository: KeyboardRepository) { + super() + this.keyboardRepository = keyboardRepository + } + + protected async process(request: CreateKeyboardRequestDto): Promise { + const keyboard = KeyboardFactory.create({ + initialOctave: request.initialOctave, + numberOfKeys: request.numberOfKeys, + initialPitchClass: request.initialPitchClass as PitchClassEnum + }) + + this.keyboardRepository.setKeyboard(keyboard) + + return { + keys: keyboard.keys.map(key => ({ + pitch: { + class: key.pitch.pitchClass.value, + octave: key.pitch.octave + }, + keyShape: key.keyShape.value, + x: key.x.value, + color: key.color + })) + } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/CreateTransaction/CreateTransactionDtos.ts b/apps/web/src/application/services/CreateTransaction/CreateTransactionDtos.ts new file mode 100644 index 0000000..38e79e7 --- /dev/null +++ b/apps/web/src/application/services/CreateTransaction/CreateTransactionDtos.ts @@ -0,0 +1,5 @@ +export interface CreateTransactionRequest { + txType: string + address: string + timestamp: number +} \ No newline at end of file diff --git a/apps/web/src/application/services/CreateTransaction/CreateTransactionService.ts b/apps/web/src/application/services/CreateTransaction/CreateTransactionService.ts new file mode 100644 index 0000000..10d49c3 --- /dev/null +++ b/apps/web/src/application/services/CreateTransaction/CreateTransactionService.ts @@ -0,0 +1,87 @@ +import { Address, Timestamp, TxType, TxTypesEnum } from '@cryptochords/shared' +import { Transaction } from '../../../domain/entities/Transaction' +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' +import { TransactionRepository } from '../../../domain/repositories/TransactionRepository' +import { Key } from '../../../domain/valueObjects/Key' +import { ObservableService } from '../../ObservableService' +import { CreateCubeService } from '../CreateCube/CreateCubeService' +import { PressKeyService } from '../PressKey/PressKeyService' +import { ReleaseKeyService } from '../ReleaseKey/ReleaseKeyService' +import { CreateTransactionRequest } from './CreateTransactionDtos' +import { InstrumentEnum } from '../../../domain/enum/InstrumentEnum' + +export class CreateTransactionService extends ObservableService { + private readonly keyboardRepository: KeyboardRepository + private readonly transactionRepository: TransactionRepository + private readonly createCubeService: CreateCubeService + private readonly pressKeyService: PressKeyService + private readonly releaseKeyService: ReleaseKeyService + + constructor( + keyboardRepository: KeyboardRepository, + transactionRepository: TransactionRepository, + createCubeService: CreateCubeService, + pressKeyService: PressKeyService, + releaseKeyService: ReleaseKeyService + ) { + super() + this.keyboardRepository = keyboardRepository + this.transactionRepository = transactionRepository + this.createCubeService = createCubeService + this.pressKeyService = pressKeyService + this.releaseKeyService = releaseKeyService + } + + protected async process(request: CreateTransactionRequest): Promise { + const transaction = await this.createTransaction(request) + const [ key, instrument ] = await this.pressKey(transaction) + if (!key) + return + await this.createCube(key) + + setTimeout(() => { + this.releaseKey(key, instrument) + }, 100 + Math.random() * 3_000) + } + + async createTransaction(request: CreateTransactionRequest): Promise { + const transaction = Transaction.create({ + txType: TxType.create(request.txType as TxTypesEnum), + address: Address.create(request.address), + timestamp: Timestamp.create(request.timestamp) + }) + await this.transactionRepository.create(transaction) + + return transaction + } + + async createCube(key: Key): Promise { + this.createCubeService.execute({ + x: key.x.value, + color: key.color + }) + } + + async pressKey(transaction: Transaction): Promise<[Key | undefined, (InstrumentEnum | undefined) ] > { + const keyboard = this.keyboardRepository.getKeyboard() + if (!keyboard) + return [undefined, undefined] + + const randomKey = keyboard.getRandomWhiteKeyByTxType(transaction.txType.value) + + const { instrument } = await this.pressKeyService.execute({ + pitchClass: randomKey.pitch.pitchClass.value, + octave: randomKey.pitch.octave + }) + + return [randomKey, instrument as InstrumentEnum] + } + + async releaseKey(key: Key, instrument?: InstrumentEnum): Promise { + this.releaseKeyService.execute({ + pitchClass: key.pitch.pitchClass.value, + octave: key.pitch.octave, + instrument + }) + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/GetCubes/GetCubesDtos.ts b/apps/web/src/application/services/GetCubes/GetCubesDtos.ts new file mode 100644 index 0000000..5af6183 --- /dev/null +++ b/apps/web/src/application/services/GetCubes/GetCubesDtos.ts @@ -0,0 +1,12 @@ +interface CubeDto { + id: string + x: number + y: number + color: string + mirrored: boolean + age: number +} + +export interface GetCubesResponseDto { + cubes: CubeDto[] +} \ No newline at end of file diff --git a/apps/web/src/application/services/GetCubes/GetCubesService.spec.ts b/apps/web/src/application/services/GetCubes/GetCubesService.spec.ts new file mode 100644 index 0000000..8c2b20f --- /dev/null +++ b/apps/web/src/application/services/GetCubes/GetCubesService.spec.ts @@ -0,0 +1,61 @@ +import { describe, expect, expectTypeOf, it, vi } from 'vitest' +import { CubeRepository } from '../../../domain/repositories/CubeRepository' +import { GetCubesService } from './GetCubesService' +import { GetCubesResponseDto } from './GetCubesDtos' + +const cubeRepositoryMock: CubeRepository = { + create: vi.fn(), + delete: vi.fn(), + update: vi.fn(), + list: vi.fn().mockResolvedValue([{ + uuid: { value: 'some-id' }, + x: { value: 0.5 }, + y: { value: 0 }, + color: { value: 'blue' }, + mirrored: false + }, { + uuid: { value: 'another-id' }, + x: { value: 0.1 }, + y: { value: 0 }, + color: { value: 'orange' }, + mirrored: false + }]), +} + +const getCubes = new GetCubesService(cubeRepositoryMock) + +describe('src/application/CreateCube/GetCubesService', () => { + describe('execute', async () => { + const response: GetCubesResponseDto = await getCubes.execute() + + it('should call list once', () => { + expect(cubeRepositoryMock.list).toHaveBeenCalledOnce() + }) + + it('should return a GetCubesResponseDto', () => { + expectTypeOf(response).toMatchTypeOf() + }) + + it('should return the correct number of items', () => { + expect(response.cubes.length).toEqual(2) + }) + + it('should return the correct values', async () => { + expect(response).toEqual({ + cubes: [{ + id: 'some-id', + x: 0.5, + y: 0, + color: 'blue', + mirrored: false + }, { + id: 'another-id', + x: 0.1, + y: 0, + color: 'orange', + mirrored: false + }] + }) + }) + }) +}) diff --git a/apps/web/src/application/services/GetCubes/GetCubesService.ts b/apps/web/src/application/services/GetCubes/GetCubesService.ts new file mode 100644 index 0000000..ae8f362 --- /dev/null +++ b/apps/web/src/application/services/GetCubes/GetCubesService.ts @@ -0,0 +1,27 @@ +import { CubeRepository } from '../../../domain/repositories/CubeRepository' +import { ObservableService } from '../../ObservableService' +import { GetCubesResponseDto } from './GetCubesDtos' + +export class GetCubesService extends ObservableService{ + private readonly cubeRepository: CubeRepository + + constructor(cubeRepository: CubeRepository) { + super() + this.cubeRepository = cubeRepository + } + + protected async process(): Promise { + const cubes = await this.cubeRepository.list() + + return { + cubes: cubes.map(cube => ({ + id: cube.uuid.value, + x: cube.x.value, + y: cube.y.value, + color: cube.color.value, + mirrored: cube.mirrored, + age: cube.age + })) + } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/GetKeyboard/GetKeyboardDtos.ts b/apps/web/src/application/services/GetKeyboard/GetKeyboardDtos.ts new file mode 100644 index 0000000..9fd2b3c --- /dev/null +++ b/apps/web/src/application/services/GetKeyboard/GetKeyboardDtos.ts @@ -0,0 +1,12 @@ +export interface GetKeyboardResponseDto { + keys: { + pitch: { + class: string + octave: number + } + keyShape: string, + x: number + color: string + pressed: boolean + }[] +} \ No newline at end of file diff --git a/apps/web/src/application/services/GetKeyboard/GetKeyboardService.ts b/apps/web/src/application/services/GetKeyboard/GetKeyboardService.ts new file mode 100644 index 0000000..2a2b57f --- /dev/null +++ b/apps/web/src/application/services/GetKeyboard/GetKeyboardService.ts @@ -0,0 +1,35 @@ +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' +import { ObservableService } from '../../ObservableService' +import { GetKeyboardResponseDto } from './GetKeyboardDtos' + +export class GetKeyboardService extends ObservableService{ + private readonly keyboardRepository: KeyboardRepository + + constructor(keyboardRepository: KeyboardRepository) { + super() + this.keyboardRepository = keyboardRepository + } + + protected async process(): Promise { + const keyboard = this.keyboardRepository.getKeyboard() + + if (!keyboard) { + return { + keys: [] + } + } + + return { + keys: keyboard.keys.map(key => ({ + pitch: { + class: key.pitch.pitchClass.value, + octave: key.pitch.octave + }, + keyShape: key.keyShape.value, + x: key.x.value, + color: key.color, + pressed: key.pressed + })) + } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/GetOptions/GetOptionsDtos.ts b/apps/web/src/application/services/GetOptions/GetOptionsDtos.ts new file mode 100644 index 0000000..1009d77 --- /dev/null +++ b/apps/web/src/application/services/GetOptions/GetOptionsDtos.ts @@ -0,0 +1,4 @@ +export interface GetOptionsResponseDto { + muted: boolean + instrument: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/GetOptions/GetOptionsService.ts b/apps/web/src/application/services/GetOptions/GetOptionsService.ts new file mode 100644 index 0000000..8cc3c5c --- /dev/null +++ b/apps/web/src/application/services/GetOptions/GetOptionsService.ts @@ -0,0 +1,21 @@ +import { OptionsRepository } from '../../../domain/repositories/OptionsRepository' +import { ObservableService } from '../../ObservableService' +import { GetOptionsResponseDto } from './GetOptionsDtos' + +export class GetOptionsService extends ObservableService{ + private readonly optionsRepository: OptionsRepository + + constructor(optionsRepository: OptionsRepository) { + super() + this.optionsRepository = optionsRepository + } + + protected async process(): Promise { + const options = await this.optionsRepository.getOptions() + + return { + instrument: options.instrument.name, + muted: options.muted + } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/ListTransactions/ListTransactionsDtos.ts b/apps/web/src/application/services/ListTransactions/ListTransactionsDtos.ts new file mode 100644 index 0000000..31fd551 --- /dev/null +++ b/apps/web/src/application/services/ListTransactions/ListTransactionsDtos.ts @@ -0,0 +1,9 @@ +interface TransactionDto { + txType: string + address: string + timestamp: number +} + +export interface ListTransactionsResponseDto { + transactions: TransactionDto[] +} \ No newline at end of file diff --git a/apps/web/src/application/services/ListTransactions/ListTransactionsService.ts b/apps/web/src/application/services/ListTransactions/ListTransactionsService.ts new file mode 100644 index 0000000..c3c9af6 --- /dev/null +++ b/apps/web/src/application/services/ListTransactions/ListTransactionsService.ts @@ -0,0 +1,24 @@ +import { TransactionRepository } from '../../../domain/repositories/TransactionRepository' +import { ObservableService } from '../../ObservableService' +import { ListTransactionsResponseDto } from './ListTransactionsDtos' + +export class ListTransactionsService extends ObservableService{ + private readonly transactionRepository: TransactionRepository + + constructor(transactionRepository: TransactionRepository) { + super() + this.transactionRepository = transactionRepository + } + + protected async process(): Promise { + const transactions = await this.transactionRepository.list() + + return { + transactions: transactions.map(transaction => ({ + txType: transaction.txType.value as string, + address: transaction.address.value, + timestamp: transaction.timestamp.value + })) + } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/LoadInstrument/LoadInstrumentDtos.ts b/apps/web/src/application/services/LoadInstrument/LoadInstrumentDtos.ts new file mode 100644 index 0000000..6dae4b4 --- /dev/null +++ b/apps/web/src/application/services/LoadInstrument/LoadInstrumentDtos.ts @@ -0,0 +1,5 @@ +export interface LoadInstrumentRequest { + instrument: string +} + +export type LoadInstrumentResponse = void \ No newline at end of file diff --git a/apps/web/src/application/services/LoadInstrument/LoadInstrumentService.ts b/apps/web/src/application/services/LoadInstrument/LoadInstrumentService.ts new file mode 100644 index 0000000..7c774fb --- /dev/null +++ b/apps/web/src/application/services/LoadInstrument/LoadInstrumentService.ts @@ -0,0 +1,18 @@ +import { SoundService } from '../../../domain/services/SoundService' +import { ObservableService } from '../../ObservableService' +import { LoadInstrumentRequest, LoadInstrumentResponse } from './LoadInstrumentDtos' + +export class LoadInstrumentService extends ObservableService { + + private soundService: SoundService + + constructor(soundService: SoundService) { + super() + this.soundService = soundService + } + + protected async process({ instrument }: LoadInstrumentRequest): Promise { + await this.soundService.loadInstrument(instrument) + return + } +} diff --git a/apps/web/src/application/services/PlaySound/PlaySoundDtos.ts b/apps/web/src/application/services/PlaySound/PlaySoundDtos.ts new file mode 100644 index 0000000..ba48bf0 --- /dev/null +++ b/apps/web/src/application/services/PlaySound/PlaySoundDtos.ts @@ -0,0 +1,8 @@ +export interface PlaySoundRequest { + pitchClass: string + octave: number +} + +export interface PlaySoundResponse { + instrument?: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/PlaySound/PlaySoundService.ts b/apps/web/src/application/services/PlaySound/PlaySoundService.ts new file mode 100644 index 0000000..05e1ff2 --- /dev/null +++ b/apps/web/src/application/services/PlaySound/PlaySoundService.ts @@ -0,0 +1,31 @@ +import { PitchClassEnum } from '../../../domain/enum/PitchClassEnum' +import { OptionsRepository } from '../../../domain/repositories/OptionsRepository' +import { SoundService } from '../../../domain/services/SoundService' +import { PitchClass } from '../../../domain/valueObjects/PitchClass' +import { ObservableService } from '../../ObservableService' +import { PlaySoundRequest, PlaySoundResponse } from './PlaySoundDtos' + +export class PlaySoundService extends ObservableService{ + + private soundService: SoundService + private optionsRepository: OptionsRepository + + constructor(soundService: SoundService, optionsRepository: OptionsRepository) { + super() + this.soundService = soundService + this.optionsRepository = optionsRepository + } + + protected async process(request: PlaySoundRequest): Promise { + const options = await this.optionsRepository.getOptions() + + if(options.muted) { + return {} + } + + const pitchClass = PitchClass.create(request.pitchClass as PitchClassEnum) + await this.soundService.playSound(pitchClass.value, request.octave, options.instrument.name) + + return { instrument: options.instrument.name } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/PressKey/PressKeyDtos.ts b/apps/web/src/application/services/PressKey/PressKeyDtos.ts new file mode 100644 index 0000000..85bceab --- /dev/null +++ b/apps/web/src/application/services/PressKey/PressKeyDtos.ts @@ -0,0 +1,8 @@ +export interface PressKeyRequest { + pitchClass: string + octave: number +} + +export interface PressKeyResponse { + instrument?: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/PressKey/PressKeyService.ts b/apps/web/src/application/services/PressKey/PressKeyService.ts new file mode 100644 index 0000000..c8fa215 --- /dev/null +++ b/apps/web/src/application/services/PressKey/PressKeyService.ts @@ -0,0 +1,35 @@ +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' +import { ObservableService } from '../../ObservableService' +import { PlaySoundService } from '../PlaySound/PlaySoundService' +import { PressKeyRequest, PressKeyResponse } from './PressKeyDtos' + +export class PressKeyService extends ObservableService{ + + private keyboardRepository: KeyboardRepository + private playSound: PlaySoundService + + constructor(keyboardRepository: KeyboardRepository, playSound: PlaySoundService) { + super() + this.keyboardRepository = keyboardRepository + this.playSound = playSound + } + + protected async process(request: PressKeyRequest): Promise { + const keyboard = this.keyboardRepository.getKeyboard() + if (!keyboard) + return {} + + const key = keyboard.findKey(request.pitchClass, request.octave) + if (!key) + return {} + + key.press() + + const { instrument } = await this.playSound.execute({ + pitchClass: request.pitchClass, + octave: request.octave + }) + + return { instrument } + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/RecalculateCubePositions/RecalculateCubePositionsDtos.ts b/apps/web/src/application/services/RecalculateCubePositions/RecalculateCubePositionsDtos.ts new file mode 100644 index 0000000..45c7f97 --- /dev/null +++ b/apps/web/src/application/services/RecalculateCubePositions/RecalculateCubePositionsDtos.ts @@ -0,0 +1,8 @@ +export interface RecalculateCubePositionsRequestDto { + maxAge: number +} + +export interface RecalculateCubePositionsResponseDto { + deletedCubes: number + updatedCubes: number +} diff --git a/apps/web/src/application/services/RecalculateCubePositions/RecalculateCubePositionsService.ts b/apps/web/src/application/services/RecalculateCubePositions/RecalculateCubePositionsService.ts new file mode 100644 index 0000000..0607a52 --- /dev/null +++ b/apps/web/src/application/services/RecalculateCubePositions/RecalculateCubePositionsService.ts @@ -0,0 +1,34 @@ +import { CubeRepository } from '../../../domain/repositories/CubeRepository' +import { ObservableService } from '../../ObservableService' +import { RecalculateCubePositionsRequestDto, RecalculateCubePositionsResponseDto } from './RecalculateCubePositionsDtos' + +export class RecalculateCubePositionsService extends ObservableService{ + private readonly cubeRepository: CubeRepository + + constructor(cubeRepository: CubeRepository) { + super() + this.cubeRepository = cubeRepository + } + + protected async process(request: RecalculateCubePositionsRequestDto): Promise { + const cubes = await this.cubeRepository.list() + const response: RecalculateCubePositionsResponseDto = { + deletedCubes: 0, + updatedCubes: 0 + } + + for await(const cube of cubes) { + cube.recalculateYByAge(request.maxAge) + if(cube.isOnTop) { + await this.cubeRepository.delete(cube.uuid) + response.deletedCubes++ + } else { + await this.cubeRepository.update(cube) + response.updatedCubes++ + } + } + + return response + } + +} diff --git a/apps/web/src/application/services/ReleaseKey/ReleaseKeyDtos.ts b/apps/web/src/application/services/ReleaseKey/ReleaseKeyDtos.ts new file mode 100644 index 0000000..4754f26 --- /dev/null +++ b/apps/web/src/application/services/ReleaseKey/ReleaseKeyDtos.ts @@ -0,0 +1,5 @@ +export interface ReleaseKeyRequest { + pitchClass: string + octave: number + instrument?: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/ReleaseKey/ReleaseKeyService.ts b/apps/web/src/application/services/ReleaseKey/ReleaseKeyService.ts new file mode 100644 index 0000000..8fdc769 --- /dev/null +++ b/apps/web/src/application/services/ReleaseKey/ReleaseKeyService.ts @@ -0,0 +1,39 @@ +import { InstrumentEnum } from '../../../domain/enum/InstrumentEnum' +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' +import { ObservableService } from '../../ObservableService' +import { StopSoundService } from '../StopSound/StopSoundService' +import { ReleaseKeyRequest as ReleaseKeyRequest } from './ReleaseKeyDtos' + +export class ReleaseKeyService extends ObservableService{ + + private keyboardRepository: KeyboardRepository + private stopSound: StopSoundService + + constructor(keyboardRepository: KeyboardRepository, stopSound: StopSoundService) { + super() + this.keyboardRepository = keyboardRepository + this.stopSound = stopSound + } + + protected async process(request: ReleaseKeyRequest): Promise { + const keyboard = this.keyboardRepository.getKeyboard() + if (!keyboard) + return + + const key = keyboard.findKey(request.pitchClass, request.octave) + if (!key) + return + + key.release() + + if(!request.instrument) { + return + } + + this.stopSound.execute({ + pitchClass: request.pitchClass, + octave: request.octave, + instrument: request.instrument as InstrumentEnum + }) + } +} \ No newline at end of file diff --git a/apps/web/src/application/services/SetInstrument/SetInstrumentDtos.ts b/apps/web/src/application/services/SetInstrument/SetInstrumentDtos.ts new file mode 100644 index 0000000..246324f --- /dev/null +++ b/apps/web/src/application/services/SetInstrument/SetInstrumentDtos.ts @@ -0,0 +1,3 @@ +export interface SetInstrumentRequest { + instrument: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/SetInstrument/SetInstrumentService.ts b/apps/web/src/application/services/SetInstrument/SetInstrumentService.ts new file mode 100644 index 0000000..41723b5 --- /dev/null +++ b/apps/web/src/application/services/SetInstrument/SetInstrumentService.ts @@ -0,0 +1,26 @@ +import { InstrumentEnum } from '../../../domain/enum/InstrumentEnum' +import { OptionsRepository } from '../../../domain/repositories/OptionsRepository' +import { Instrument } from '../../../domain/valueObjects/Instrument' +import { Options } from '../../../domain/valueObjects/Options' +import { ObservableService } from '../../ObservableService' +import { SetInstrumentRequest } from './SetInstrumentDtos' + +export class SetInstrumentService extends ObservableService{ + + private optionsRepository: OptionsRepository + + constructor(optionsRepository: OptionsRepository) { + super() + this.optionsRepository = optionsRepository + } + + protected async process(request: SetInstrumentRequest): Promise { + const options = await this.optionsRepository.getOptions() + this.optionsRepository.setOptions( + Options.create({ + muted: options.muted, + instrument: Instrument.create({ name: request.instrument as InstrumentEnum}) + }) + ) + } +} diff --git a/apps/web/src/application/services/SetMuted/SetMutedDtos.ts b/apps/web/src/application/services/SetMuted/SetMutedDtos.ts new file mode 100644 index 0000000..86cb31e --- /dev/null +++ b/apps/web/src/application/services/SetMuted/SetMutedDtos.ts @@ -0,0 +1,3 @@ +export interface SetMutedRequest { + muted: boolean +} \ No newline at end of file diff --git a/apps/web/src/application/services/SetMuted/SetMutedService.ts b/apps/web/src/application/services/SetMuted/SetMutedService.ts new file mode 100644 index 0000000..bb6ee06 --- /dev/null +++ b/apps/web/src/application/services/SetMuted/SetMutedService.ts @@ -0,0 +1,24 @@ +import { OptionsRepository } from '../../../domain/repositories/OptionsRepository' +import { Options } from '../../../domain/valueObjects/Options' +import { ObservableService } from '../../ObservableService' +import { SetMutedRequest } from './SetMutedDtos' + +export class SetMutedService extends ObservableService{ + + private optionsRepository: OptionsRepository + + constructor(optionsRepository: OptionsRepository) { + super() + this.optionsRepository = optionsRepository + } + + protected async process(request: SetMutedRequest): Promise { + const options = await this.optionsRepository.getOptions() + this.optionsRepository.setOptions( + Options.create({ + instrument: options.instrument, + muted: request.muted + }) + ) + } +} diff --git a/apps/web/src/application/services/StopSound/StopSoundDtos.ts b/apps/web/src/application/services/StopSound/StopSoundDtos.ts new file mode 100644 index 0000000..223bd57 --- /dev/null +++ b/apps/web/src/application/services/StopSound/StopSoundDtos.ts @@ -0,0 +1,5 @@ +export interface StopSoundRequest { + pitchClass: string + octave: number + instrument: string +} \ No newline at end of file diff --git a/apps/web/src/application/services/StopSound/StopSoundService.ts b/apps/web/src/application/services/StopSound/StopSoundService.ts new file mode 100644 index 0000000..c8ab2e6 --- /dev/null +++ b/apps/web/src/application/services/StopSound/StopSoundService.ts @@ -0,0 +1,20 @@ +import { PitchClassEnum } from '../../../domain/enum/PitchClassEnum' +import { SoundService } from '../../../domain/services/SoundService' +import { PitchClass } from '../../../domain/valueObjects/PitchClass' +import { ObservableService } from '../../ObservableService' +import { StopSoundRequest } from './StopSoundDtos' + +export class StopSoundService extends ObservableService{ + + private soundService: SoundService + + constructor(soundService: SoundService) { + super() + this.soundService = soundService + } + + protected async process(request: StopSoundRequest): Promise { + const pitchClass = PitchClass.create(request.pitchClass as PitchClassEnum) + await this.soundService.stopSound(pitchClass.value, request.octave, request.instrument) + } +} \ No newline at end of file diff --git a/apps/web/src/domain/entities/Cube.spec.ts b/apps/web/src/domain/entities/Cube.spec.ts new file mode 100644 index 0000000..fb1716f --- /dev/null +++ b/apps/web/src/domain/entities/Cube.spec.ts @@ -0,0 +1,119 @@ +import { describe, it, expect } from 'vitest' +import { Cube } from './Cube' +import { TransactionColor } from '../valueObjects/TransactionColor' +import { UnitInterval } from '../valueObjects/UnitInterval' +import { InvalidCubePositionError } from '../errors/InvalidCubePositionError' +import { TransactionColorEnum } from '../enum/TransactionColorEnum' + +describe('src/domain/entities/Cube', () => { + it('should create a cube with valid position', () => { + const color = TransactionColor.create(TransactionColorEnum.Orange) + const x = UnitInterval.create(0) + const cube = Cube.create(color, x) + + expect(cube.color).toBe(color) + expect(cube.x).toBe(x) + expect(cube.y).toBeInstanceOf(UnitInterval) + expect(cube.creation).toBeCloseTo(Date.now(), -2) + expect(cube.mirrored).toBeDefined() + expect(cube.uuid).toBeDefined() + expect(cube.age).toBeGreaterThan(0) + }) + + it('should not find the range of color', () => { + const color = TransactionColor.create('red' as TransactionColorEnum) + const x = UnitInterval.create(0) + + expect(() => { + Cube.create(color, x) + }).toThrow(Error) + }) + + it('should not create a cube with invalid position for orange', () => { + const color = TransactionColor.create(TransactionColorEnum.Orange) + const x = UnitInterval.create(0.7) + + expect(() => { + Cube.create(color, x) + }).toThrow(InvalidCubePositionError) + }) + + it('should not create a cube with invalid position for blue', () => { + const color = TransactionColor.create(TransactionColorEnum.Blue) + const x = UnitInterval.create(0.1) + + expect(() => { + Cube.create(color, x) + }).toThrow(InvalidCubePositionError) + }) + + it('should not create a cube with invalid position for purple', () => { + const color = TransactionColor.create(TransactionColorEnum.Purple) + const x = UnitInterval.create(0.3) + + expect(() => { + Cube.create(color, x) + }).toThrow(InvalidCubePositionError) + }) + + it('should not create a cube with invalid position for green', () => { + const color = TransactionColor.create(TransactionColorEnum.Green) + const x = UnitInterval.create(0.4) + + expect(() => { + Cube.create(color, x) + }).toThrow(InvalidCubePositionError) + }) + + it('should create a cube with y equals 0', () => { + const color = TransactionColor.create(TransactionColorEnum.Orange) + const x = UnitInterval.create(0) + const cube = Cube.create(color, x) + + expect(cube.y.value).toEqual(0) + }) + + it('should move the cube up', () => { + const cube = Cube.random() + + cube.moveUp(0.5) + + expect(cube.y.value).toEqual(0.5) + }) + + it('should move the cube up using the default step value', () => { + const cube = Cube.random() + + cube.moveUp() + + expect(cube.y.value).toEqual(0.01) + }) + + it('should not move the cube up if it is on top', () => { + const cube = Cube.random() + + cube.moveUp(1) + + expect(cube.y.value).toEqual(1) + + cube.moveUp(0.001) + + expect(cube.y.value).toEqual(1) + }) + + it('should check if the cube is on top', () => { + const cube = Cube.random() + + cube.moveUp(1) + + expect(cube.isOnTop).toBeTruthy() + }) + + it('should check if the cube is not on top', () => { + const cube = Cube.random() + + cube.moveUp(0.9999999) + + expect(cube.isOnTop).toBeFalsy() + }) +}) diff --git a/apps/web/src/domain/entities/Cube.ts b/apps/web/src/domain/entities/Cube.ts new file mode 100644 index 0000000..3532aba --- /dev/null +++ b/apps/web/src/domain/entities/Cube.ts @@ -0,0 +1,116 @@ +import { Entity, Uuid } from '@cryptochords/shared' +import { TransactionColorEnum } from '../enum/TransactionColorEnum' +import { InvalidCubePositionError } from '../errors/InvalidCubePositionError' +import { TransactionColor } from '../valueObjects/TransactionColor' +import { UnitInterval } from '../valueObjects/UnitInterval' +import { UnitIntervalRange } from '../valueObjects/UnitIntervalRange' + +export const CubeXRange = new Map([ + [TransactionColorEnum.Orange, UnitIntervalRange.create(0, 0.25)], + [TransactionColorEnum.Blue, UnitIntervalRange.create(0.25, 0.5)], + [TransactionColorEnum.Purple, UnitIntervalRange.create(0.5, 0.75)], + [TransactionColorEnum.Green, UnitIntervalRange.create(0.75, 1)], +]) + +interface CubeProps { + color: TransactionColor + x: UnitInterval + y: UnitInterval + creation: number + mirrored: boolean +} + +export class Cube extends Entity { + private constructor(color: TransactionColor, x: UnitInterval, uuid: Uuid) { + super({ + color, + x: x, + y: UnitInterval.create(0), + creation: Date.now(), + mirrored: Cube.randomMirrored() + }, uuid) + } + + private static randomMirrored() { + return Math.random() > 0.5 + } + + static create(color: TransactionColor, x: UnitInterval) { + if (!this.isValidPosition(color, x)) { + throw new InvalidCubePositionError() + } + + return new Cube(color, x, Uuid.create()) + } + + private static isValidPosition(color: TransactionColor, x: UnitInterval): boolean { + const range = CubeXRange.get(color.value) + + if (!range) { + throw new Error(`Range not setted for '${color.value}' color`) + } + + if (x.value < range.min || x.value > range.max) { + return false + } + + return true + } + + static random() { + const color = TransactionColor.random() + return new Cube(color, this.randomX(color), Uuid.create()) + } + + private static randomX(color: TransactionColor) { + if (color.value === 'orange') { + return UnitInterval.random(0, 0.25) + } else if (color.value === 'blue') { + return UnitInterval.random(0.25, 0.5) + } else if (color.value === 'purple') { + return UnitInterval.random(0.5, 0.75) + } + return UnitInterval.random(0.75) + } + + get color() { + return this.props.color + } + + get x() { + return this.props.x + } + + get y() { + return this.props.y + } + + get creation() { + return this.props.creation + } + + get mirrored() { + return this.props.mirrored + } + + get age() { + return Date.now() - this.props.creation + } + + public moveUp(step: number = 0.01) { + if (this.props.y.isMaxReached()) { + return + } + + this.props.y = this.props.y.increment(step) + } + + public recalculateYByAge(maxAge: number = 10_000) { + const y = Math.min(1, this.age / maxAge) + this.props.y = UnitInterval.create(y) + } + + get isOnTop() { + return this.props.y.isMaxReached() + } +} diff --git a/apps/web/src/domain/entities/Transaction.ts b/apps/web/src/domain/entities/Transaction.ts new file mode 100644 index 0000000..1e87c26 --- /dev/null +++ b/apps/web/src/domain/entities/Transaction.ts @@ -0,0 +1,31 @@ +import { Address, Timestamp, TxType } from '@cryptochords/shared' + +export interface TransactionProps { + txType: TxType + address: Address + timestamp: Timestamp +} + +export class Transaction { + private props: TransactionProps + + private constructor(props: TransactionProps) { + this.props = props + } + + static create(props: TransactionProps) { + return new Transaction(props) + } + + get txType() { + return this.props.txType + } + + get address() { + return this.props.address + } + + get timestamp() { + return this.props.timestamp + } +} \ No newline at end of file diff --git a/apps/web/src/domain/enum/InstrumentEnum.ts b/apps/web/src/domain/enum/InstrumentEnum.ts new file mode 100644 index 0000000..929dee4 --- /dev/null +++ b/apps/web/src/domain/enum/InstrumentEnum.ts @@ -0,0 +1,43 @@ +export enum InstrumentEnum { + BassElectric = 'bass-electric', + Bassoon = 'bassoon', + Clarinet = 'clarinet', + Contrabass = 'contrabass', + Flute = 'flute', + FrenchHorn = 'french-horn', + GuitarAcoustic = 'guitar-acoustic', + GuitarElectric = 'guitar-electric', + GuitarNylon = 'guitar-nylon', + Harmonium = 'harmonium', + Harp = 'harp', + Organ = 'organ', + Piano = 'piano', + Saxophone = 'saxophone', + Trombone = 'trombone', + Trumpet = 'trumpet', + Tuba = 'tuba', + Violin = 'violin', + Xylophone = 'xylophone', +} + +export const instrumentLabels: Map = new Map([ + [InstrumentEnum.BassElectric, 'Bass Electric'], + [InstrumentEnum.Bassoon, 'Bassoon'], + [InstrumentEnum.Clarinet, 'Clarinet'], + [InstrumentEnum.Contrabass, 'Contrabass'], + [InstrumentEnum.Flute, 'Flute'], + [InstrumentEnum.FrenchHorn, 'French Horn'], + [InstrumentEnum.GuitarAcoustic, 'Guitar Acoustic'], + [InstrumentEnum.GuitarElectric, 'Guitar Electric'], + [InstrumentEnum.GuitarNylon, 'Guitar Nylon'], + [InstrumentEnum.Harmonium, 'Harmonium'], + [InstrumentEnum.Harp, 'Harp'], + [InstrumentEnum.Organ, 'Organ'], + [InstrumentEnum.Piano, 'Piano'], + [InstrumentEnum.Saxophone, 'Saxophone'], + [InstrumentEnum.Trombone, 'Trombone'], + [InstrumentEnum.Trumpet, 'Trumpet'], + [InstrumentEnum.Tuba, 'Tuba'], + [InstrumentEnum.Violin, 'Violin'], + [InstrumentEnum.Xylophone, 'Xylophone'], +]) \ No newline at end of file diff --git a/apps/web/src/domain/enum/KeyShapeEnum.ts b/apps/web/src/domain/enum/KeyShapeEnum.ts new file mode 100644 index 0000000..3412eec --- /dev/null +++ b/apps/web/src/domain/enum/KeyShapeEnum.ts @@ -0,0 +1,7 @@ +export enum KeyShapeEnum { + Black = 'black', + White = 'white', + WhiteMiddle = 'white-middle', + WhiteLeft = 'white-left', + WhiteRight = 'white-right' +} \ No newline at end of file diff --git a/apps/web/src/domain/enum/PitchClassEnum.ts b/apps/web/src/domain/enum/PitchClassEnum.ts new file mode 100644 index 0000000..0f8bfc2 --- /dev/null +++ b/apps/web/src/domain/enum/PitchClassEnum.ts @@ -0,0 +1,14 @@ +export enum PitchClassEnum { + C = 'C', + C_SHARP = 'C#', + D = 'D', + D_SHARP = 'D#', + E = 'E', + F = 'F', + F_SHARP = 'F#', + G = 'G', + G_SHARP = 'G#', + A = 'A', + A_SHARP = 'A#', + B = 'B' +} diff --git a/apps/web/src/domain/enum/TransactionColorEnum.ts b/apps/web/src/domain/enum/TransactionColorEnum.ts new file mode 100644 index 0000000..f4d415c --- /dev/null +++ b/apps/web/src/domain/enum/TransactionColorEnum.ts @@ -0,0 +1,6 @@ +export enum TransactionColorEnum { + Orange = 'orange', + Blue = 'blue', + Purple = 'purple', + Green = 'green' +} diff --git a/apps/web/src/domain/errors/InvalidCubePositionError.ts b/apps/web/src/domain/errors/InvalidCubePositionError.ts new file mode 100644 index 0000000..1509bfe --- /dev/null +++ b/apps/web/src/domain/errors/InvalidCubePositionError.ts @@ -0,0 +1,7 @@ +import { DomainError } from '@cryptochords/shared' + +export class InvalidCubePositionError extends DomainError { + constructor() { + super('INVALID_CUBE_POSITION', true) + } +} diff --git a/apps/web/src/domain/errors/InvalidIdPitchClassError.ts b/apps/web/src/domain/errors/InvalidIdPitchClassError.ts new file mode 100644 index 0000000..5eb8ffd --- /dev/null +++ b/apps/web/src/domain/errors/InvalidIdPitchClassError.ts @@ -0,0 +1,7 @@ +import { DomainError } from '@cryptochords/shared' + +export class InvalidPitchClassError extends DomainError { + constructor() { + super('INVALID_PITCH_CLASS', false) + } +} diff --git a/apps/web/src/domain/errors/InvalidUnitIntervalError.ts b/apps/web/src/domain/errors/InvalidUnitIntervalError.ts new file mode 100644 index 0000000..6ec9207 --- /dev/null +++ b/apps/web/src/domain/errors/InvalidUnitIntervalError.ts @@ -0,0 +1,7 @@ +import { DomainError } from '@cryptochords/shared' + +export class InvalidUnitIntervalError extends DomainError { + constructor() { + super('INVALID_UNIT_INTERVAL', true) + } +} diff --git a/apps/web/src/domain/events/TransactionCreatedEvent.ts b/apps/web/src/domain/events/TransactionCreatedEvent.ts new file mode 100644 index 0000000..6be77c8 --- /dev/null +++ b/apps/web/src/domain/events/TransactionCreatedEvent.ts @@ -0,0 +1,21 @@ +import { Event } from '@cryptochords/shared' +import { Transaction } from '../entities/Transaction' + +export class TransactionCreatedEvent extends Event { + static eventKey:symbol = Symbol('TransactionCreatedEvent') + + private readonly transaction: Transaction + + constructor(transaction: Transaction) { + super(TransactionCreatedEvent.eventKey) + this.transaction = transaction + } + + static create(transaction: Transaction): TransactionCreatedEvent { + return new TransactionCreatedEvent(transaction) + } + + getTransaction(): Transaction { + return this.transaction + } +} \ No newline at end of file diff --git a/apps/web/src/domain/factories/KeyboardFactory.test.ts b/apps/web/src/domain/factories/KeyboardFactory.test.ts new file mode 100644 index 0000000..ffac32a --- /dev/null +++ b/apps/web/src/domain/factories/KeyboardFactory.test.ts @@ -0,0 +1,119 @@ +import { describe, expect, it } from 'vitest' +import { KeyboardFactory } from './KeyboardFactory' +import { KeyShapeEnum } from '../enum/KeyShapeEnum' +import { PitchClassEnum } from '../enum/PitchClassEnum' + +describe('src/domain/factories/KeyboardFactory', () => { + it('should be defined', () => { + expect(KeyboardFactory).toBeDefined() + }) + + it('should return a keyboard with 88 keys', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 0, + numberOfKeys: 88, + initialPitchClass: PitchClassEnum.A + }) + expect(keyboard.keys.length).toBe(88) + }) + + it('should return a keyboard with the correct keys', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 1, + numberOfKeys: 12, + initialPitchClass: PitchClassEnum.F + }) + expect(keyboard.keys[0].pitch.pitchClass.value).toBe(PitchClassEnum.F) + expect(keyboard.keys[0].pitch.octave).toBe(1) + expect(keyboard.keys[0].keyShape.value).toBe(KeyShapeEnum.WhiteLeft) + + expect(keyboard.keys[1].pitch.pitchClass.value).toBe(PitchClassEnum.F_SHARP) + expect(keyboard.keys[1].pitch.octave).toBe(1) + expect(keyboard.keys[1].keyShape.value).toBe(KeyShapeEnum.Black) + + expect(keyboard.keys[2].pitch.pitchClass.value).toBe(PitchClassEnum.G) + expect(keyboard.keys[2].pitch.octave).toBe(1) + expect(keyboard.keys[2].keyShape.value).toBe(KeyShapeEnum.WhiteMiddle) + + expect(keyboard.keys[3].pitch.pitchClass.value).toBe(PitchClassEnum.G_SHARP) + expect(keyboard.keys[3].pitch.octave).toBe(1) + expect(keyboard.keys[3].keyShape.value).toBe(KeyShapeEnum.Black) + + expect(keyboard.keys[4].pitch.pitchClass.value).toBe(PitchClassEnum.A) + expect(keyboard.keys[4].pitch.octave).toBe(1) + expect(keyboard.keys[4].keyShape.value).toBe(KeyShapeEnum.WhiteMiddle) + + expect(keyboard.keys[5].pitch.pitchClass.value).toBe(PitchClassEnum.A_SHARP) + expect(keyboard.keys[5].pitch.octave).toBe(1) + expect(keyboard.keys[5].keyShape.value).toBe(KeyShapeEnum.Black) + + expect(keyboard.keys[6].pitch.pitchClass.value).toBe(PitchClassEnum.B) + expect(keyboard.keys[6].pitch.octave).toBe(1) + expect(keyboard.keys[6].keyShape.value).toBe(KeyShapeEnum.WhiteRight) + + expect(keyboard.keys[7].pitch.pitchClass.value).toBe(PitchClassEnum.C) + expect(keyboard.keys[7].pitch.octave).toBe(2) + expect(keyboard.keys[7].keyShape.value).toBe(KeyShapeEnum.WhiteLeft) + + expect(keyboard.keys[8].pitch.pitchClass.value).toBe(PitchClassEnum.C_SHARP) + expect(keyboard.keys[8].pitch.octave).toBe(2) + expect(keyboard.keys[8].keyShape.value).toBe(KeyShapeEnum.Black) + + expect(keyboard.keys[9].pitch.pitchClass.value).toBe(PitchClassEnum.D) + expect(keyboard.keys[9].pitch.octave).toBe(2) + expect(keyboard.keys[9].keyShape.value).toBe(KeyShapeEnum.WhiteMiddle) + + expect(keyboard.keys[10].pitch.pitchClass.value).toBe(PitchClassEnum.D_SHARP) + expect(keyboard.keys[10].pitch.octave).toBe(2) + expect(keyboard.keys[10].keyShape.value).toBe(KeyShapeEnum.Black) + + expect(keyboard.keys[11].pitch.pitchClass.value).toBe(PitchClassEnum.E) + expect(keyboard.keys[11].pitch.octave).toBe(2) + expect(keyboard.keys[11].keyShape.value).toBe(KeyShapeEnum.WhiteRight) + }) + + it('should return a keyboard with a full white last key when it finish with a C key', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 1, + numberOfKeys: 2, + initialPitchClass: PitchClassEnum.B + }) + expect(keyboard.keys[1].keyShape.value).toBe(KeyShapeEnum.White) + }) + + it('should return a keyboard with a full white last key when it finish with a F key', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 1, + numberOfKeys: 2, + initialPitchClass: PitchClassEnum.E + }) + expect(keyboard.keys[1].keyShape.value).toBe(KeyShapeEnum.White) + }) + + it('should return a keyboard with a white-right last key when it finish with a D key', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 1, + numberOfKeys: 3, + initialPitchClass: PitchClassEnum.C + }) + expect(keyboard.keys[2].keyShape.value).toBe(KeyShapeEnum.WhiteRight) + }) + + it('should return a keyboard with a white-right last key when it finish with a G key', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 1, + numberOfKeys: 3, + initialPitchClass: PitchClassEnum.F + }) + expect(keyboard.keys[2].keyShape.value).toBe(KeyShapeEnum.WhiteRight) + }) + + it('should return a keyboard with a white-right last key when it finish with a A key', () => { + const keyboard = KeyboardFactory.create({ + initialOctave: 1, + numberOfKeys: 3, + initialPitchClass: PitchClassEnum.G + }) + expect(keyboard.keys[2].keyShape.value).toBe(KeyShapeEnum.WhiteRight) + }) +}) diff --git a/apps/web/src/domain/factories/KeyboardFactory.ts b/apps/web/src/domain/factories/KeyboardFactory.ts new file mode 100644 index 0000000..ee67555 --- /dev/null +++ b/apps/web/src/domain/factories/KeyboardFactory.ts @@ -0,0 +1,133 @@ +import { KeyShapeEnum } from '../enum/KeyShapeEnum' +import { PitchClassEnum } from '../enum/PitchClassEnum' +import { TransactionColorEnum } from '../enum/TransactionColorEnum' +import { Key, KeyProps } from '../valueObjects/Key' +import { KeyShape } from '../valueObjects/KeyShape' +import { Keyboard } from '../valueObjects/Keyboard' +import { Pitch } from '../valueObjects/Pitch' +import { PitchClass } from '../valueObjects/PitchClass' +import { UnitInterval } from '../valueObjects/UnitInterval' + + +enum KeyPosition { + Beginning, + Middle, + End +} + +export interface CreateProps { + numberOfKeys: number + initialPitchClass: PitchClassEnum + initialOctave: number +} + +export class KeyboardFactory { + static create(props: CreateProps) { + const keysProps: Pick[] = [] + let pitchClassValue = props.initialPitchClass + let octave = props.initialOctave + for (let i = 0; i < props.numberOfKeys; i++) { + const isLastKey = i === props.numberOfKeys - 1 + const isFirstKey = i === 0 + const position = isFirstKey ? KeyPosition.Beginning : isLastKey ? KeyPosition.End : KeyPosition.Middle + const keyShape = KeyShape.create(KeyboardFactory.getKeyShape(pitchClassValue, position)) + const pitchClass = PitchClass.create(pitchClassValue) + const pitch = Pitch.create({ pitchClass: pitchClass, octave }) + const color = KeyboardFactory.getColor(props.numberOfKeys, i) + const key = { pitch, keyShape, pressed: false, color } + keysProps.push(key) + pitchClassValue = KeyboardFactory.getNextNoteEnum(pitchClassValue) + if (pitchClassValue === PitchClassEnum.C) { + octave++ + } + } + + const keysPropsWithX = this.calculatePositions(keysProps) + + const keys = keysPropsWithX.map(key => Key.create(key)) + return Keyboard.create({ keys }) + } + + private static getColor(numberOfKeys: number, keyNumber: number) { + const quarter = numberOfKeys / 4 + const keyQuarter = Math.floor(keyNumber / quarter) + + return Object.values(TransactionColorEnum)[keyQuarter] + } + + private static calculatePositions(keysProps: Pick[]) { + const whiteKeysCount = keysProps.filter(key => key.keyShape.value !== KeyShapeEnum.Black).length + const minKeyCenterSpacing = 1 / (whiteKeysCount * 2 - 1) + const result = [] + let x = 0 + for (let i = 0; i < keysProps.length; i++) { + const shape = keysProps[i].keyShape.value + if (i === 0) { + x += minKeyCenterSpacing * 0.75 + } else if (shape === KeyShapeEnum.WhiteLeft || shape === KeyShapeEnum.White) { + x += minKeyCenterSpacing * 2 + } else { + x += minKeyCenterSpacing + } + result.push({ ...keysProps[i], x: UnitInterval.create(x) }) + } + return result + } + + private static getNextNoteEnum(pitchClass: PitchClassEnum) { + switch (pitchClass) { + case PitchClassEnum.C: + return PitchClassEnum.C_SHARP + case PitchClassEnum.C_SHARP: + return PitchClassEnum.D + case PitchClassEnum.D: + return PitchClassEnum.D_SHARP + case PitchClassEnum.D_SHARP: + return PitchClassEnum.E + case PitchClassEnum.E: + return PitchClassEnum.F + case PitchClassEnum.F: + return PitchClassEnum.F_SHARP + case PitchClassEnum.F_SHARP: + return PitchClassEnum.G + case PitchClassEnum.G: + return PitchClassEnum.G_SHARP + case PitchClassEnum.G_SHARP: + return PitchClassEnum.A + case PitchClassEnum.A: + return PitchClassEnum.A_SHARP + case PitchClassEnum.A_SHARP: + return PitchClassEnum.B + case PitchClassEnum.B: + return PitchClassEnum.C + } + } + + private static getKeyShape(pitchClass: PitchClassEnum, position: KeyPosition): KeyShapeEnum { + if (pitchClass === PitchClassEnum.C || pitchClass === PitchClassEnum.F) { + if (position === KeyPosition.End) { + return KeyShapeEnum.White + } + return KeyShapeEnum.WhiteLeft + } + + if (pitchClass === PitchClassEnum.E || pitchClass === PitchClassEnum.B) { + if (position === KeyPosition.Beginning) { + return KeyShapeEnum.White + } + return KeyShapeEnum.WhiteRight + } + + if (pitchClass === PitchClassEnum.D || pitchClass === PitchClassEnum.G || pitchClass === PitchClassEnum.A) { + if (position === KeyPosition.End) { + return KeyShapeEnum.WhiteRight + } + if (position === KeyPosition.Beginning) { + return KeyShapeEnum.WhiteLeft + } + return KeyShapeEnum.WhiteMiddle + } + + return KeyShapeEnum.Black + } +} diff --git a/apps/web/src/domain/repositories/CubeRepository.ts b/apps/web/src/domain/repositories/CubeRepository.ts new file mode 100644 index 0000000..87fd0a3 --- /dev/null +++ b/apps/web/src/domain/repositories/CubeRepository.ts @@ -0,0 +1,9 @@ +import { Uuid } from '@cryptochords/shared' +import { Cube } from '../entities/Cube' + +export interface CubeRepository { + list: () => Promise + create: (cube: Cube) => Promise + update: (cube: Cube) => Promise + delete: (id: Uuid) => Promise +} \ No newline at end of file diff --git a/apps/web/src/domain/repositories/KeyboardRepository.ts b/apps/web/src/domain/repositories/KeyboardRepository.ts new file mode 100644 index 0000000..063e7a4 --- /dev/null +++ b/apps/web/src/domain/repositories/KeyboardRepository.ts @@ -0,0 +1,6 @@ +import { Keyboard } from '../valueObjects/Keyboard' + +export interface KeyboardRepository { + setKeyboard: (keyboard: Keyboard) => void + getKeyboard: () => Keyboard | undefined +} \ No newline at end of file diff --git a/apps/web/src/domain/repositories/OptionsRepository.ts b/apps/web/src/domain/repositories/OptionsRepository.ts new file mode 100644 index 0000000..9ae5f43 --- /dev/null +++ b/apps/web/src/domain/repositories/OptionsRepository.ts @@ -0,0 +1,6 @@ +import { Options } from '../valueObjects/Options' + +export interface OptionsRepository { + setOptions: (options: Options) => Promise + getOptions: () => Promise +} \ No newline at end of file diff --git a/apps/web/src/domain/repositories/TransactionRepository.ts b/apps/web/src/domain/repositories/TransactionRepository.ts new file mode 100644 index 0000000..5982724 --- /dev/null +++ b/apps/web/src/domain/repositories/TransactionRepository.ts @@ -0,0 +1,6 @@ +import { Transaction } from '../entities/Transaction' + +export interface TransactionRepository { + create(block: Transaction): Promise + list(): Promise +} \ No newline at end of file diff --git a/apps/web/src/domain/services/SoundService.ts b/apps/web/src/domain/services/SoundService.ts new file mode 100644 index 0000000..5ae5fb3 --- /dev/null +++ b/apps/web/src/domain/services/SoundService.ts @@ -0,0 +1,7 @@ +import { PitchClassEnum } from '../enum/PitchClassEnum' + +export interface SoundService { + loadInstrument(instrument: string): Promise + playSound(pitch: PitchClassEnum, octave: number, instrument: string): Promise + stopSound(pitch: PitchClassEnum, octave: number, instrument: string): Promise +} diff --git a/apps/web/src/domain/valueObjects/Instrument.ts b/apps/web/src/domain/valueObjects/Instrument.ts new file mode 100644 index 0000000..83639ae --- /dev/null +++ b/apps/web/src/domain/valueObjects/Instrument.ts @@ -0,0 +1,20 @@ +import { ValueObject } from '@cryptochords/shared' +import { InstrumentEnum } from '../enum/InstrumentEnum' + +export interface InstrumentProps { + name: InstrumentEnum +} + +export class Instrument extends ValueObject{ + private constructor(props: InstrumentProps) { + super(props) + } + + static create(props: InstrumentProps) { + return new Instrument(props) + } + + get name() { + return this.props.name + } +} diff --git a/apps/web/src/domain/valueObjects/Key.ts b/apps/web/src/domain/valueObjects/Key.ts new file mode 100644 index 0000000..d4d5244 --- /dev/null +++ b/apps/web/src/domain/valueObjects/Key.ts @@ -0,0 +1,50 @@ +import { ValueObject } from '@cryptochords/shared' +import { KeyShape } from './KeyShape' +import { Pitch } from './Pitch' +import { UnitInterval } from './UnitInterval' + +export interface KeyProps { + keyShape: KeyShape + pitch: Pitch + x: UnitInterval + color: string + pressed: boolean +} + +export class Key extends ValueObject { + private constructor(props: KeyProps) { + super(props) + } + + static create(props: KeyProps) { + return new Key(props) + } + + press() { + this.props.pressed = true + } + + release() { + this.props.pressed = false + } + + get keyShape() { + return this.props.keyShape + } + + get pitch() { + return this.props.pitch + } + + get x() { + return this.props.x + } + + get color() { + return this.props.color + } + + get pressed() { + return this.props.pressed + } +} diff --git a/apps/web/src/domain/valueObjects/KeyShape.ts b/apps/web/src/domain/valueObjects/KeyShape.ts new file mode 100644 index 0000000..742906f --- /dev/null +++ b/apps/web/src/domain/valueObjects/KeyShape.ts @@ -0,0 +1,20 @@ +import { ValueObject } from '@cryptochords/shared' +import { KeyShapeEnum } from '../enum/KeyShapeEnum' + +interface KeyShapeProps { + value: KeyShapeEnum +} + +export class KeyShape extends ValueObject{ + private constructor(shape: KeyShapeEnum) { + super({ value: shape }) + } + + static create(shape: KeyShapeEnum) { + return new KeyShape(shape) + } + + get value() { + return this.props.value + } +} \ No newline at end of file diff --git a/apps/web/src/domain/valueObjects/Keyboard.ts b/apps/web/src/domain/valueObjects/Keyboard.ts new file mode 100644 index 0000000..eeaa035 --- /dev/null +++ b/apps/web/src/domain/valueObjects/Keyboard.ts @@ -0,0 +1,50 @@ +import { TxTypesEnum, ValueObject } from '@cryptochords/shared' +import { Key } from './Key' +import { KeyShapeEnum } from '../enum/KeyShapeEnum' + +export interface KeyboardProps { + keys: Key[] +} + +export class Keyboard extends ValueObject { + private constructor(props: KeyboardProps) { + super(props) + } + + static create(props: KeyboardProps) { + return new Keyboard(props) + } + + get keys() { + return this.props.keys + } + + findKey(pitchClass: string, octave: number): Key | undefined { + return this.props.keys.find((key) => + key.pitch.pitchClass.value === pitchClass + && key.pitch.octave === octave + ) + } + + getRandomWhiteKeyByTxType(txType: TxTypesEnum): Key { + if (txType === TxTypesEnum.Btc) + return this.getRandomWhiteKey(0, Math.floor(this.props.keys.length / 4)) + if (txType === TxTypesEnum.Eth) + return this.getRandomWhiteKey(Math.floor(this.props.keys.length / 4), Math.floor(this.props.keys.length / 2)) + if (txType === TxTypesEnum.Pop) + return this.getRandomWhiteKey(Math.floor(this.props.keys.length / 2), Math.floor(this.props.keys.length * 3 / 4)) + return this.getRandomWhiteKey(Math.floor(this.props.keys.length * 3 / 4), this.props.keys.length) + } + + getRandomKey(min: number, max: number): Key { + const randomIndex = Math.floor(Math.random() * (max - min)) + min + return this.props.keys[randomIndex] + } + + getRandomWhiteKey(min: number, max: number): Key { + const keysInTheRange = this.props.keys.slice(min, max) + const whiteKeys = keysInTheRange.filter((key) => key.keyShape.value !== KeyShapeEnum.Black ) + const randomIndex = Math.floor(Math.random() * whiteKeys.length) + return whiteKeys[randomIndex] + } +} diff --git a/apps/web/src/domain/valueObjects/Options.ts b/apps/web/src/domain/valueObjects/Options.ts new file mode 100644 index 0000000..4759d96 --- /dev/null +++ b/apps/web/src/domain/valueObjects/Options.ts @@ -0,0 +1,45 @@ +import { ValueObject } from '@cryptochords/shared' +import { Instrument } from './Instrument' +import { InstrumentEnum } from '../enum/InstrumentEnum' + +export interface OptionsProps { + muted: boolean + instrument: Instrument +} + +export interface OptionsJSON { + muted: boolean + instrument: string +} + +export class Options extends ValueObject{ + private constructor(props: OptionsProps) { + super(props) + } + + static create(props: OptionsProps) { + return new Options(props) + } + + get muted() { + return this.props.muted + } + + get instrument() { + return this.props.instrument + } + + toJSON(): OptionsJSON { + return { + muted: this.props.muted, + instrument: this.props.instrument.name + } + } + + static fromJSON(json: OptionsJSON) { + return Options.create({ + muted: json.muted, + instrument: Instrument.create({ name: json.instrument as InstrumentEnum}) + }) + } +} diff --git a/apps/web/src/domain/valueObjects/Pitch.ts b/apps/web/src/domain/valueObjects/Pitch.ts new file mode 100644 index 0000000..1d03500 --- /dev/null +++ b/apps/web/src/domain/valueObjects/Pitch.ts @@ -0,0 +1,77 @@ +import { ValueObject } from '@cryptochords/shared' +import { PitchClass } from './PitchClass' + +export interface PitchProps { + pitchClass: PitchClass + octave: number +} + +export class Pitch extends ValueObject { + private constructor(props: PitchProps) { + super(props) + } + + static create(props: PitchProps) { + return new Pitch(props) + } + + get pitchClass() { + return this.props.pitchClass + } + + get octave() { + return this.props.octave + } + + get next() { + const nextClass = this.props.pitchClass.next + return Pitch.create({ + pitchClass: nextClass, + octave: nextClass.value === 'C' ? this.props.octave + 1 : this.props.octave + }) + } + + get previous() { + const previousClass = this.props.pitchClass.previous + return Pitch.create({ + pitchClass: previousClass, + octave: previousClass.value === 'B' ? this.props.octave - 1 : this.props.octave + }) + } + + isBefore(pitch: Pitch) { + return this.props.octave < pitch.octave || (this.props.octave === pitch.octave && this.props.pitchClass.value < pitch.pitchClass.value) + } + + isAfter(pitch: Pitch) { + return this.props.octave > pitch.octave || (this.props.octave === pitch.octave && this.props.pitchClass.value > pitch.pitchClass.value) + } + + equals(pitch: Pitch) { + return this.props.octave === pitch.octave && this.props.pitchClass.value === pitch.pitchClass.value + } + + toString() { + return `${this.props.pitchClass.value}${this.props.octave}` + } + + static random(min: Pitch, max: Pitch) { + const range = Pitch.range(min, max) + return range[Math.floor(Math.random() * range.length)] + } + + static range(min: Pitch, max: Pitch): Pitch[] { + if(min.isAfter(max)) { + throw new Error('Min pitch must be before max pitch') + } + + const possibleValues:Pitch[] = [] + let current = min + while(!current.isAfter(max)) { + possibleValues.push(current) + current = current.next + } + + return possibleValues + } +} diff --git a/apps/web/src/domain/valueObjects/PitchClass.spec.ts b/apps/web/src/domain/valueObjects/PitchClass.spec.ts new file mode 100644 index 0000000..b185194 --- /dev/null +++ b/apps/web/src/domain/valueObjects/PitchClass.spec.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest' +import { PitchClass } from './PitchClass' +import { InvalidPitchClassError } from '../errors/InvalidIdPitchClassError' +import { PitchClassEnum } from '../enum/PitchClassEnum' + +describe('src/domain/valueObjects/PitchClass', () => { + it('should be defined', () => { + expect(PitchClass).toBeDefined() + }) + + it('should return the correct value', () => { + const pitchClass = PitchClass.create(PitchClassEnum.A) + expect(pitchClass.value).toBe(PitchClassEnum.A) + }) + + it('should throw an error when the value is invalid', () => { + expect(() => { + PitchClass.create('Z' as PitchClassEnum) + }).toThrow(InvalidPitchClassError) + }) +}) \ No newline at end of file diff --git a/apps/web/src/domain/valueObjects/PitchClass.ts b/apps/web/src/domain/valueObjects/PitchClass.ts new file mode 100644 index 0000000..23e4fd1 --- /dev/null +++ b/apps/web/src/domain/valueObjects/PitchClass.ts @@ -0,0 +1,38 @@ +import { ValueObject } from '@cryptochords/shared' +import { PitchClassEnum } from '../enum/PitchClassEnum' +import { InvalidPitchClassError } from '../errors/InvalidIdPitchClassError' + +export interface PitchClassProps { + value: PitchClassEnum +} + +export class PitchClass extends ValueObject { + private constructor(props: PitchClassProps) { + super(props) + } + + static create(value: PitchClassEnum) { + if (!PitchClass.isValidValue(value)) { + throw new InvalidPitchClassError() + } + return new PitchClass({ value }) + } + + private static isValidValue(value: PitchClassEnum) { + return Object.values(PitchClassEnum).includes(value) + } + + get value() { + return this.props.value + } + + get next() { + const nextIndex = (Object.values(PitchClassEnum).indexOf(this.props.value) + 1) % Object.values(PitchClassEnum).length + return PitchClass.create(Object.values(PitchClassEnum)[nextIndex]) + } + + get previous() { + const previousIndex = (Object.values(PitchClassEnum).indexOf(this.props.value) - 1 + Object.values(PitchClassEnum).length) % Object.values(PitchClassEnum).length + return PitchClass.create(Object.values(PitchClassEnum)[previousIndex]) + } +} diff --git a/apps/web/src/domain/valueObjects/TransactionColor.spec.ts b/apps/web/src/domain/valueObjects/TransactionColor.spec.ts new file mode 100644 index 0000000..8dcd138 --- /dev/null +++ b/apps/web/src/domain/valueObjects/TransactionColor.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import { TransactionColor } from './TransactionColor' +import { TransactionColorEnum } from '../enum/TransactionColorEnum' + +describe('src/domain/valueObjects/CubeColor', () => { + it('should be defined', () => { + expect(TransactionColor).toBeDefined() + }) + + describe('create', () => { + describe('when a valid color is provided as parameter', () => { + it('should set the valid color in the value props', () => { + const color = TransactionColor.create(TransactionColorEnum.Orange) + expect(color.value).toBe('orange') + }) + }) + }) + + describe('random', () => { + it('should return a random color', () => { + const color = TransactionColor.random() + expect(color.value).toBeDefined() + }) + }) + +}) \ No newline at end of file diff --git a/apps/web/src/domain/valueObjects/TransactionColor.ts b/apps/web/src/domain/valueObjects/TransactionColor.ts new file mode 100644 index 0000000..76bd7a1 --- /dev/null +++ b/apps/web/src/domain/valueObjects/TransactionColor.ts @@ -0,0 +1,44 @@ +import { TxTypesEnum, ValueObject } from '@cryptochords/shared' +import { TransactionColorEnum } from '../enum/TransactionColorEnum' + +interface TransactionColorProps { + value: TransactionColorEnum +} + +export class TransactionColor extends ValueObject { + private constructor(color: TransactionColorEnum) { + super({ value: color }) + } + + static createByTxType(txType: string) { + if (txType === TxTypesEnum.Btc) + return this.create(TransactionColorEnum.Orange) + + if (txType === TxTypesEnum.Eth) + return this.create(TransactionColorEnum.Blue) + + if (txType === TxTypesEnum.Pop) + return this.create(TransactionColorEnum.Purple) + + return this.create(TransactionColorEnum.Green) + } + + static create(color: TransactionColorEnum) { + return new TransactionColor(color) + } + + static random() { + return this.create(TransactionColor.randomColorValue()) + } + + private static randomColorValue(): TransactionColorEnum { + const enumValues = Object.values(TransactionColorEnum) + const randomIndex = Math.floor(Math.random() * enumValues.length) + const randomEnumValue = enumValues[randomIndex] + return randomEnumValue as TransactionColorEnum + } + + get value() { + return this.props.value + } +} diff --git a/apps/web/src/domain/valueObjects/UnitInterval.spec.ts b/apps/web/src/domain/valueObjects/UnitInterval.spec.ts new file mode 100644 index 0000000..dbda5fe --- /dev/null +++ b/apps/web/src/domain/valueObjects/UnitInterval.spec.ts @@ -0,0 +1,87 @@ +import { describe, expect, it } from 'vitest' +import { UnitInterval } from './UnitInterval' + +describe('src/domain/valueObjects/UnitInterval', () => { + it('should be defined', () => { + expect(UnitInterval).toBeDefined() + }) + + it('should throw an error when a value greater than 1 is provided', () => { + const test = () => { + UnitInterval.create(1.1) + } + + expect(test).toThrowError() + }) + + it('should throw an error when a value less than 0 is provided', () => { + const test = () => { + UnitInterval.create(-0.1) + } + + expect(test).toThrowError() + }) + + it('Should return a value between 0 and 1', () => { + const unitInterval = UnitInterval.random() + + expect(unitInterval.value).toBeGreaterThanOrEqual(0) + expect(unitInterval.value).toBeLessThanOrEqual(1) + }) + + it('Should return a value between 0 and 0.25', () => { + const unitInterval = UnitInterval.random(0, 0.25) + + expect(unitInterval.value).toBeGreaterThanOrEqual(0) + expect(unitInterval.value).toBeLessThanOrEqual(0.25) + }) + + it('Should return a value between 0.25 and 0.26', () => { + const unitInterval = UnitInterval.random(0.25, 0.26) + + expect(unitInterval.value).toBeGreaterThanOrEqual(0.25) + expect(unitInterval.value).toBeLessThanOrEqual(0.26) + }) + + it('Should return a value less than than 0.01 or equal', () => { + const unitInterval = UnitInterval.random(undefined, 0.01) + + expect(unitInterval.value).toBeGreaterThanOrEqual(0) + expect(unitInterval.value).toBeLessThanOrEqual(0.01) + }) + + it('Should return a value greater than than 0.9 or equal', () => { + const unitInterval = UnitInterval.random(0.9) + + expect(unitInterval.value).toBeGreaterThanOrEqual(0.9) + expect(unitInterval.value).toBeLessThanOrEqual(1) + }) + + it('Should increment the value correctly', () => { + const unitInterval = UnitInterval.create(0.5) + + const incremented = unitInterval.increment(0.1) + + expect(incremented.value).toBe(0.6) + }) + + it('Should not increment the value greater than 1', () => { + const unitInterval = UnitInterval.create(0.9) + + const incremented = unitInterval.increment(0.2) + + expect(incremented.value).toBe(1) + }) + + it('Should return true when the value is 1', () => { + const unitInterval = UnitInterval.create(1) + + expect(unitInterval.isMaxReached()).toBe(true) + }) + + it('Should return false when the value is less than 1', () => { + const unitInterval = UnitInterval.create(0.9) + + expect(unitInterval.isMaxReached()).toBe(false) + }) +}) diff --git a/apps/web/src/domain/valueObjects/UnitInterval.ts b/apps/web/src/domain/valueObjects/UnitInterval.ts new file mode 100644 index 0000000..46d67ec --- /dev/null +++ b/apps/web/src/domain/valueObjects/UnitInterval.ts @@ -0,0 +1,50 @@ +import { ValueObject } from '@cryptochords/shared' +import { InvalidUnitIntervalError } from '../errors/InvalidUnitIntervalError' + + +interface UnitIntevalProps { + value: number +} + +export class UnitInterval extends ValueObject { + private constructor(value: number) { + super({ value }) + } + + static create(value: number) { + if (typeof value !== 'number' || + value < 0 || + value > 1) { + throw new InvalidUnitIntervalError() + } + + return new UnitInterval(value) + } + + static random(min?: number, max?: number): UnitInterval { + if (min === undefined) { + return this.random(0, max) + } + + if (max == undefined) { + return this.random(min, 1) + } + + const minSanitized = Math.max(0, min) + const maxSanitized = Math.min(1, max) + + return this.create(Math.random() * (maxSanitized - minSanitized) + minSanitized) + } + + increment(value: number) { + return UnitInterval.create(Math.min(this.props.value + value, 1)) + } + + isMaxReached() { + return this.props.value === 1 + } + + get value() { + return this.props.value + } +} diff --git a/apps/web/src/domain/valueObjects/UnitIntervalRange.spec.ts b/apps/web/src/domain/valueObjects/UnitIntervalRange.spec.ts new file mode 100644 index 0000000..a37ed24 --- /dev/null +++ b/apps/web/src/domain/valueObjects/UnitIntervalRange.spec.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from 'vitest' +import { UnitIntervalRange } from './UnitIntervalRange' + +describe('src/domain/valueObjects/UnitIntervalRange', () => { + it('should be defined', () => { + expect(UnitIntervalRange).toBeDefined() + }) + + describe('create', () => { + it('should return a UnitIntervalRange', () => { + const unitIntervalRange = UnitIntervalRange.create(0, 1) + expect(unitIntervalRange).toBeInstanceOf(UnitIntervalRange) + }) + }) +}) diff --git a/apps/web/src/domain/valueObjects/UnitIntervalRange.ts b/apps/web/src/domain/valueObjects/UnitIntervalRange.ts new file mode 100644 index 0000000..1d18d9b --- /dev/null +++ b/apps/web/src/domain/valueObjects/UnitIntervalRange.ts @@ -0,0 +1,27 @@ +import { ValueObject } from '@cryptochords/shared' +import { UnitInterval } from './UnitInterval' + + +interface UnitIntevalRangeProps { + min: UnitInterval, + max: UnitInterval +} + +export class UnitIntervalRange extends ValueObject { + private constructor(min: UnitInterval, max: UnitInterval) { + super({ min, max }) + } + + static create(min: number, max: number) { + return new UnitIntervalRange(UnitInterval.create(min), UnitInterval.create(max)) + } + + get min() { + return this.props.min.value + } + + get max() { + return this.props.max.value + } + +} diff --git a/apps/web/src/infrastructure/repositories/InMemoryCubeRepository.spec.ts b/apps/web/src/infrastructure/repositories/InMemoryCubeRepository.spec.ts new file mode 100644 index 0000000..f3ec3f3 --- /dev/null +++ b/apps/web/src/infrastructure/repositories/InMemoryCubeRepository.spec.ts @@ -0,0 +1,63 @@ +import { InMemoryCubeRepository } from './InMemoryCubeRepository' +import { Cube } from '../../domain/entities/Cube' +import { beforeEach, describe, expect, it } from 'vitest' +import { Uuid } from '@cryptochords/shared' + +describe('src/infrastructure/repositories/InMemoryCubeRepository', () => { + let repository: InMemoryCubeRepository + + beforeEach(() => { + repository = new InMemoryCubeRepository() + }) + + describe('list', () => { + it('should return an empty array when no cubes are stored', async () => { + const cubes = await repository.list() + expect(cubes).toEqual([]) + }) + }) + + describe('create', () => { + it('should store the cube and return it', async () => { + const cube: Cube = Cube.random() + const createdCube = await repository.create(cube) + expect(createdCube).toEqual(cube) + }) + }) + + describe('delete', () => { + it('should remove the cube with the given id', async () => { + const cube: Cube = Cube.random() + await repository.create(cube) + await repository.delete(cube.uuid) + const cubes = await repository.list() + expect(cubes).toEqual([]) + }) + }) + + describe('update', () => { + it('should update the cube and return it', async () => { + const id = Uuid.create() + const cube: Cube = { + uuid: id, + x: { value: 0.5 }, + y: { value: 0 }, + color: { value: 'blue' }, + mirrored: false, + } as Cube + await repository.create(cube) + + const updatedCube = { + uuid: id, + x: { value: 0.7 }, + y: { value: 0.1 }, + color: { value: 'orange' }, + mirrored: true, + } as Cube + + const result = await repository.update(updatedCube) + expect(result).toEqual(updatedCube) + expect(await repository.list()).toEqual([updatedCube]) + }) + }) +}) diff --git a/apps/web/src/infrastructure/repositories/InMemoryCubeRepository.ts b/apps/web/src/infrastructure/repositories/InMemoryCubeRepository.ts new file mode 100644 index 0000000..bb71d20 --- /dev/null +++ b/apps/web/src/infrastructure/repositories/InMemoryCubeRepository.ts @@ -0,0 +1,25 @@ +import { Uuid } from '@cryptochords/shared' +import { Cube } from '../../domain/entities/Cube' +import { CubeRepository } from '../../domain/repositories/CubeRepository' + +export class InMemoryCubeRepository implements CubeRepository { + private cubes: Map = new Map() + + async list(): Promise { + return Array.from(this.cubes.values()) + } + + async create(cube: Cube): Promise { + this.cubes.set(cube.uuid.value, cube) + return cube + } + + async delete(id: Uuid): Promise { + this.cubes.delete(id.value) + } + + async update(cube: Cube): Promise { + this.cubes.set(cube.uuid.value, cube) + return cube + } +} diff --git a/apps/web/src/infrastructure/repositories/InMemoryKeyboardRepository.ts b/apps/web/src/infrastructure/repositories/InMemoryKeyboardRepository.ts new file mode 100644 index 0000000..1c86c64 --- /dev/null +++ b/apps/web/src/infrastructure/repositories/InMemoryKeyboardRepository.ts @@ -0,0 +1,13 @@ +import { Keyboard } from '../../domain/valueObjects/Keyboard' + +export class InMemoryKeyboardRepository { + private keyboard: Keyboard | undefined + + public setKeyboard(keyboard: Keyboard) { + this.keyboard = keyboard + } + + public getKeyboard() { + return this.keyboard + } +} diff --git a/apps/web/src/infrastructure/repositories/LimitedInMemoryTransactionRepository.ts b/apps/web/src/infrastructure/repositories/LimitedInMemoryTransactionRepository.ts new file mode 100644 index 0000000..ef299a0 --- /dev/null +++ b/apps/web/src/infrastructure/repositories/LimitedInMemoryTransactionRepository.ts @@ -0,0 +1,25 @@ +import { Transaction } from '../../domain/entities/Transaction' +import { TransactionRepository } from '../../domain/repositories/TransactionRepository' + +export class LimitedInMemoryTransactionRepository implements TransactionRepository { + private transactions: Transaction[] = [] + + constructor(private max = 9) { + // + } + + async create(transaction: Transaction): Promise { + this.transactions.push(transaction) + this.enforceMaxSize() + } + + private enforceMaxSize() { + if (this.transactions.length > this.max) { + this.transactions.shift() + } + } + + async list(): Promise { + return this.transactions + } +} \ No newline at end of file diff --git a/apps/web/src/infrastructure/repositories/LocalStorageOptionsRepository.ts b/apps/web/src/infrastructure/repositories/LocalStorageOptionsRepository.ts new file mode 100644 index 0000000..f090a2d --- /dev/null +++ b/apps/web/src/infrastructure/repositories/LocalStorageOptionsRepository.ts @@ -0,0 +1,22 @@ +import { InstrumentEnum } from '../../domain/enum/InstrumentEnum' +import { OptionsRepository } from '../../domain/repositories/OptionsRepository' +import { Instrument } from '../../domain/valueObjects/Instrument' +import { Options } from '../../domain/valueObjects/Options' + +export class LocalStorageOptionsRepository implements OptionsRepository { + public async setOptions(options: Options) { + localStorage.setItem('options', JSON.stringify(options.toJSON())) + } + + public async getOptions() { + const options = localStorage.getItem('options') + if (options) { + return Options.fromJSON(JSON.parse(options)) + } + + return Options.create({ + muted: true, + instrument: Instrument.create({ name: InstrumentEnum.Piano }) + }) + } +} diff --git a/apps/web/src/infrastructure/services/ToneJs.ts b/apps/web/src/infrastructure/services/ToneJs.ts new file mode 100644 index 0000000..f7a4db1 --- /dev/null +++ b/apps/web/src/infrastructure/services/ToneJs.ts @@ -0,0 +1,83 @@ +import { Instrument, InstrumentOptions } from 'tone/build/esm/instrument/Instrument' +import InstrumentBassElectricMp3 from 'tonejs-instrument-bass-electric-mp3' +import InstrumentBassoonMp3 from 'tonejs-instrument-bassoon-mp3' +import InstrumentClarinetMp3 from 'tonejs-instrument-clarinet-mp3' +import InstrumentContrabassMp3 from 'tonejs-instrument-contrabass-mp3' +import InstrumentFluteMp3 from 'tonejs-instrument-flute-mp3' +import InstrumentFrenchHornMp3 from 'tonejs-instrument-french-horn-mp3' +import InstrumentGuitarAcousticMp3 from 'tonejs-instrument-guitar-acoustic-mp3' +import InstrumentGuitarElectricMp3 from 'tonejs-instrument-guitar-electric-mp3' +import InstrumentGuitarNylonMp3 from 'tonejs-instrument-guitar-nylon-mp3' +import InstrumentHarmoniumMp3 from 'tonejs-instrument-harmonium-mp3' +import InstrumentHarpMp3 from 'tonejs-instrument-harp-mp3' +import InstrumentOrganMp3 from 'tonejs-instrument-organ-mp3' +import InstrumentPianoMp3 from 'tonejs-instrument-piano-mp3' +import InstrumentSaxophoneMp3 from 'tonejs-instrument-saxophone-mp3' +import InstrumentTromboneMp3 from 'tonejs-instrument-trombone-mp3' +import InstrumentTrumpetMp3 from 'tonejs-instrument-trumpet-mp3' +import InstrumentTubaMp3 from 'tonejs-instrument-tuba-mp3' +import InstrumentViolinMp3 from 'tonejs-instrument-violin-mp3' +import InstrumentXylophoneMp3 from 'tonejs-instrument-xylophone-mp3' + +import { InstrumentEnum } from '../../domain/enum/InstrumentEnum' +import { SoundService } from '../../domain/services/SoundService' + + +export class ToneJS implements SoundService { + static instruments: Map> = new Map() + + static async getInstrument(instrumentId: string): Promise> { + let instrument = ToneJS.instruments.get(instrumentId) + if (instrument) { + return instrument + } + + const InstrumentClass = ToneJS.getInstrumentClass(instrumentId) + + return new Promise>((resolve) => { + instrument = new InstrumentClass({ + onload: () => { + ToneJS.instruments.set(instrumentId, instrument!) + resolve(ToneJS.getInstrument(instrumentId)) + } + }).toDestination() + }) + } + + static getInstrumentClass(instrumentId: string) { + switch (instrumentId) { + case InstrumentEnum.BassElectric: return InstrumentBassElectricMp3 + case InstrumentEnum.Bassoon: return InstrumentBassoonMp3 + case InstrumentEnum.Clarinet: return InstrumentClarinetMp3 + case InstrumentEnum.Contrabass: return InstrumentContrabassMp3 + case InstrumentEnum.Flute: return InstrumentFluteMp3 + case InstrumentEnum.FrenchHorn: return InstrumentFrenchHornMp3 + case InstrumentEnum.GuitarAcoustic: return InstrumentGuitarAcousticMp3 + case InstrumentEnum.GuitarElectric: return InstrumentGuitarElectricMp3 + case InstrumentEnum.GuitarNylon: return InstrumentGuitarNylonMp3 + case InstrumentEnum.Harmonium: return InstrumentHarmoniumMp3 + case InstrumentEnum.Harp: return InstrumentHarpMp3 + case InstrumentEnum.Organ: return InstrumentOrganMp3 + case InstrumentEnum.Piano: return InstrumentPianoMp3 + case InstrumentEnum.Saxophone: return InstrumentSaxophoneMp3 + case InstrumentEnum.Trombone: return InstrumentTromboneMp3 + case InstrumentEnum.Trumpet: return InstrumentTrumpetMp3 + case InstrumentEnum.Tuba: return InstrumentTubaMp3 + case InstrumentEnum.Violin: return InstrumentViolinMp3 + case InstrumentEnum.Xylophone: return InstrumentXylophoneMp3 + default: throw new Error(`Instrument ${instrumentId} not found`) + } + } + + async loadInstrument(instrument: string): Promise { + await ToneJS.getInstrument(instrument) + } + + async playSound(pitch: string, octave: number, instrument: string): Promise { + (await ToneJS.getInstrument(instrument)).triggerAttack(`${pitch}${octave}`) + } + + async stopSound(pitch: string, octave: number, instrument: string): Promise { + (await ToneJS.getInstrument(instrument)).triggerRelease(`${pitch}${octave}`) + } +} \ No newline at end of file diff --git a/apps/web/src/presentation/common/base/Presenter.ts b/apps/web/src/presentation/common/base/Presenter.ts new file mode 100644 index 0000000..815dee3 --- /dev/null +++ b/apps/web/src/presentation/common/base/Presenter.ts @@ -0,0 +1,3 @@ +import { StateController } from './StateController' + +export abstract class Presenter extends StateController {} \ No newline at end of file diff --git a/apps/web/src/presentation/common/base/StateController.spec.ts b/apps/web/src/presentation/common/base/StateController.spec.ts new file mode 100644 index 0000000..041e70b --- /dev/null +++ b/apps/web/src/presentation/common/base/StateController.spec.ts @@ -0,0 +1,87 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { StateController } from './StateController' + +type State = { + count: number +} + +const initalState: State = { + count: 0, +} + +class SimpleStateController extends StateController { + public increment(): void { + this.changeState({ count: this.state.count + 1 }) + } +} + +describe('src/presentation/common/base/StateController', () => { + let stateController: SimpleStateController + + beforeEach(() => { + stateController = new SimpleStateController(initalState) + }) + + it('should initialize with initial state', () => { + expect(stateController.state).toEqual(initalState) + }) + + it('should update state correctly', () => { + stateController.increment() + expect(stateController.state).toEqual({ count: 1 }) + }) + + describe('subscribe', () => { + it('should notify subscribers when state changes', () => { + const subscriber1 = vi.fn() + const subscriber2 = vi.fn() + + stateController.subscribe(subscriber1) + stateController.subscribe(subscriber2) + + stateController.increment() + + expect(subscriber1).toHaveBeenCalledWith({ count: 1 }) + expect(subscriber2).toHaveBeenCalledWith({ count: 1 }) + }) + + it('should send current status to subscriber when sendCurrentStatus is true', () => { + const subscriber = vi.fn() + + stateController.subscribe(subscriber, true) + + expect(subscriber).toHaveBeenCalledWith(initalState) + }) + }) + + describe('unsubscribe', () => { + + it('should unsubscribe subscribers correctly', () => { + const subscriber1 = vi.fn() + const subscriber2 = vi.fn() + + stateController.subscribe(subscriber1) + stateController.subscribe(subscriber2) + + stateController.increment() + + stateController.unsubscribe(subscriber1) + + stateController.increment() + + expect(subscriber1).toHaveBeenCalledTimes(1) + expect(subscriber2).toHaveBeenCalledTimes(2) + }) + + it('should not throw an error or affect the listeners if unsubscribe an unsubscribed listener', () => { + const subscriber1 = vi.fn() + const subscriber2 = vi.fn() + + stateController.subscribe(subscriber1) + stateController.unsubscribe(subscriber2) + + stateController.increment() + expect(subscriber1).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/apps/web/src/presentation/common/base/StateController.ts b/apps/web/src/presentation/common/base/StateController.ts new file mode 100644 index 0000000..8e0e801 --- /dev/null +++ b/apps/web/src/presentation/common/base/StateController.ts @@ -0,0 +1,52 @@ +type Subscription = (state: State) => void + +export abstract class StateController { + private internalState: State + private listeners: Subscription[] = [] + + constructor(initalState: State) { + this.internalState = initalState + } + + public get state(): State { + return this.internalState + } + + protected changeState(state: Partial): void { + this.setState({ + ...this.internalState, + ...state + }) + } + + protected setState(state: State): void { + this.internalState = state + this.notifyStateChangeToListeners() + } + + protected notifyStateChangeToListeners() { + if (this.listeners.length > 0) { + this.listeners.forEach(this.notifyStateChangeToListener.bind(this)) + } + } + + protected notifyStateChangeToListener(listener: Subscription) { + listener(this.state) + } + + public subscribe(listener: Subscription, sendCurrentStatus = false): void { + this.listeners.push(listener) + if (sendCurrentStatus) { + this.notifyStateChangeToListener(listener) + } + } + + public unsubscribe(listener: Subscription): void { + const index = this.listeners.indexOf(listener) + if (index < 0) { + return + } + + this.listeners.splice(index, 1) + } +} diff --git a/apps/web/src/presentation/common/context/domainServices.ts b/apps/web/src/presentation/common/context/domainServices.ts new file mode 100644 index 0000000..07e18f9 --- /dev/null +++ b/apps/web/src/presentation/common/context/domainServices.ts @@ -0,0 +1,10 @@ +import { SoundService } from '../../../domain/services/SoundService' +import { ToneJS } from '../../../infrastructure/services/ToneJs' + +export interface DomainServices { + soundService: SoundService +} + +export const domainServices: DomainServices = { + soundService: new ToneJS() +} diff --git a/apps/web/src/presentation/common/context/presenters.ts b/apps/web/src/presentation/common/context/presenters.ts new file mode 100644 index 0000000..34ec0c6 --- /dev/null +++ b/apps/web/src/presentation/common/context/presenters.ts @@ -0,0 +1,54 @@ +import { Event, ObservableSet } from '@cryptochords/shared' +import { AppPresenter } from '../presenter/app/AppPresenter' +import { CubesPresenter } from '../presenter/cubes/CubesPresenter' +import { KeyboardPresenter } from '../presenter/keyboard/KeyboardPresenter' +import { TransactionsPresenter } from '../presenter/transactions/TransactionsPresenter' +import { services } from './services' +import { OptionsPresenter } from '../presenter/options/OptionsPresenter' + +export interface Presenters { + cubesPresenter: CubesPresenter + appPresenter: AppPresenter + keyboardPresenter: KeyboardPresenter + transactionsPresenter: TransactionsPresenter + optionsPresenter: OptionsPresenter +} + +const cubesPresenter = new CubesPresenter( + services.getCubes, + services.recalculateCubePosition +) + +const appPresenter = new AppPresenter( + services.createTransaction +) + +const keyboardPresenter = new KeyboardPresenter( + services.createKeyboard, + services.getKeyboard, + new ObservableSet( + services.pressKey, + services.releaseKey + ) +) + +const transactionsPresenter = new TransactionsPresenter( + services.listTransactions, + services.createTransaction +) + +const optionsPresenter = new OptionsPresenter( + services.getOptions, + services.setMuted, + services.setInstrument, + services.loadInstrument +) + + +export const presenters: Presenters = { + cubesPresenter, + appPresenter, + keyboardPresenter, + transactionsPresenter, + optionsPresenter +} diff --git a/apps/web/src/presentation/common/context/repositories.ts b/apps/web/src/presentation/common/context/repositories.ts new file mode 100644 index 0000000..da70f68 --- /dev/null +++ b/apps/web/src/presentation/common/context/repositories.ts @@ -0,0 +1,22 @@ +import { CubeRepository } from '../../../domain/repositories/CubeRepository' +import { KeyboardRepository } from '../../../domain/repositories/KeyboardRepository' +import { OptionsRepository } from '../../../domain/repositories/OptionsRepository' +import { TransactionRepository } from '../../../domain/repositories/TransactionRepository' +import { InMemoryCubeRepository } from '../../../infrastructure/repositories/InMemoryCubeRepository' +import { InMemoryKeyboardRepository } from '../../../infrastructure/repositories/InMemoryKeyboardRepository' +import { LimitedInMemoryTransactionRepository } from '../../../infrastructure/repositories/LimitedInMemoryTransactionRepository' +import { LocalStorageOptionsRepository } from '../../../infrastructure/repositories/LocalStorageOptionsRepository' + +export interface Repositories { + cubeRepository: CubeRepository + transactionRepository: TransactionRepository, + keyboardRepository: KeyboardRepository, + optionsRepository: OptionsRepository +} + +export const repositories: Repositories = { + cubeRepository: new InMemoryCubeRepository(), + transactionRepository: new LimitedInMemoryTransactionRepository(), + keyboardRepository: new InMemoryKeyboardRepository(), + optionsRepository: new LocalStorageOptionsRepository() +} diff --git a/apps/web/src/presentation/common/context/services.ts b/apps/web/src/presentation/common/context/services.ts new file mode 100644 index 0000000..123de7e --- /dev/null +++ b/apps/web/src/presentation/common/context/services.ts @@ -0,0 +1,71 @@ +import { CreateCubeService } from '../../../application/services/CreateCube/CreateCubeService' +import { CreateKeyboardService } from '../../../application/services/CreateKeyboard/CreateKeyboardService' +import { CreateTransactionService } from '../../../application/services/CreateTransaction/CreateTransactionService' +import { GetCubesService } from '../../../application/services/GetCubes/GetCubesService' +import { GetKeyboardService } from '../../../application/services/GetKeyboard/GetKeyboardService' +import { ListTransactionsService } from '../../../application/services/ListTransactions/ListTransactionsService' +import { PressKeyService } from '../../../application/services/PressKey/PressKeyService' +import { ReleaseKeyService } from '../../../application/services/ReleaseKey/ReleaseKeyService' +import { repositories } from './repositories' +import { domainServices } from './domainServices' +import { PlaySoundService } from '../../../application/services/PlaySound/PlaySoundService' +import { StopSoundService } from '../../../application/services/StopSound/StopSoundService' +import { GetOptionsService } from '../../../application/services/GetOptions/GetOptionsService' +import { SetMutedService } from '../../../application/services/SetMuted/SetMutedService' +import { SetInstrumentService } from '../../../application/services/SetInstrument/SetInstrumentService' +import { LoadInstrumentService } from '../../../application/services/LoadInstrument/LoadInstrumentService' +import { RecalculateCubePositionsService } from '../../../application/services/RecalculateCubePositions/RecalculateCubePositionsService' + +export interface Services { + createTransaction: CreateTransactionService + getCubes: GetCubesService + createCube: CreateCubeService + recalculateCubePosition: RecalculateCubePositionsService + createKeyboard: CreateKeyboardService + pressKey: PressKeyService + releaseKey: ReleaseKeyService + listTransactions: ListTransactionsService + getKeyboard: GetKeyboardService + getOptions: GetOptionsService + setMuted: SetMutedService + setInstrument: SetInstrumentService + loadInstrument: LoadInstrumentService +} + +const getCubes = new GetCubesService(repositories.cubeRepository) +const createCube = new CreateCubeService(repositories.cubeRepository) +const recalculateCubePosition = new RecalculateCubePositionsService(repositories.cubeRepository) +const createKeyboard = new CreateKeyboardService(repositories.keyboardRepository) +const playSound = new PlaySoundService(domainServices.soundService, repositories.optionsRepository) +const stopSound = new StopSoundService(domainServices.soundService) +const pressKey = new PressKeyService(repositories.keyboardRepository, playSound) +const releaseKey = new ReleaseKeyService(repositories.keyboardRepository, stopSound) +const createTransaction = new CreateTransactionService( + repositories.keyboardRepository, + repositories.transactionRepository, + createCube, + pressKey, + releaseKey +) +const listTransactions = new ListTransactionsService(repositories.transactionRepository) +const getKeyboard = new GetKeyboardService(repositories.keyboardRepository) +const getOptions = new GetOptionsService(repositories.optionsRepository) +const setMuted = new SetMutedService(repositories.optionsRepository) +const setInstrument = new SetInstrumentService(repositories.optionsRepository) +const loadInstrument = new LoadInstrumentService(domainServices.soundService) + +export const services: Services = { + createTransaction, + getCubes, + createCube, + recalculateCubePosition, + createKeyboard, + pressKey, + releaseKey, + listTransactions, + getKeyboard, + getOptions, + setMuted, + setInstrument, + loadInstrument +} diff --git a/apps/web/src/presentation/common/presenter/app/AppPresenter.ts b/apps/web/src/presentation/common/presenter/app/AppPresenter.ts new file mode 100644 index 0000000..e061907 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/app/AppPresenter.ts @@ -0,0 +1,38 @@ +import { CreateTransactionService } from '../../../../application/services/CreateTransaction/CreateTransactionService' +import { Presenter } from '../../base/Presenter' +import { AppPresenterState } from './AppPresenterState' + +const initalState: AppPresenterState = { + navMenuVisible: false, +} + +export class AppPresenter extends Presenter { + private createTransactionService: CreateTransactionService + + constructor( + createTransactionService: CreateTransactionService + ) { + super(initalState) + this.createTransactionService = createTransactionService + } + + closeButtonClicked() { + this.changeState({ + navMenuVisible: false, + }) + } + + navButtonClicked() { + this.changeState({ + navMenuVisible: true, + }) + } + + async createTransaction(txType: string, address: string): Promise { + return this.createTransactionService.execute({ + txType, + address, + timestamp: Date.now() + }) + } +} diff --git a/apps/web/src/presentation/common/presenter/app/AppPresenterState.ts b/apps/web/src/presentation/common/presenter/app/AppPresenterState.ts new file mode 100644 index 0000000..5f65736 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/app/AppPresenterState.ts @@ -0,0 +1,3 @@ +export type AppPresenterState = { + navMenuVisible: boolean +} \ No newline at end of file diff --git a/apps/web/src/presentation/common/presenter/cubes/CubesPresenter.spec.ts b/apps/web/src/presentation/common/presenter/cubes/CubesPresenter.spec.ts new file mode 100644 index 0000000..98250d2 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/cubes/CubesPresenter.spec.ts @@ -0,0 +1,82 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { CreateCubeService } from '../../../../application/services/CreateCube/CreateCubeService' +import { CreateKeyboardService } from '../../../../application/services/CreateKeyboard/CreateKeyboardService' +import { GetCubesService } from '../../../../application/services/GetCubes/GetCubesService' +import { RecalculateCubePositionsService } from '../../../../application/services/RecalculateCubePositions/RecalculateCubePositionsService' +import { KeyboardRepository } from '../../../../domain/repositories/KeyboardRepository' +import { InMemoryCubeRepository } from '../../../../infrastructure/repositories/InMemoryCubeRepository' +import { InMemoryKeyboardRepository } from '../../../../infrastructure/repositories/InMemoryKeyboardRepository' +import { CubesPresenter } from './CubesPresenter' + +describe('src/presentation/common/CubesPresenter', () => { + let cubeRepository: InMemoryCubeRepository + let getCubesService: GetCubesService + let recalculateCubePositionsService: RecalculateCubePositionsService + let presenter: CubesPresenter + let keyboardRepository: KeyboardRepository + let createKeyboardService: CreateKeyboardService + let createCubeService: CreateCubeService + + + const options = { + timeToFirstBlock: 100, + maxCubeCreationInterval: 10, + tickInterval: 5, + cubeStep: 0.001 + } + + + beforeEach(async () => { + keyboardRepository = new InMemoryKeyboardRepository() + createKeyboardService = new CreateKeyboardService(keyboardRepository) + await createKeyboardService.execute({ + numberOfKeys: 88, + initialPitchClass: 'A', + initialOctave: 1 + }) + cubeRepository = new InMemoryCubeRepository() + getCubesService = new GetCubesService(cubeRepository) + recalculateCubePositionsService = new RecalculateCubePositionsService(cubeRepository) + createCubeService = new CreateCubeService(cubeRepository) + presenter = new CubesPresenter(getCubesService, recalculateCubePositionsService, options) + await createCubeService.execute({ color: 'blue', x: 0.5 }) + }) + + afterEach(() => { + presenter.stop() + }) + + it('should move cubes up every predetermined interval', async () => { + presenter.run() + await new Promise(resolve => setTimeout(resolve, options.timeToFirstBlock)) + const cubes = await cubeRepository.list() + expect(cubes[0].y.value).toBeGreaterThan(0) + }) + + it('should stop creating cubes when stopped', async () => { + presenter.run() + await new Promise(resolve => setTimeout(resolve, options.maxCubeCreationInterval)) + presenter.stop() + const cubes = await cubeRepository.list() + const cubesCount = cubes.length + await new Promise(resolve => setTimeout(resolve, options.maxCubeCreationInterval)) + expect(cubes.length).toBe(cubesCount) + }) + + it('should stop moving cubes when stopped', async () => { + presenter.run() + await new Promise(resolve => setTimeout(resolve, options.timeToFirstBlock)) + presenter.stop() + const cubes = await cubeRepository.list() + const firstCubeY = cubes[0].y.value + await new Promise(resolve => setTimeout(resolve, options.tickInterval)) + expect(cubes[0].y.value).toBe(firstCubeY) + }) + + it('should not create new runners if is already running', async () => { + presenter.run() + const tickLoop = presenter['tickLoop'] + presenter.run() + expect(presenter['tickLoop']).toBe(tickLoop) + }) +}) diff --git a/apps/web/src/presentation/common/presenter/cubes/CubesPresenter.ts b/apps/web/src/presentation/common/presenter/cubes/CubesPresenter.ts new file mode 100644 index 0000000..7b8f4b3 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/cubes/CubesPresenter.ts @@ -0,0 +1,90 @@ +import { GetCubesService } from '../../../../application/services/GetCubes/GetCubesService' +import { RecalculateCubePositionsService } from '../../../../application/services/RecalculateCubePositions/RecalculateCubePositionsService' +import { Presenter } from '../../base/Presenter' +import { CubesPresenterState } from './CubesPresenterState' + +const initalState: CubesPresenterState = { + cubes: [], +} + +interface CubesPresenterOptions { + maxCubeCreationInterval: number + tickInterval: number + maxCubeAge: number +} + +const defaultOptions: CubesPresenterOptions = { + maxCubeCreationInterval: 300, + tickInterval: 20, + maxCubeAge: 17_000 +} + +export class CubesPresenter extends Presenter { + + private readonly getCubesService: GetCubesService + private readonly recalculateCubePositions: RecalculateCubePositionsService + + + private options: CubesPresenterOptions + + private tickLoop: NodeJS.Timeout | undefined + + + constructor( + getCubesService: GetCubesService, + recalculateCubePositions: RecalculateCubePositionsService, + options?: Partial + ) { + super(initalState) + this.getCubesService = getCubesService + this.recalculateCubePositions = recalculateCubePositions + this.options = { + ...defaultOptions, + ...options + } + + this.run() + } + + async run() { + if (this.isRunning()) { + return + } + this.runTickLoop() + } + + stop() { + this.stopTickLoop() + } + + private isRunning() { + return !!this.tickLoop + } + + private stopTickLoop() { + if (this.tickLoop) { + clearInterval(this.tickLoop) + this.tickLoop = undefined + } + } + + private runTickLoop() { + this.tickLoop = setInterval(async () => { + await this.moveAllCubesUp() + await this.syncState() + }, this.options.tickInterval) + } + + private async moveAllCubesUp() { + await this.recalculateCubePositions.execute({ + maxAge: this.options.maxCubeAge + }) + } + + private async syncState() { + const getCubesResponse = await this.getCubesService.execute() + this.changeState({ + cubes: getCubesResponse.cubes + }) + } +} diff --git a/apps/web/src/presentation/common/presenter/cubes/CubesPresenterState.ts b/apps/web/src/presentation/common/presenter/cubes/CubesPresenterState.ts new file mode 100644 index 0000000..ce796cc --- /dev/null +++ b/apps/web/src/presentation/common/presenter/cubes/CubesPresenterState.ts @@ -0,0 +1,10 @@ + +export type CubesPresenterState = { + cubes: { + id: string + x: number + y: number + color: string + mirrored: boolean + }[] +} diff --git a/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenter.spec.ts b/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenter.spec.ts new file mode 100644 index 0000000..012acd7 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenter.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' +import { KeyboardPresenter } from './KeyboardPresenter' +import { InMemoryKeyboardRepository } from '../../../../infrastructure/repositories/InMemoryKeyboardRepository' +import { CreateKeyboardService } from '../../../../application/services/CreateKeyboard/CreateKeyboardService' +import { GetKeyboardService } from '../../../../application/services/GetKeyboard/GetKeyboardService' + +const keyboardRepository = new InMemoryKeyboardRepository() +const createKeyboardService = new CreateKeyboardService(keyboardRepository) +await createKeyboardService.execute({ + numberOfKeys: 88, + initialPitchClass: 'A', + initialOctave: 1 +}) +const getKeyboardService = new GetKeyboardService(keyboardRepository) +const keyboardPresenter = new KeyboardPresenter(createKeyboardService, getKeyboardService) + +describe('src/presentation/common/presenter/keyboard/KeyboardPresenter', () => { + it('should be defined', () => { + expect(keyboardPresenter).toBeDefined() + }) + + it('should have 88 keys', () => { + expect(keyboardPresenter.state.keys.length).toBe(88) + }) + +}) \ No newline at end of file diff --git a/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenter.ts b/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenter.ts new file mode 100644 index 0000000..9da55da --- /dev/null +++ b/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenter.ts @@ -0,0 +1,81 @@ +import { Event, Observable } from '@cryptochords/shared' +import { CreateKeyboardService } from '../../../../application/services/CreateKeyboard/CreateKeyboardService' +import { GetKeyboardService } from '../../../../application/services/GetKeyboard/GetKeyboardService' +import { Presenter } from '../../base/Presenter' +import { KeyboardPresenterState } from './KeyboardPresenterState' + +const initalState: KeyboardPresenterState = { + keys: [] +} + +interface KeyboardPresenterOptions { + numberOfKeys: number + initialPitchClass: string + initialOctave: number +} + +const defaultOptions: KeyboardPresenterOptions = { + numberOfKeys: 88, + initialPitchClass: 'A', + initialOctave: 0 +} + +export class KeyboardPresenter extends Presenter { + + private options: KeyboardPresenterOptions + private createKeyboardService: CreateKeyboardService + private getKeyboardService: GetKeyboardService + + constructor( + createKeyboardService: CreateKeyboardService, + getKeyboardService: GetKeyboardService, + keyboardChangesObserver?: Observable, + options?: Partial + ) { + super(initalState) + + this.createKeyboardService = createKeyboardService + this.getKeyboardService = getKeyboardService + + this.options = { + ...defaultOptions, + ...options + } + + this.createKeyboard() + this.refresh() + + if(keyboardChangesObserver) { + keyboardChangesObserver.listen(this.refresh.bind(this)) + } + } + + async refresh() { + const keyboard = await this.getKeyboardService.execute() + + if (!keyboard) { + return + } + + this.changeState({ + keys: keyboard.keys.map(key => ({ + pitch: { + class: key.pitch.class, + octave: key.pitch.octave + }, + keyShape: key.keyShape, + x: key.x, + color: key.pressed ? key.color : undefined + })) + }) + } + + private async createKeyboard() { + await this.createKeyboardService.execute({ + numberOfKeys: this.options.numberOfKeys, + initialPitchClass: this.options.initialPitchClass, + initialOctave: this.options.initialOctave + }) + } + +} diff --git a/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenterState.ts b/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenterState.ts new file mode 100644 index 0000000..696a4a9 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/keyboard/KeyboardPresenterState.ts @@ -0,0 +1,11 @@ +export type KeyboardPresenterState = { + keys: { + pitch: { + class: string + octave: number + } + keyShape: string + x: number + color?: string + }[] +} diff --git a/apps/web/src/presentation/common/presenter/options/OptionsPresenter.ts b/apps/web/src/presentation/common/presenter/options/OptionsPresenter.ts new file mode 100644 index 0000000..b91f01d --- /dev/null +++ b/apps/web/src/presentation/common/presenter/options/OptionsPresenter.ts @@ -0,0 +1,78 @@ +import { GetOptionsService } from '../../../../application/services/GetOptions/GetOptionsService' +import { LoadInstrumentService } from '../../../../application/services/LoadInstrument/LoadInstrumentService' +import { SetInstrumentService } from '../../../../application/services/SetInstrument/SetInstrumentService' +import { SetMutedService } from '../../../../application/services/SetMuted/SetMutedService' +import { InstrumentEnum, instrumentLabels } from '../../../../domain/enum/InstrumentEnum' +import { Presenter } from '../../base/Presenter' +import { OptionsPresenterState } from './OptionsPresenterState' + +const instruments: { label: string, value: string }[] = Object.values(InstrumentEnum).map( + (instrument) => ({ + label: instrumentLabels.get(instrument) ?? instrument, + value: instrument + }) +) + +const initalState: OptionsPresenterState = { + muted: true, + selectedInstrument: '', + instruments, + displayLoadingMessage: false, + displayInstrumentPicker: false +} + +export class OptionsPresenter extends Presenter { + + private getOptionsService: GetOptionsService + private setMutedService: SetMutedService + private setInstrumentService: SetInstrumentService + private loadInstrumentService: LoadInstrumentService + + constructor( + getOptionsService: GetOptionsService, + setMutedService: SetMutedService, + setInstrumentService: SetInstrumentService, + loadInstrumentService: LoadInstrumentService + ) { + super(initalState) + + this.getOptionsService = getOptionsService + this.setMutedService = setMutedService + this.setInstrumentService = setInstrumentService + this.loadInstrumentService = loadInstrumentService + + this.init() + } + + async init() { + const options = await this.getOptionsService.execute() + this.setState({ + muted: options.muted, + selectedInstrument: options.instrument, + instruments, + displayLoadingMessage: false, + displayInstrumentPicker: true + }) + } + + + async setMuted(muted: boolean) { + if (!muted) { + await this.loadInstrument(this.state.selectedInstrument) + } + await this.setMutedService.execute({ muted }) + this.changeState({ muted }) + } + + async setInstrument(instrument: string) { + await this.loadInstrument(instrument) + await this.setInstrumentService.execute({ instrument }) + this.changeState({ selectedInstrument: instrument }) + } + + private async loadInstrument(instrument: string) { + this.changeState({ displayLoadingMessage: true, displayInstrumentPicker: false }) + await this.loadInstrumentService.execute({ instrument }) + this.changeState({ displayLoadingMessage: false, displayInstrumentPicker: true }) + } +} diff --git a/apps/web/src/presentation/common/presenter/options/OptionsPresenterState.ts b/apps/web/src/presentation/common/presenter/options/OptionsPresenterState.ts new file mode 100644 index 0000000..4d529cb --- /dev/null +++ b/apps/web/src/presentation/common/presenter/options/OptionsPresenterState.ts @@ -0,0 +1,12 @@ +export type InstrumentState = { + value: string + label: string +} + +export type OptionsPresenterState = { + muted: boolean + selectedInstrument: string + instruments: InstrumentState[] + displayLoadingMessage: boolean + displayInstrumentPicker: boolean +} diff --git a/apps/web/src/presentation/common/presenter/transactions/TransactionsPresenter.ts b/apps/web/src/presentation/common/presenter/transactions/TransactionsPresenter.ts new file mode 100644 index 0000000..192d273 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/transactions/TransactionsPresenter.ts @@ -0,0 +1,87 @@ +import { Event, Observable, TxTypesEnum } from '@cryptochords/shared' +import { ListTransactionsService } from '../../../../application/services/ListTransactions/ListTransactionsService' +import { Presenter } from '../../base/Presenter' +import { TransactionsPresenterState } from './TransactionsPresenterState' + +const initalState: TransactionsPresenterState = { + transactions: [], +} + +const titleMap: Map = new Map([ + [TxTypesEnum.Block, 'New Block'], + [TxTypesEnum.Eth, 'ETH'], + [TxTypesEnum.Pop, 'PoP'], + [TxTypesEnum.Btc, 'BTC'] +]) + +const rgbMap: Map = new Map([ + [TxTypesEnum.Block, '#10FF2A'], + [TxTypesEnum.Eth, '#00D3FF'], + [TxTypesEnum.Pop, '#DC53FF'], + [TxTypesEnum.Btc, '#FFB200'] +]) + +const messageMap: Map = new Map([ + [TxTypesEnum.Block, 'created by'], + [TxTypesEnum.Eth, 'transaction by'], + [TxTypesEnum.Pop, 'transaction by'], + [TxTypesEnum.Btc, 'transaction by'] +]) + +const urlPatternMap: Map = new Map([ + [TxTypesEnum.Block, import.meta.env['VITE_EXPLORER_BLOCK_URL']], + [TxTypesEnum.Eth, import.meta.env['VITE_EXPLORER_ETH_URL']], + [TxTypesEnum.Pop, import.meta.env['VITE_EXPLORER_POP_URL']], + [TxTypesEnum.Btc, import.meta.env['VITE_EXPLORER_BTC_URL']] +]) + +export class TransactionsPresenter extends Presenter { + + private listTransactions: ListTransactionsService + + constructor(listTransactions: ListTransactionsService, transactionsChangeObserver?: Observable) { + super(initalState) + this.listTransactions = listTransactions + if(transactionsChangeObserver) { + transactionsChangeObserver.listen(this.refresh.bind(this)) + } + } + + async refresh() { + const response = await this.listTransactions.execute() + this.changeState({ + transactions: response.transactions.map(transaction => ({ + type: titleMap.get(transaction.txType as TxTypesEnum) ?? 'Unknown', + color: rgbMap.get(transaction.txType as TxTypesEnum) ?? '#fff', + message: messageMap.get(transaction.txType as TxTypesEnum) ?? ' ', + id: transaction.address, + at: this.formatDate(transaction.timestamp), + url: this.buildTransactionUrl(transaction.txType as TxTypesEnum, transaction.address) + })) + }) + } + + private formatDate(date: number) { + const formattedDate = new Date(date).toLocaleString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + hour12: true + }) + + return formattedDate.replace(/,/g, '') + } + + private buildTransactionUrl(txType: TxTypesEnum, address: string) { + const pattern = urlPatternMap.get(txType) + if(pattern) { + // replaces ${hash} with the address ignoring spaces and tabs within the brackets + const regex = /\$\{[ \t]*hash[ \t]*\}/i + return pattern.replace(regex, address) + } + return `#${address}` + } +} diff --git a/apps/web/src/presentation/common/presenter/transactions/TransactionsPresenterState.ts b/apps/web/src/presentation/common/presenter/transactions/TransactionsPresenterState.ts new file mode 100644 index 0000000..b07f631 --- /dev/null +++ b/apps/web/src/presentation/common/presenter/transactions/TransactionsPresenterState.ts @@ -0,0 +1,11 @@ + +export type TransactionsPresenterState = { + transactions: { + type: string + color: string + message: string + id: string + at: string + url: string + }[] +} diff --git a/apps/web/src/presentation/react/App.tsx b/apps/web/src/presentation/react/App.tsx new file mode 100644 index 0000000..9fb56ef --- /dev/null +++ b/apps/web/src/presentation/react/App.tsx @@ -0,0 +1,26 @@ +import { useContext } from 'react' +import { AppPresenter } from '../common/presenter/app/AppPresenter' +import { AppPresenterState } from '../common/presenter/app/AppPresenterState' +import { Header } from './components/Header' +import { MainContent } from './components/MainContent' +import { NavMenu } from './components/NavMenu' +import { presenters } from './context' +import { usePresenter } from './hooks/usePresenter' + +function App() { + const { appPresenter } = useContext(presenters) + + const { + navMenuVisible, + } = usePresenter(appPresenter) + + return ( + <> +
appPresenter.navButtonClicked()} /> + appPresenter.closeButtonClicked()} className={`${navMenuVisible ? '' : 'hidden'} md:hidden`} /> + + + ) +} + +export default App diff --git a/apps/web/src/presentation/react/components/Card.tsx b/apps/web/src/presentation/react/components/Card.tsx new file mode 100644 index 0000000..0cdded2 --- /dev/null +++ b/apps/web/src/presentation/react/components/Card.tsx @@ -0,0 +1,10 @@ +export const Card = function (props: { + className?: string + children?: React.ReactNode +}) { + return ( +
+ {props.children} +
+ ) +} diff --git a/apps/web/src/presentation/react/components/Checkbox.tsx b/apps/web/src/presentation/react/components/Checkbox.tsx new file mode 100644 index 0000000..652fb9f --- /dev/null +++ b/apps/web/src/presentation/react/components/Checkbox.tsx @@ -0,0 +1,45 @@ +export const Checkbox = function (props: { + value: boolean + className?: string + onClick?: () => void +}) { + return ( + <> + {}}/> +
+ + ) +} diff --git a/apps/web/src/presentation/react/components/Cube.tsx b/apps/web/src/presentation/react/components/Cube.tsx new file mode 100644 index 0000000..d0ebf91 --- /dev/null +++ b/apps/web/src/presentation/react/components/Cube.tsx @@ -0,0 +1,21 @@ +export const Cube = function (props: { + className?: string + style?: React.CSSProperties + centerPositioning?: boolean + color: string + mirrored?: boolean +}) { + return ( + + ) +} diff --git a/apps/web/src/presentation/react/components/Cubes.tsx b/apps/web/src/presentation/react/components/Cubes.tsx new file mode 100644 index 0000000..878ba00 --- /dev/null +++ b/apps/web/src/presentation/react/components/Cubes.tsx @@ -0,0 +1,36 @@ +import { useContext } from 'react' +import { CubesPresenter } from '../../common/presenter/cubes/CubesPresenter' +import { CubesPresenterState } from '../../common/presenter/cubes/CubesPresenterState' +import { presenters } from '../context' +import { usePresenter } from '../hooks/usePresenter' +import { Cube } from './Cube' + +export const Cubes = function (props: { + className?: string + centerPositioning?: boolean + yMultiplier?: number + bottomOffset?: number +}) { + const { cubesPresenter } = useContext(presenters) + const { cubes } = usePresenter(cubesPresenter) + + return ( +
+ {cubes + .map((cube) => { + return ( + + ) + })} +
+ ) +} diff --git a/apps/web/src/presentation/react/components/Header.tsx b/apps/web/src/presentation/react/components/Header.tsx new file mode 100644 index 0000000..05c9c43 --- /dev/null +++ b/apps/web/src/presentation/react/components/Header.tsx @@ -0,0 +1,28 @@ +import { JoinCommunityButton } from './JoinCommunityButton' +import { Logo } from './Logo' +import { NavButton } from './NavButton' +import { NavItems } from './NavItems' + +export const Header = (props: { + className?: string + onNavButtonClick?: () => void +}) => { + return ( +
+ + + + +
+ ) +} \ No newline at end of file diff --git a/apps/web/src/presentation/react/components/JoinCommunityButton.tsx b/apps/web/src/presentation/react/components/JoinCommunityButton.tsx new file mode 100644 index 0000000..f93ab02 --- /dev/null +++ b/apps/web/src/presentation/react/components/JoinCommunityButton.tsx @@ -0,0 +1,24 @@ +export const JoinCommunityButton = (props: { + className?: string +}) => { + return ( + Join Community + ) +} diff --git a/apps/web/src/presentation/react/components/Key.tsx b/apps/web/src/presentation/react/components/Key.tsx new file mode 100644 index 0000000..b1d0329 --- /dev/null +++ b/apps/web/src/presentation/react/components/Key.tsx @@ -0,0 +1,28 @@ +export interface KeyProps { + keyShape: string + x: number + index: number + color?: string +} +export const Key = function ({ keyShape, x, index, color}: KeyProps) { + const isBlack = keyShape === 'black' + const zIndex = isBlack ? 1 : 0 + const left = `${x * 100}%` + const top = 0 + const width = isBlack ? '5.4vw' : '8.4vw' + + return ( + + ) +} diff --git a/apps/web/src/presentation/react/components/Keyboard.tsx b/apps/web/src/presentation/react/components/Keyboard.tsx new file mode 100644 index 0000000..d0fe8c2 --- /dev/null +++ b/apps/web/src/presentation/react/components/Keyboard.tsx @@ -0,0 +1,29 @@ +import { useContext } from 'react' +import { KeyboardPresenter } from '../../common/presenter/keyboard/KeyboardPresenter' +import { KeyboardPresenterState } from '../../common/presenter/keyboard/KeyboardPresenterState' +import { presenters } from '../context' +import { usePresenter } from '../hooks/usePresenter' +import { Key } from './Key' +import keyboard from '/image/keyboard/base.svg' + +export const Keyboard = function (props: { + className?: string +}) { + + const { keyboardPresenter } = useContext(presenters) + const { keys } = usePresenter(keyboardPresenter) + + return ( +
+ +
+ { + keys.map( + ({ keyShape, x, color, pitch }, index) => + + ) + } +
+
+ ) +} diff --git a/apps/web/src/presentation/react/components/Logo.tsx b/apps/web/src/presentation/react/components/Logo.tsx new file mode 100644 index 0000000..0594daf --- /dev/null +++ b/apps/web/src/presentation/react/components/Logo.tsx @@ -0,0 +1,16 @@ +import logo from '/image/crypto-chords.svg' + +export const Logo = function () { + const url = import.meta.env.VITE_LOGO_URL + return ( + + Crypto Chords + + ) +} diff --git a/apps/web/src/presentation/react/components/MainContent.tsx b/apps/web/src/presentation/react/components/MainContent.tsx new file mode 100644 index 0000000..deb5669 --- /dev/null +++ b/apps/web/src/presentation/react/components/MainContent.tsx @@ -0,0 +1,39 @@ +import { Card } from './Card' +import { Cubes } from './Cubes' +import { Keyboard } from './Keyboard' +import { Social } from './Social' +import { Options } from './Options' +import { Transactions } from './Transactions' +import desktopBackground from '/image/background/desktop.svg' +import mobileBackground from '/image/background/mobile.svg' + +// This constant is used to multiply the y position of the cubes +// to allow the cubes extrapolate the top of the component +const CUBES_Y_MULTIPLIER = 2 +// This constant is used to offset the cubes from the bottom of the component +// to hide the creation of the cubes avoiding a visual glitch +const CUBES_BOTTOM_OFFSET = 0.15 + +export function MainContent(props: { + className: string +}) { + return ( +
+ + +
+ + +
+ + + + + + +
+ +
+
+ ) +} diff --git a/apps/web/src/presentation/react/components/NavButton.tsx b/apps/web/src/presentation/react/components/NavButton.tsx new file mode 100644 index 0000000..bc1c067 --- /dev/null +++ b/apps/web/src/presentation/react/components/NavButton.tsx @@ -0,0 +1,9 @@ +import navButton from '/image/nav-button.svg' +export const NavButton = function (props: { + className?: string + onClick?: () => void +}) { + return ( + + ) +} diff --git a/apps/web/src/presentation/react/components/NavItem.tsx b/apps/web/src/presentation/react/components/NavItem.tsx new file mode 100644 index 0000000..a744532 --- /dev/null +++ b/apps/web/src/presentation/react/components/NavItem.tsx @@ -0,0 +1,13 @@ +export const NavItem = function (options: { + href: string + children: React.ReactNode +}) { + return ( + + {options.children} + + ) +} diff --git a/apps/web/src/presentation/react/components/NavItems.tsx b/apps/web/src/presentation/react/components/NavItems.tsx new file mode 100644 index 0000000..724f9be --- /dev/null +++ b/apps/web/src/presentation/react/components/NavItems.tsx @@ -0,0 +1,13 @@ +import { NavItem } from './NavItem' + +export const NavItems = function (props: { + className?: string +}) { + return ( + + ) +} diff --git a/apps/web/src/presentation/react/components/NavMenu.tsx b/apps/web/src/presentation/react/components/NavMenu.tsx new file mode 100644 index 0000000..71fdfe9 --- /dev/null +++ b/apps/web/src/presentation/react/components/NavMenu.tsx @@ -0,0 +1,33 @@ +import { JoinCommunityButton } from './JoinCommunityButton' +import { Logo } from './Logo' +import { NavItems } from './NavItems' +import { Social } from './Social' +import closeButton from '/image/close-button.svg' + +export const NavMenu = function (props: { + className?: string + onCloseButtonClick?: () => void +}) { + return ( +
+
+ + +
+ +
+ +
+ +
+ ) +} diff --git a/apps/web/src/presentation/react/components/Options.tsx b/apps/web/src/presentation/react/components/Options.tsx new file mode 100644 index 0000000..2a02e7a --- /dev/null +++ b/apps/web/src/presentation/react/components/Options.tsx @@ -0,0 +1,47 @@ +import { useContext, useEffect } from 'react' +import { usePresenter } from '../hooks/usePresenter' +import { presenters } from '../context' +import { OptionsPresenter } from '../../common/presenter/options/OptionsPresenter' +import { OptionsPresenterState } from '../../common/presenter/options/OptionsPresenterState' +import { Checkbox } from './Checkbox' +import { Select } from './Select' + +export const Options = function (props: { + className?: string +}) { + const { optionsPresenter } = useContext(presenters) + const { muted, instruments, selectedInstrument, displayLoadingMessage, displayInstrumentPicker } = usePresenter(optionsPresenter) + + /** + * Since the browser will not allow sound to be played without user interaction, + * we will mute the sound by default. + */ + useEffect(() => { + optionsPresenter.setMuted(true) + }, [optionsPresenter]) + + return ( +
+ Sound + optionsPresenter.setMuted(!muted)} /> + { + displayLoadingMessage && Loading instrument sounds... + } + { + displayInstrumentPicker &&