diff --git a/.gitignore b/.gitignore index 5d778b370c..e6d43a8c94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .vscode testing-project .mypy_cache -poetry.lock +# poetry.lock dev-link/ diff --git a/README.md b/README.md index d85e943834..709ad226cf 100644 --- a/README.md +++ b/README.md @@ -1,269 +1,172 @@ -# Full Stack FastAPI and PostgreSQL - Base Project Generator - -[![Build Status](https://travis-ci.com/tiangolo/full-stack-fastapi-postgresql.svg?branch=master)](https://travis-ci.com/tiangolo/full-stack-fastapi-postgresql) - -Generate a backend and frontend stack using Python, including interactive API documentation. - -### Interactive API documentation - -[![API docs](img/docs.png)](https://github.com/tiangolo/full-stack-fastapi-postgresql) - -### Alternative API documentation - -[![API docs](img/redoc.png)](https://github.com/tiangolo/full-stack-fastapi-postgresql) +# Full Stack FastAPI, PostgreSQL, Neo4j & Nuxt 3 Base Project Generator + +[![Build Status](https://app.travis-ci.com/whythawk/full-stack-fastapi-postgresql.svg?branch=master)](https://app.travis-ci.com/whythawk/full-stack-fastapi-postgresql) + +Accelerate your next web development project with this FastAPI/NuxtJS base project generator. + +This project is for developers looking to build and maintain full-feature progressive web applications using Python on the backend / Typescript on the frontend, and want the complex-but-routine aspects of auth 'n auth, and component and deployment configuration, taken care of, including interactive API documentation. + +This project is a fork of [Sebastián Ramírez's](https://github.com/tiangolo) [Full Stack FastAPI and PostgreSQL Base Project Generator](https://github.com/tiangolo/full-stack-fastapi-postgresql). FastAPI is updated to version 0.109 (April 2024), SQLAlchemy to version 2.0.29 (March 2024), and the frontend to Nuxt 3.11 (April 2024). + +- [Key features](#key-features) +- [Screenshots](#screenshots) +- [How to use it](#how-to-use-it) + - [Getting started](./docs/getting-started.md) + - [Development and installation](./docs/development-guide.md) + - [Deployment for production](./docs/deployment-guide.md) + - [Authentication and magic tokens](./docs/authentication-guide.md) + - [Websockets for interactive communication](./docs/websocket-guide.md) +- [Fork differences](#fork-differences) +- [More details](#more-details) +- [Help needed](#help-needed) +- [Release notes](#release-notes) +- [License](#license) + +## Key features + +This FastAPI, PostgreSQL, Neo4j & Nuxt 3 repo will generate a complete web application stack as a foundation for your project development. + +- **Docker Compose** integration and optimization for local development. +- **Authentication** user management schemas, models, crud and apis already built, with OAuth2 JWT token support & default hashing. Offers _magic link_ authentication, with password fallback, with cookie management, including `access` and `refresh` tokens. +- [**FastAPI**](https://github.com/tiangolo/fastapi) backend with [Inboard](https://inboard.bws.bio/) one-repo Docker images, using Python 3.11: + - **SQLAlchemy** version 2.0 support for models. + - **Pydantic** version 2.7 for schemas. + - **Metadata Schema** based on [Dublin Core](https://www.dublincore.org/specifications/dublin-core/dcmi-terms/#section-3) for inheritance. + - **Common CRUD** support via generic inheritance. + - **Standards-based**: Based on (and fully compatible with) the open standards for APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) and [JSON Schema](http://json-schema.org/). + - **MJML** templates for common email transactions. + - [**Many other features**]("https://fastapi.tiangolo.com/features/"): including automatic validation, serialization, interactive documentation, etc. +- [**Nuxt/Vue 3**](https://nuxt.com/) frontend using TypeScript: + - **Authorisation** via middleware for page access, including logged in or superuser. + - **Model blog** project, with [Nuxt Content](https://content.nuxtjs.org/) for writing Markdown pages. + - **Form validation** with [Vee-Validate 4](https://vee-validate.logaretm.com/v4/). + - **State management** with [Pinia](https://pinia.vuejs.org/), and persistance with [Pinia PersistedState](https://prazdevs.github.io/pinia-plugin-persistedstate/). + - **CSS and templates** with [TailwindCSS](https://tailwindcss.com/), [HeroIcons](https://heroicons.com/), and [HeadlessUI](https://headlessui.com/). +- **PostgreSQL** database. +- **PGAdmin** for PostgreSQL database management. +- **Celery** worker that can import and use models and code from the rest of the backend selectively. +- **Flower** for Celery jobs monitoring. +- **Neo4j** graph database, including integration into the FastAPI base project. +- Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers. +- Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation. + +## Screenshots + +### App landing page + +![Landing page](img/landing.png) ### Dashboard Login -[![API docs](img/login.png)](https://github.com/tiangolo/full-stack-fastapi-postgresql) - -### Dashboard - Create User - -[![API docs](img/dashboard.png)](https://github.com/tiangolo/full-stack-fastapi-postgresql) - -## Features - -* Full **Docker** integration (Docker based). -* Docker Swarm Mode deployment. -* **Docker Compose** integration and optimization for local development. -* **Production ready** Python web server using Uvicorn and Gunicorn. -* Python **FastAPI** backend: - * **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). - * **Intuitive**: Great editor support. Completion everywhere. Less time debugging. - * **Easy**: Designed to be easy to use and learn. Less time reading docs. - * **Short**: Minimize code duplication. Multiple features from each parameter declaration. - * **Robust**: Get production-ready code. With automatic interactive documentation. - * **Standards-based**: Based on (and fully compatible with) the open standards for APIs: OpenAPI and JSON Schema. - * **Many other features** including automatic validation, serialization, interactive documentation, authentication with OAuth2 JWT tokens, etc. -* **Secure password** hashing by default. -* **JWT token** authentication. -* **SQLAlchemy** models (independent of Flask extensions, so they can be used with Celery workers directly). -* Basic starting models for users (modify and remove as you need). -* **Alembic** migrations. -* **CORS** (Cross Origin Resource Sharing). -* **Celery** worker that can import and use models and code from the rest of the backend selectively. -* REST backend tests based on **Pytest**, integrated with Docker, so you can test the full API interaction, independent on the database. As it runs in Docker, it can build a new data store from scratch each time (so you can use ElasticSearch, MongoDB, CouchDB, or whatever you want, and just test that the API works). -* Easy Python integration with **Jupyter Kernels** for remote or in-Docker development with extensions like Atom Hydrogen or Visual Studio Code Jupyter. -* **Vue** frontend: - * Generated with Vue CLI. - * **JWT Authentication** handling. - * Login view. - * After login, main dashboard view. - * Main dashboard with user creation and edition. - * Self user edition. - * **Vuex**. - * **Vue-router**. - * **Vuetify** for beautiful material design components. - * **TypeScript**. - * Docker server based on **Nginx** (configured to play nicely with Vue-router). - * Docker multi-stage building, so you don't need to save or commit compiled code. - * Frontend tests ran at build time (can be disabled too). - * Made as modular as possible, so it works out of the box, but you can re-generate with Vue CLI or create it as you need, and re-use what you want. - * It's also easy to remove it if you have an API-only app, check the instructions in the generated `README.md`. -* **PGAdmin** for PostgreSQL database, you can modify it to use PHPMyAdmin and MySQL easily. -* **Flower** for Celery jobs monitoring. -* Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers. -* Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation. -* GitLab **CI** (continuous integration), including frontend and backend testing. - -## How to use it - -Go to the directory where you want to create your project and run: - -```bash -pip install cookiecutter -cookiecutter https://github.com/tiangolo/full-stack-fastapi-postgresql -``` - -### Generate passwords +![Magic-link login](img/login.png) -You will be asked to provide passwords and secret keys for several components. Open another terminal and run: +### Dashboard User Management -```bash -openssl rand -hex 32 -# Outputs something like: 99d3b1f01aa639e4a76f4fc281fc834747a543720ba4c8a8648ba755aef9be7f -``` +![Moderator user management](img/dashboard.png) -Copy the contents and use that as password / secret key. And run that again to generate another secure key. - - -### Input variables +### Interactive API documentation -The generator (cookiecutter) will ask you for some data, you might want to have at hand before generating the project. +![Interactive API docs](img/redoc.png) -The input variables, with their default values (some auto generated) are: +### Enabling two-factor security (TOTP) -* `project_name`: The name of the project -* `project_slug`: The development friendly name of the project. By default, based on the project name -* `domain_main`: The domain in where to deploy the project for production (from the branch `production`), used by the load balancer, backend, etc. By default, based on the project slug. -* `domain_staging`: The domain in where to deploy while staging (before production) (from the branch `master`). By default, based on the main domain. +![Enabling TOTP](img/totp.png) -* `docker_swarm_stack_name_main`: The name of the stack while deploying to Docker in Swarm mode for production. By default, based on the domain. -* `docker_swarm_stack_name_staging`: The name of the stack while deploying to Docker in Swarm mode for staging. By default, based on the domain. +## How to use it -* `secret_key`: Backend server secret key. Use the method above to generate it. -* `first_superuser`: The first superuser generated, with it you will be able to create more users, etc. By default, based on the domain. -* `first_superuser_password`: First superuser password. Use the method above to generate it. -* `backend_cors_origins`: Origins (domains, more or less) that are enabled for CORS (Cross Origin Resource Sharing). This allows a frontend in one domain (e.g. `https://dashboard.example.com`) to communicate with this backend, that could be living in another domain (e.g. `https://api.example.com`). It can also be used to allow your local frontend (with a custom `hosts` domain mapping, as described in the project's `README.md`) that could be living in `http://dev.example.com:8080` to communicate with the backend at `https://stag.example.com`. Notice the `http` vs `https` and the `dev.` prefix for local development vs the "staging" `stag.` prefix. By default, it includes origins for production, staging and development, with ports commonly used during local development by several popular frontend frameworks (Vue with `:8080`, React, Angular). -* `smtp_port`: Port to use to send emails via SMTP. By default `587`. -* `smtp_host`: Host to use to send emails, it would be given by your email provider, like Mailgun, Sparkpost, etc. -* `smtp_user`: The user to use in the SMTP connection. The value will be given by your email provider. -* `smtp_password`: The password to be used in the SMTP connection. The value will be given by the email provider. -* `smtp_emails_from_email`: The email account to use as the sender in the notification emails, it would be something like `info@your-custom-domain.com`. - -* `postgres_password`: Postgres database password. Use the method above to generate it. (You could easily modify it to use MySQL, MariaDB, etc). -* `pgadmin_default_user`: PGAdmin default user, to log-in to the PGAdmin interface. -* `pgadmin_default_user_password`: PGAdmin default user password. Generate it with the method above. - -* `traefik_constraint_tag`: The tag to be used by the internal Traefik load balancer (for example, to divide requests between backend and frontend) for production. Used to separate this stack from any other stack you might have. This should identify each stack in each environment (production, staging, etc). -* `traefik_constraint_tag_staging`: The Traefik tag to be used while on staging. -* `traefik_public_constraint_tag`: The tag that should be used by stack services that should communicate with the public. +- [Getting started](./docs/getting-started.md) +- [Development and installation](./docs/development-guide.md) +- [Deployment for production](./docs/deployment-guide.md) +- [Authentication and magic tokens](./docs/authentication-guide.md) +- [Websockets for interactive communication](./docs/websocket-guide.md) -* `flower_auth`: Basic HTTP authentication for flower, in the form`user:password`. By default: "`admin:changethis`". +## Fork differences -* `sentry_dsn`: Key URL (DSN) of Sentry, for live error reporting. You can use the open source version or a free account. E.g.: `https://1234abcd:5678ef@sentry.example.com/30`. +The original objective of this fork was to maintain parity with the [Full Stack FastAPI and PostgreSQL Base Project Generator](https://github.com/tiangolo/full-stack-fastapi-postgresql) but update it to bring it up to current stack versions, fixes, and with a complete auth 'n auth system. -* `docker_image_prefix`: Prefix to use for Docker image names. If you are using GitLab Docker registry it would be based on your code repository. E.g.: `git.example.com/development-team/my-awesome-project/`. -* `docker_image_backend`: Docker image name for the backend. By default, it will be based on your Docker image prefix, e.g.: `git.example.com/development-team/my-awesome-project/backend`. And depending on your environment, a different tag will be appended ( `prod`, `stag`, `branch` ). So, the final image names used will be like: `git.example.com/development-team/my-awesome-project/backend:prod`. -* `docker_image_celeryworker`: Docker image for the celery worker. By default, based on your Docker image prefix. -* `docker_image_frontend`: Docker image for the frontend. By default, based on your Docker image prefix. +With the most recent updates to the base stack, Sebastián has made some fairly dramatic changes and these two stacks are no longer compatible. This table presents a summary of the major differences: -## How to deploy +| This base stack | Tiangolo base stack | +| :------------------------------- | :---------------------- | +| SQLAlchemy & Pydantic | SqlModel | +| Postgresql 15 & PGAdmin | Postgresql 12 & Adminer | +| Celery & RabbitMQ task queue | - | +| NuxtJS frontend | React frontend | -This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it is designed to be used in a cluster controlled with pure Docker in Swarm Mode with a Traefik main load balancer proxy handling automatic HTTPS certificates, using the ideas from DockerSwarm.rocks. +I use this stack to produce some fairly complex web-based applications and I need to get to the full APIs for SQLAlchemy and Pydantic, and SqlModel doesn't offer me that. I also need to run distributed asyncronous tasks, so Celery is important. Finally, I prefer Nuxt. -Please refer to DockerSwarm.rocks to see how to deploy such a cluster in 20 minutes. +This stack also has a much more sophisticated and feature-complete auth 'n auth system which is a requirement for any web app. ## More details After using this generator, your new project (the directory created) will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](./{{cookiecutter.project_slug}}/README.md). -## Sibling project generators +This current release (May 2024) is for FastAPI version 0.109 introduces support for Pydantic 2.7. Since this is intended as a base stack on which you will build complex applications, there is no intention of backwards compatability between releases, and the objective is to ensure that each release has the latest long-term-support versions of the core libraries so that you can rely on your application core for as long as possible. -* Full Stack FastAPI Couchbase: [https://github.com/tiangolo/full-stack-fastapi-couchbase](https://github.com/tiangolo/full-stack-fastapi-couchbase). +To align with [Inboard](https://inboard.bws.bio/), Poetry has been deprecated in favour of [Hatch](https://hatch.pypa.io/latest/). -## Release Notes - -### Latest Changes - -* Update issue-manager. PR [#211](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/211). -* Add [GitHub Sponsors](https://github.com/sponsors/tiangolo) button. PR [#201](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/201). -* Add consistent errors for env vars not set. PR [#200](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/200). -* Upgrade Traefik to version 2, keeping in sync with DockerSwarm.rocks. PR [#199](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/199). -* Add docs about reporting test coverage in HTML. PR [#161](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/161). -* Run tests with `TestClient`. PR [#160](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/160). -* Refactor backend: - * Simplify configs for tools and format to better support editor integration. - * Add mypy configurations and plugins. - * Add types to all the codebase. - * Update types for SQLAlchemy models with plugin. - * Update and refactor CRUD utils. - * Refactor DB sessions to use dependencies with `yield`. - * Refactor dependencies, security, CRUD, models, schemas, etc. To simplify code and improve autocompletion. - * Change from PyJWT to Python-JOSE as it supports additional use cases. - * Fix JWT tokens using user email/ID as the subject in `sub`. - * PR [#158](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/158). -* Add docs about removing the frontend, for an API-only app. PR [#156](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/156). -* Simplify scripts and development, update docs and configs. PR [#155](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/155). -* Simplify `docker-compose.*.yml` files, refactor deployment to reduce config files. PR [#153](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/153). -* Simplify env var files, merge to a single `.env` file. PR [#151](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/151). - -### 0.5.0 - -* Make the Traefik public network a fixed default of `traefik-public` as done in DockerSwarm.rocks, to simplify development and iteration of the project generator. PR [#150](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/150). -* Update to PostgreSQL 12. PR [#148](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/148). by [@RCheese](https://github.com/RCheese). -* Use Poetry for package management. Initial PR [#144](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/144) by [@RCheese](https://github.com/RCheese). -* Fix Windows line endings for shell scripts after project generation with Cookiecutter hooks. PR [#149](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/149). -* Upgrade Vue CLI to version 4. PR [#120](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/120) by [@br3ndonland](https://github.com/br3ndonland). -* Remove duplicate `login` tag. PR [#135](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/135) by [@Nonameentered](https://github.com/Nonameentered). -* Fix showing email in dashboard when there's no user's full name. PR [#129](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/129) by [@rlonka](https://github.com/rlonka). -* Format code with Black and Flake8. PR [#121](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/121) by [@br3ndonland](https://github.com/br3ndonland). -* Simplify SQLAlchemy Base class. PR [#117](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/117) by [@airibarne](https://github.com/airibarne). -* Update CRUD utils for users, handling password hashing. PR [#106](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/106) by [@mocsar](https://github.com/mocsar). -* Use `.` instead of `source` for interoperability. PR [#98](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/98) by [@gucharbon](https://github.com/gucharbon). -* Use Pydantic's `BaseSettings` for settings/configs and env vars. PR [#87](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/87) by [@StephenBrown2](https://github.com/StephenBrown2). -* Remove `package-lock.json` to let everyone lock their own versions (depending on OS, etc). -* Simplify Traefik service labels PR [#139](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/139). -* Add email validation. PR [#40](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/40) by [@kedod](https://github.com/kedod). -* Fix typo in README. PR [#83](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/83) by [@ashears](https://github.com/ashears). -* Fix typo in README. PR [#80](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/80) by [@abjoker](https://github.com/abjoker). -* Fix function name `read_item` and response code. PR [#74](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/74) by [@jcaguirre89](https://github.com/jcaguirre89). -* Fix typo in comment. PR [#70](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/70) by [@daniel-butler](https://github.com/daniel-butler). -* Fix Flower Docker configuration. PR [#37](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/37) by [@dmontagu](https://github.com/dmontagu). -* Add new CRUD utils based on DB and Pydantic models. Initial PR [#23](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/23) by [@ebreton](https://github.com/ebreton). -* Add normal user testing Pytest fixture. PR [#20](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/20) by [@ebreton](https://github.com/ebreton). - -### 0.4.0 - -* Fix security on resetting a password. Receive token as body, not query. PR [#34](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/34). - -* Fix security on resetting a password. Receive it as body, not query. PR [#33](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/33) by [@dmontagu](https://github.com/dmontagu). +You will also find an initial implementation of internationalisation using [@nuxt/i18n](https://nuxt.com/modules/i18n). The [Vite PWA plugin](https://vite-pwa-org.netlify.app/frameworks/nuxt.html) is also included, along with a Node CLI for generating all necessary app icons. You will see links and notes to this in the [nuxt.config.ts](./{{cookiecutter.project_slug}}/frontend/nuxt.config.ts) file. -* Fix SQLAlchemy class lookup on initialization. PR [#29](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/29) by [@ebreton](https://github.com/ebreton). +## Help needed -* Fix SQLAlchemy operation errors on database restart. PR [#32](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/32) by [@ebreton](https://github.com/ebreton). +The tests are broken and it would be great if someone could take that on. Other potential roadmap items: -* Fix locations of scripts in generated README. PR [#19](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/19) by [@ebreton](https://github.com/ebreton). +- Translation: docs are all in English and it would be great if those could be in other languages. +- Internationalisation: [nuxt/i18n](https://v8.i18n.nuxtjs.org/) is added, but the sample pages are not all translated. +- Code review and optimisation: both the front- and backend stacks have seen some big generational changes, so would be good to have more eyes on the updates to this stack. -* Forward arguments from script to `pytest` inside container. PR [#17](https://github.com/tiangolo/full-stack-fastapi-postgresql/pull/17) by [@ebreton](https://github.com/ebreton). - -* Update development scripts. - -* Read Alembic configs from env vars. PR #9 by @ebreton. - -* Create DB Item objects from all Pydantic model's fields. - -* Update Jupyter Lab installation and util script/environment variable for local development. - -### 0.3.0 - -* PR #14: - * Update CRUD utils to use types better. - * Simplify Pydantic model names, from `UserInCreate` to `UserCreate`, etc. - * Upgrade packages. - * Add new generic "Items" models, crud utils, endpoints, and tests. To facilitate re-using them to create new functionality. As they are simple and generic (not like Users), it's easier to copy-paste and adapt them to each use case. - * Update endpoints/*path operations* to simplify code and use new utilities, prefix and tags in `include_router`. - * Update testing utils. - * Update linting rules, relax vulture to reduce false positives. - * Update migrations to include new Items. - * Update project README.md with tips about how to start with backend. - -* Upgrade Python to 3.7 as Celery is now compatible too. PR #10 by @ebreton. +## Release Notes -### 0.2.2 +See notes and [releases](https://github.com/whythawk/full-stack-fastapi-postgresql/releases). -* Fix frontend hijacking /docs in development. Using latest https://github.com/tiangolo/node-frontend with custom Nginx configs in frontend. PR #6. +## 0.9.0 -### 0.2.1 +Updates to `backend`: +- FastAPI 0.99 -> 0.109 (Inboard 0.51 -> 0.68) +- Pydantic 1.10 -> 2.7.1 -* Fix documentation for *path operation* to get user by ID. PR #4 by @mpclarkson in FastAPI. +Updates to `frontend`: +- NuxtJS 3.6.5 -> 3.11.2 +- Nuxtjs i18n 8.0.0 RC -> 8.3.1 -* Set `/start-reload.sh` as a command override for development by default. +The Pydantic change is dramatic, so please revise their [migration guide](https://docs.pydantic.dev/2.7/migration/). Similarly, [nuxt/i18n](https://i18n.nuxtjs.org/docs/getting-started) has some major quality of life improvements. -* Update generated README. +## 0.8.2 -### 0.2.0 +Fixing [#39](https://github.com/whythawk/full-stack-fastapi-postgresql/issues/39), thanks to @a-vorobyoff: -**PR #2**: +- Exposing port 24678 for Vite on frontend in development mode. +- Ensuring Nuxt content on /api/_content doesn't interfere with backend /api/v routes. +- Checking for password before hashing on user creation. +- Updating generated README for Hatch (after Poetry deprecation). +- Minor fixes. -* Simplify and update backend `Dockerfile`s. -* Refactor and simplify backend code, improve naming, imports, modules and "namespaces". -* Improve and simplify Vuex integration with TypeScript accessors. -* Standardize frontend components layout, buttons order, etc. -* Add local development scripts (to develop this project generator itself). -* Add logs to startup modules to detect errors early. -* Improve FastAPI dependency utilities, to simplify and reduce code (to require a superuser). +### 0.8.1 -### 0.1.2 +- Minor updates to Docker scripts for `build`. -* Fix path operation to update self-user, set parameters as body payload. +### 0.8.0 -### 0.1.1 +- Updates to `frontend`, [#37](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/37) by @turukawa: + - `@nuxtjs/i18n` for internationalisation, along with language selection component. + - `@vite-pwa/nuxt` along with button components for install and refreshing the app and service workers, and a CLI icon generator. + - `@nuxtjs/robots` for simple control of `robots.txt` permissions from `nuxt.config.ts`. -Several bug fixes since initial publication, including: +### 0.7.4 +- Updates: Complete update of stack to latest long-term releases. [#35](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/35) by @turukawa, review by @br3ndonland + - `frontend`: + - Node 16 -> 18 + - Nuxt 3.2 -> 3.6.5 + - Latest Pinia requires changes in stores, where imports are not required (cause actual errors), and parameter declaration must happen in functions. + - `backend` and `celeryworker`: + - Python 3.9 -> 3.11 + - FastAPI 0.88 -> 0.99 (Inboard 0.37 -> 0.51) + - Poetry -> Hatch + - Postgres 14 -> 15 +- Fixed: Updated token url in deps.py [#29](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/29) by @vusa +- Docs: Reorganised documentation [#21](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/21) by @turukawa -* Order of path operations for users. -* Frontend sending login data in the correct format. -* Add https://localhost variants to CORS. +[Historic changes from original](https://github.com/tiangolo/full-stack-fastapi-postgresql#release-notes) ## License diff --git a/cookiecutter.json b/cookiecutter.json index fc0e6fab00..ac27a057af 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -3,24 +3,32 @@ "project_slug": "{{ cookiecutter.project_name|lower|replace(' ', '-') }}", "domain_main": "{{cookiecutter.project_slug}}.com", "domain_staging": "stag.{{cookiecutter.domain_main}}", + "domain_base_api_url": "http://{{cookiecutter.domain_main}}/api/v1", + "domain_base_ws_url": "ws://{{cookiecutter.domain_main}}/api/v1", "docker_swarm_stack_name_main": "{{cookiecutter.domain_main|replace('.', '-')}}", "docker_swarm_stack_name_staging": "{{cookiecutter.domain_staging|replace('.', '-')}}", "secret_key": "changethis", + "totp_secret_key": "changethis", "first_superuser": "admin@{{cookiecutter.domain_main}}", "first_superuser_password": "changethis", "backend_cors_origins": "[\"http://localhost\", \"http://localhost:4200\", \"http://localhost:3000\", \"http://localhost:8080\", \"https://localhost\", \"https://localhost:4200\", \"https://localhost:3000\", \"https://localhost:8080\", \"http://dev.{{cookiecutter.domain_main}}\", \"https://{{cookiecutter.domain_staging}}\", \"https://{{cookiecutter.domain_main}}\", \"http://local.dockertoolbox.tiangolo.com\", \"http://localhost.tiangolo.com\"]", + "smtp_tls": true, "smtp_port": "587", "smtp_host": "", "smtp_user": "", "smtp_password": "", "smtp_emails_from_email": "info@{{cookiecutter.domain_main}}", + "smtp_emails_from_name": "Symona Adaro", + "smtp_emails_to_email": "info@{{cookiecutter.domain_main}}", "postgres_password": "changethis", "pgadmin_default_user": "{{cookiecutter.first_superuser}}", "pgadmin_default_user_password": "{{cookiecutter.first_superuser_password}}", + "neo4j_password": "changethis", + "traefik_constraint_tag": "{{cookiecutter.domain_main}}", "traefik_constraint_tag_staging": "{{cookiecutter.domain_staging}}", "traefik_public_constraint_tag": "traefik-public", @@ -36,9 +44,11 @@ "docker_image_frontend": "{{cookiecutter.docker_image_prefix}}frontend", "_copy_without_render": [ - "frontend/src/**/*.html", - "frontend/src/**/*.vue", + "frontend/**/*.html", + "frontend/**/*.vue", + "frontend/.nuxt/*", "frontend/node_modules/*", - "backend/app/app/email-templates/**" + "backend/app/app/email-templates/**", + "backend/app/app/gdb/**" ] } diff --git a/docs/authentication-guide.md b/docs/authentication-guide.md new file mode 100644 index 0000000000..2fe9259ada --- /dev/null +++ b/docs/authentication-guide.md @@ -0,0 +1,117 @@ +# Authentication with Magic and Oauth2 + +1. [Getting started](getting-started.md) +2. [Development and installation](development-guide.md) +3. [Deployment for production](deployment-guide.md) +4. [Authentication and magic tokens](authentication-guide.md) +5. [Websockets for interactive communication](websocket-guide.md) + +--- + +## Contents + +- [Minimum security requirements](#minimum-security-requirements) +- [Authenticated email-based _magic_ login](#authenticated-email-based-magic-login) + - [Magic login workflow](#magic-login-workflow) + - [Oauth2 password login](#oauth2-password-login) + - [Account recovery and reset](#account-recovery-and-reset) +- [Two-factor authentication](#two-factor-authentication) +- [Access and Refresh tokens](#access-and-refresh-tokens) +- [References](#references) + +## Minimum security requirements + +The following is the baseline [recommended approach](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md) for ensuring safe app authentication: + +- Any user account change must require current password verification to ensure that it's the legitimate user. +- Login page and all subsequent authenticated pages must be exclusively accessed over TLS or other strong transport. +- Account recovery must ensure that the starting point is a logged-out account. +- Where a state is unclear, use two tokens (one emailed, one stored in the browser) with a handshaking / fingerprinting protocol to ensure a chain of custody. +- An application should respond with a generic error message regardless of whether: + - The user ID or password was incorrect. + - The account does not exist. + - The account is locked or disabled. +- Code should go through the same process, no matter what, allowing the application to return in approximately the same response time. +- In the words of [George Orwell](https://en.wikipedia.org/wiki/Politics_and_the_English_Language#Remedy_of_Six_Rules), "break any of these rules sooner than do anything outright barbarous". + +[On passwords](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Password_Storage_Cheat_Sheet.md): +- Use `Argon2id` with a minimum configuration of 15 MiB of memory, an iteration count of 2, and 1 degree of parallelism. +- Passwords shorter than 8 characters are considered to be weak (NIST SP800-63B). +- Maximum password length of 64 prevents long password Denial of Service attacks. +- Do not silently truncate passwords. +- Allow usage of all characters, including unicode and whitespace. + +## Authenticated email-based magic login + +Most web applications permit account recovery through requesting a password reset via email. This is a weak point in the custodial chain _even_ assuming a savvy user adhering to best-practice password conventions. In which case, secure this and offer it as a login option ... a _magic_ login. + +> Any custodial changes to user-controlled information must be treated as requiring full authentication. Do **not** assume that a logged-in user is the authorised account holder. + +### Magic login workflow + +- Ensure the user is logged out. +- User enters email in `frontend` and submits via API to `backend`. +- Check if an account exists, if not silently create one, and get the user `id`. +- Generate two 30-second-duration magic JWT tokens, each with a randomly-generated UUID `sub` (subject) and `fingerprint`, where `sub1 != sub2` and `fingerprint1 == fingerprint2`. +- One is emailed to the user (with `sub = user.id`) and the other is returned to the `frontend` to be stored in the user's browser. +- Once the user clicks on (or pastes) the magic / emailed link and returns to the browser, check that the `fingerprint` in the stored and submitted tokens are the same, and submit both to the `backend`. +- Validate the tokens, and check compliance with `sub` and `fingerprint` rules. +- If the custodial chain is secure, return an `access_token` and `refresh_token` to the `frontend`. +- Note that the tokens provide no meaningful information to an adversary. No email address or personal information. + +### Oauth2 password login + +Users may not always have access to email, or use a password manager, making it easier (and faster) to login with a password. A fallback to a stored password must be offered. You could also choose to enforce passwords for admin users. + +### Account recovery and reset + +Clearly a user does not _need_ a password to login with this process, and so there is no enforcement in the core stack. However, an application may require evidence of a deliberate decision in the custodial chain. Enforcing a password is part of that chain, and that raises the need to reset a password if it is ever lost. + +Password recovery functions much the same as the magic workflow, with the same dual token validation process, except that the user now goes to a page that permits them to save a new password. + +The user can also change their password while logged in, but - mindful of the rules for validation - they will need to provide their original password before doing so. + +## Two-factor authentication + +Time-based One-Time Password (TOTP) authentication extends the login process to include a _challenge-response_ component where the user needs to enter a time-based token _after_ their preferred login method. + +This requires that the user: + +- Install an authenticator app. +- Generate a QR code or key and pair that with their app. +- Confirm that they are paired. + +After that, the user will be challenged to enter a 6-digit verification code to conclude each login. + +The login workflow is extended as follows: + +- TOTP requires the use of third-party token generators, and they seem to be stuck on `sha-1` hashing. That means deliberately dumbing down from `sha256`. +- After successful login (oauth2 or magic) instead of generating `access_token` and `refresh_token`, **instead** generate a special `access_token` with `totp = True` as a key in the token. +- Specifically test for this in each authentication check. `totp = True` can **only** be used to verify a TOTP submission, not for any other purpose. The user is not considered to be authenticated. +- When the user submits their verification code, `post` that, plus the `access_token` with `totp = True`, to complete authentication and receive the standard `access_token` and `refresh_token`. + +As before, enabling or disabling TOTP requires full authentication with a password. + +## Access and Refresh tokens + +Persisting the authentication `state` of a user requires a mechanism to respond to an authentication challenge which does not inconvenience the user, while still maintaining security. + +The standard method for doing so is via `access_token` and `refresh_token`, where: + +- The access token is of short duration (30 minutes, or even less). +- The refresh token is of long duration (3 months, and sometimes indefinite). +- An access token can only be used to authenticate the user, and a refresh token can only be used to generate new access tokens. +- Access tokens are not stored, and refresh tokens are maintained in the database ... meaning they must be deliberately deactivated on use. +- When a user logs out, deactivate their refresh tokens. + +Obviously, this still means that a long-living, active `refresh_token` is equivalent to authentication, which returns us to the caveat raised above: + +> Any custodial changes to user-controlled information must be treated as requiring full authentication. Do **not** assume that a logged-in user is the authorised account holder. + +## References + +- [Python JOSE](https://python-jose.readthedocs.io/) to generate JWT tokens. +- [PassLib](https://passlib.readthedocs.io/) to manage hashing and TOTP. +- [OWASP authentication cheat sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md) +- [OWASP password storage cheat sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Password_Storage_Cheat_Sheet.md) +- [Ensuring randomness](https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/) \ No newline at end of file diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md new file mode 100644 index 0000000000..8c2a759823 --- /dev/null +++ b/docs/deployment-guide.md @@ -0,0 +1,324 @@ +# Deployment guide for production + +1. [Getting started](getting-started.md) +2. [Development and installation](development-guide.md) +3. [Deployment for production](deployment-guide.md) +4. [Authentication and magic tokens](authentication-guide.md) +5. [Websockets for interactive communication](websocket-guide.md) + +--- + +## Contents + +- [Preparation](#preparation) + - [Committing to GitHub](#committing-to-github) + - [DigitalOcean Droplets](#digital-ocean-droplets) + - [Domain name and email](#domain-name-and-email) +- [Deployment](#deployment) + - [Docker](#docker) + - [Clone your repository](#clone-your-repository) + - [Docker Swarm Mode](#docker-swarm-mode) + - [Traefik Proxy with HTTPS](#traefik-proxy-with-https) + - [Deploy to a Docker Swarm mode cluster](#deploy-to-a-docker-swarm-mode-cluster) +- [Production URLs](#production-urls) + +There are easier ways to run a blog. You will find the resource requirements for this stack quite substantive. This really is if you intend to run some complex web service and need all the architecture. + +> **NOTE**: this is a more focused, step-by-step version of the generated "README". You will find a few more details there, especially about customising larger deployments, or changing your configuration after deployment. This is designed to get you up-and-running with your first production deployment in a controlled way. No guarantees, though. + +## Preparation + +### Committing to GitHub + +Prepare your code and resources for your first commit. There are three files which must **not** be committed unless you're quite positive your data will never leak. + +- `/.env` +- `/cookiecutter-config-file.yml` +- `/frontend/.env` + +These files will also need to be customised for production deployment. Make alternative arrangements for these files. Don't trust `.gitignore` to save you. + +### DigitalOcean Droplets + +This guide uses [DigitalOcean Droplets](https://www.digitalocean.com/pricing/droplets), so customise as required. Deploy to the smallest (currently 500MiB memory, 1 vCPU and 10GiB SSD for $4/month). You can upgrade later when you know your resource requirements. + +> **WARNING**: if you're using `neo4j` then the `java` server alone will need 1Gb of memory, and you may need a 2Gb to 4Gb base droplet. Plan accordingly. If you decide not to use it, you will need to carefully remove it. That will require editing `docker-compose.yml` and the start-up sequence in the backend. Shouldn't be too challenging. + +Ensure you add your SSH encryption keys on launch so that your server can be secure from the beginning. + +Deploy on whatever server image your prefer, although the default would be Ubuntu 20.04 (22.04 is the latest). End-of-life for 20.04 is April 2030, and for 22.04 is April 2032. You have time. The underlying image isn't that critical, as you'll be using the Docker images at their current versions. + +### Domain name and email + +Get your settings and redirects at your registrar, and then set up the various DNS records at DigitalOcean, pointing at the IP address for the droplet you set up. + +For reference: +- [Link Namecheap domain to DigitalOcean](https://www.namecheap.com/support/knowledgebase/article.aspx/10375/2208/how-do-i-link-a-domain-to-my-digitalocean-account/) +- [Manage DNS records at DigitalOcean](https://docs.digitalocean.com/products/networking/dns/how-to/manage-records/) + +Don't forget to create DNS A records for `flower`, `neo4j`, `traefik`, and `pgadmin`. + +Now you should be able to login to your server and begin deployment. + +## Deployment + +### Docker + +Update your server, and install all required packages: + +```shell +# Install the latest updates +apt-get update +apt-get upgrade -y +``` + +Then: + +```shell +# Download Docker +curl -fsSL get.docker.com -o get-docker.sh +# Install Docker using the stable channel (instead of the default "edge") +CHANNEL=stable sh get-docker.sh +# Remove Docker install script +rm get-docker.sh +``` + +### Clone your repository + +The basic approach is to clone from GitHub then set up the appropriate `.env` files and any custom `conf` files called from `docker-compose`. If yours is a private repo, review the GitHub docs for how to set that up. + +Remember you can create new passwords as follows: + +```bash +openssl rand -hex 32 +# Outputs something like: 99d3b1f01aa639e4a76f4fc281fc834747a543720ba4c8a8648ba755aef9be7f +``` + +From `/srv`: + +```shell +git clone https://github.com//.git +``` + +Then continue from the project directory `/srv/`. You can always pull your latest code from that directory, with: + +```shell +git pull +``` + +### Docker Swarm Mode + +Deploy the stack to a Docker Swarm mode cluster with a main Traefik proxy, set up using the ideas from [DockerSwarm.rocks](https://dockerswarm.rocks). And you can use CI (continuous integration) systems to do it automatically. + +This stack expects the public Traefik network to be named `traefik-public`. + +```bash +export USE_HOSTNAME=example.com +``` + +```bash +# Set up the server hostname +echo $USE_HOSTNAME > /etc/hostname +hostname -F /etc/hostname +``` + +Set up **Swarm Mode**: + +```shell +docker swarm init +``` + +If this fails, you'll need to explicitly link the public IP for the droplet: + +```shell +docker swarm init --advertise-addr 123.123.123.123 +``` + +You can add additional manager and worker nodes. This is optional and you can read the DockerSwarm.rocks link for more. + +Check that the nodes are connected and running: + +```shell +docker node ls +``` + +Which would output something like: + +``` +ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION +ndcg2iavasdfrm6q2qwere2rr * dog.example.com Ready Active Leader 18.06.1-ce +``` + +### Traefik Proxy with HTTPS + +Follow the documentation from DockerSwarm.rocks to [get automatic HTTPS certificates](https://dockerswarm.rocks/traefik/). + +Create a network that will be shared with Traefik and the containers that should be accessible from the outside, with + +```shell +docker network create --driver=overlay traefik-public +``` + +Get the Swarm node ID of this node and store it in an environment variable (use the code below exactly): + +```shell +export NODE_ID=$(docker info -f '{{.Swarm.NodeID}}') +``` + +Create a tag in this node, so that Traefik is always deployed to the same node and uses the same volume: + +```shell +docker node update --label-add traefik-public.traefik-public-certificates=true $NODE_ID +``` + +Create an environment variable with your email, to be used for the generation of Let's Encrypt certificates, e.g.: + +```shell +export EMAIL=someone@example.com +``` + +Create an environment variable with the domain you want to use for the Traefik UI (user interface), e.g.: + +```shell +export DOMAIN=traefik.example.com +``` + +You will access the Traefik dashboard at this domain, e.g. `traefik.example.com`. + +Create an environment variable with a username (you will use it for the HTTP Basic Auth for Traefik and Consul UIs), for example: + +```shell +export USERNAME=admin +``` + +Create an environment variable with the password, e.g.: + +```shell +export PASSWORD=changethis +``` + +Use `openssl` to generate the "hashed" version of the password and store it in an environment variable: + +```shell +export HASHED_PASSWORD=$(openssl passwd -apr1 $PASSWORD) +``` + +Download the file `traefik.yml`: + +```shell +curl -L dockerswarm.rocks/traefik.yml -o traefik.yml +``` + +Deploy the stack with: + +```shell +docker stack deploy -c traefik.yml traefik +``` + +It will use the environment variables you created above. Check if the stack was deployed with: + +```bash +docker stack ps traefik +``` + +It will output something like: + +``` +ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS +w5o6fmmln8ni traefik_traefik.1 traefik:v2.2 dog.example.com Running Running 1 minute ago +``` + +You can check the Traefik logs with: + +```bash +docker service logs traefik_traefik +``` + +### Deploy to a Docker Swarm mode cluster + +There are 4 (5) steps: + +1. **Pull** your git repo +2. **Build** your app images +3. **Deploy** your stack +4. **Restart** your docker service + +--- + +Here are the steps in detail: + +1. **Pull** your git repo + +```bash +cd /srv/example +``` +```bash +sudo git pull +``` + +2. **Build your app images** + +* Set these environment variables, right before the next command: + * `TAG=prod` + * `FRONTEND_ENV=production` +* Use the provided `scripts/build.sh` file with those environment variables: + +```bash +TAG=prod DOMAIN=example.com STACK_NAME=example-com TRAEFIK_TAG=example.com FRONTEND_ENV=production bash -x scripts/build.sh +``` + +**Persisting Docker named volumes** + +You can use [`docker-auto-labels`](https://github.com/tiangolo/docker-auto-labels) to automatically read the placement constraint labels in your Docker stack (Docker Compose file) and assign them to a random Docker node in your Swarm mode cluster if those labels don't exist yet. + +To do that, you can install `docker-auto-labels`: + +```bash +pip install docker-auto-labels +``` + +And then run it passing your `docker-stack.yml` file as a parameter: + +```bash +docker-auto-labels docker-stack.yml +``` + +You can run that command every time you deploy, right before deploying, as it doesn't modify anything if the required labels already exist. + +3. **Deploy your stack** + +* Set these environment variables: + * `DOMAIN=example.com` + * `TRAEFIK_TAG=example.com` + * `STACK_NAME=example-com` + * `TAG=prod` +* Use the provided `scripts/deploy.sh` file with those environment variables: + +```bash +DOMAIN=example.com TRAEFIK_TAG=example.com STACK_NAME=example-com TAG=prod bash -x scripts/deploy.sh +``` + +4. **Restart** your docker service + +```bash +sudo service docker restart +``` + +You may need to prune regularly while developing if you find yourself running out of space: + +```shell +docker system prune +``` + +## Production URLs + +These are the URLs served in production (replace `example.com` with your own): + +- Frontend: https://example.com +- Backend: https://example.com/api/ +- Automatic Interactive Docs (Swagger UI): https://example.com/docs +- Automatic Alternative Docs (ReDoc): https://example.com/redoc +- PGAdmin: https://pgadmin.example.com +- Flower: https://flower.example.com +- Traefik: https://traefik.example.com +- Neo4j: https://neo4j.example.com \ No newline at end of file diff --git a/docs/development-guide.md b/docs/development-guide.md new file mode 100644 index 0000000000..d81197f8c3 --- /dev/null +++ b/docs/development-guide.md @@ -0,0 +1,148 @@ +# Development and cookiecutter installation + +1. [Getting started](getting-started.md) +2. [Development and installation](development-guide.md) +3. [Deployment for production](deployment-guide.md) +4. [Authentication and magic tokens](authentication-guide.md) +5. [Websockets for interactive communication](websocket-guide.md) + +--- + +## Contents + +- [Run Cookiecutter](#run-cookiecutter) +- [Generate passwords](#generate-passwords) +- [Input variables](#input-variables) +- [Local development](#local-development) +- [Starting Jupyter Lab](#starting-jupyter-lab) + +## Run Cookiecutter + +Go to the directory where you want to create your project and run: + +```bash +pip install cookiecutter +cookiecutter https://github.com/whythawk/full-stack-fastapi-postgresql +``` + +## Generate passwords + +You will be asked to provide passwords and secret keys for several components. Open another terminal and run: + +```bash +openssl rand -hex 32 +# Outputs something like: 99d3b1f01aa639e4a76f4fc281fc834747a543720ba4c8a8648ba755aef9be7f +``` + +Copy the contents and use that as password / secret key. And run that again to generate another secure key. + +## Input variables + +The generator (Cookiecutter) will ask you for data on a long list of fields which will be used to populate variables across the project, customising it for you out the box. You might want to have these on hand before generating the project. + +The input variables, with their default values (some auto generated) are: + +- `project_name`: The name of the project. This will also be the folder in which your project is generated. +- `project_slug`: The development friendly name of the project. By default, based on the project name +- `domain_main`: The domain in where to deploy the project for production (from the branch `production`), used by the load balancer, backend, etc. By default, based on the project slug. +- `domain_staging`: The domain in where to deploy while staging (before production) (from the branch `master`). By default, based on the main domain. +- `domain_base_api_url`: The domain url used by the frontend app for backend api calls. If deploying a localhost development environment, likely to be `http://localhost/api/v1` +- `domain_base_ws_url`: The domain url used by the frontend app for backend websocket calls. If deploying a localhost development environment, likely to be `ws://localhost/api/v1` +- `docker_swarm_stack_name_main`: The name of the stack while deploying to Docker in Swarm mode for production. By default, based on the domain. +- `docker_swarm_stack_name_staging`: The name of the stack while deploying to Docker in Swarm mode for staging. By default, based on the domain. +- `secret_key`: Backend server secret key. Use the method above to generate it. +- `first_superuser`: The first superuser generated, with it you will be able to create more users, etc. By default, based on the domain. +- `first_superuser_password`: First superuser password. Use the method above to generate it. +- `backend_cors_origins`: Origins (domains, more or less) that are enabled for CORS (Cross Origin Resource Sharing). This allows a frontend in one domain (e.g. `https://dashboard.example.com`) to communicate with this backend, that could be living in another domain (e.g. `https://api.example.com`). It can also be used to allow your local frontend (with a custom `hosts` domain mapping, as described in the project's `README.md`) that could be living in `http://dev.example.com:8080` to communicate with the backend at `https://stag.example.com`. Notice the `http` vs `https` and the `dev.` prefix for local development vs the "staging" `stag.` prefix. By default, it includes origins for production, staging and development, with ports commonly used during local development by several popular frontend frameworks (Vue with `:8080`, React, Angular). +- `smtp_port`: Port to use to send emails via SMTP. By default `587`. +- `smtp_host`: Host to use to send emails, it would be given by your email provider, like Mailgun, Sparkpost, etc. +- `smtp_user`: The user to use in the SMTP connection. The value will be given by your email provider. +- `smtp_password`: The password to be used in the SMTP connection. The value will be given by the email provider. +- `smtp_emails_from_email`: The email account to use as the sender in the notification emails, it could be something like `info@your-custom-domain.com`. +- `smtp_emails_from_name`: The email account name to use as the sender in the notification emails, it could be something like `Symona Adaro`. +- `smtp_emails_to_email`: The email account to use as the recipient for `contact us` emails, it could be something like `requests@your-custom-domain.com`. +- `postgres_password`: Postgres database password. Use the method above to generate it. (You could easily modify it to use MySQL, MariaDB, etc). +- `pgadmin_default_user`: PGAdmin default user, to log-in to the PGAdmin interface. +- `pgadmin_default_user_password`: PGAdmin default user password. Generate it with the method above. +- `neo4j_password`: Neo4j database password. Use the method above to generate it. +- `traefik_constraint_tag`: The tag to be used by the internal Traefik load balancer (for example, to divide requests between backend and frontend) for production. Used to separate this stack from any other stack you might have. This should identify each stack in each environment (production, staging, etc). +- `traefik_constraint_tag_staging`: The Traefik tag to be used while on staging. +- `traefik_public_constraint_tag`: The tag that should be used by stack services that should communicate with the public. +- `flower_auth`: Basic HTTP authentication for flower, in the form`user:password`. By default: "`admin:changethis`". +- `sentry_dsn`: Key URL (DSN) of Sentry, for live error reporting. You can use the open source version or a free account. E.g.: `https://1234abcd:5678ef@sentry.example.com/30`. +- `docker_image_prefix`: Prefix to use for Docker image names. If you are using GitLab Docker registry it would be based on your code repository. E.g.: `git.example.com/development-team/my-awesome-project/`. +- `docker_image_backend`: Docker image name for the backend. By default, it will be based on your Docker image prefix, e.g.: `git.example.com/development-team/my-awesome-project/backend`. And depending on your environment, a different tag will be appended ( `prod`, `stag`, `branch` ). So, the final image names used will be like: `git.example.com/development-team/my-awesome-project/backend:prod`. +- `docker_image_celeryworker`: Docker image for the celery worker. By default, based on your Docker image prefix. +- `docker_image_frontend`: Docker image for the frontend. By default, based on your Docker image prefix. + +## Local development + +Once the Cookiecutter script has completed, you will have a folder populated with the base project and all input variables customised. + +Change into the project folder and run the `docker-compose` script to build the project containers: + +```bash +docker-compose build --no-cache +``` + +And start them: + +```bash +docker-compose up -d +``` + +By default, `backend` Python dependencies are managed with [Hatch](https://hatch.pypa.io/latest/). From `./backend/app/` you can install all the dependencies with: + +```console +$ hatch env prune +$ hatch env create production +``` + +Because Hatch doesn't have a version lock file (like Poetry), it is helpful to `prune` when you rebuild to avoid any sort of dependency hell. Then you can start a shell session with the new environment with: + +```console +$ hatch shell +``` + +Make sure your editor uses the environment you just created with Hatch. For Visual Studio Code, from the shell, launch an appropriate development environment with: + +```console +$ code . +``` + +**NOTE:** The Nuxt image does not automatically refresh while running in development mode. Any changes will need a rebuild. This gets tired fast, so it's easier to run Nuxt outside Docker and call through to the `backend` for API calls. You can then view the frontend at `http://localhost:3000` and the backend api endpoints at `http://localhost/redoc`. This problem won't be a concern in production. + +Change into the `/frontend` folder, and: + +```bash +yarn install +yarn dev +``` + +FastAPI `backend` updates will refresh automatically, but the `celeryworker` container must be restarted before changes take effect. + +## Starting Jupyter Lab + +If you like to do algorithmic development and testing in Jupyter Notebooks, then launch the `backend` terminal and start Jupyter as follows: + +```bash +docker-compose exec backend bash +``` + +From the terminal: + +```bash +$JUPYTER +``` + +Copy the link generated into your browser and start. + +**NOTE:** Notebooks developed in the container are not saved outside, so remember to copy them for persistence. You can do that from inside Jupyter (download), or: + +```bash +docker cp :/file/path/within/container /host/path/target +``` + +Or share a folder via `docker-compose.override.yml`. + +At this point, development is over to you. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000000..dc949840c9 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,158 @@ +# Getting started with the Base Project Generator + +1. [Getting started](getting-started.md) +2. [Development and installation](development-guide.md) +3. [Deployment for production](deployment-guide.md) +4. [Authentication and magic tokens](authentication-guide.md) +5. [Websockets for interactive communication](websocket-guide.md) + +--- + +## Contents + +- [What is it?](#what-is-it) +- [Who is it for?](#who-is-it-for) +- [What does it look like?](#what-does-it-look-like) +- [How to use it](#how-to-use-it) +- [Release notes](#release-notes) +- [License](#license) + +## What is it? + +This FastAPI, PostgreSQL, Neo4j & Nuxt 3 repo will generate a complete web application stack as a foundation for your project development. + +It consists of the following key components: + +- **Docker Compose** integration and optimization for local development. +- **Authentication** user management schemas, models, crud and apis already built, with OAuth2 JWT token support & default hashing. Offers _magic link_ authentication, with password fallback, with cookie management, including `access` and `refresh` tokens. +- [**FastAPI**](https://github.com/tiangolo/fastapi) backend with [Inboard](https://inboard.bws.bio/) one-repo Docker images, using Python 3.11: + - **SQLAlchemy** version 2.0 support for models. + - **Pydantic** version 2.7 for schemas. + - **Metadata Schema** based on [Dublin Core](https://www.dublincore.org/specifications/dublin-core/dcmi-terms/#section-3) for inheritance. + - **Common CRUD** support via generic inheritance. + - **Standards-based**: Based on (and fully compatible with) the open standards for APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) and [JSON Schema](http://json-schema.org/). + - **MJML** templates for common email transactions. + - [**Many other features**]("https://fastapi.tiangolo.com/features/"): including automatic validation, serialization, interactive documentation, etc. +- [**Nuxt/Vue 3**](https://nuxt.com/) frontend using TypeScript: + - **Authorisation** via middleware for page access, including logged in or superuser. + - **Model blog** project, with [Nuxt Content](https://content.nuxtjs.org/) for writing Markdown pages. + - **Form validation** with [Vee-Validate 4](https://vee-validate.logaretm.com/v4/). + - **State management** with [Pinia](https://pinia.vuejs.org/), and persistance with [Pinia PersistedState](https://prazdevs.github.io/pinia-plugin-persistedstate/). + - **CSS and templates** with [TailwindCSS](https://tailwindcss.com/), [HeroIcons](https://heroicons.com/), and [HeadlessUI](https://headlessui.com/). +- **PostgreSQL** database. +- **PGAdmin** for PostgreSQL database management. +- **Celery** worker that can import and use models and code from the rest of the backend selectively. +- **Flower** for Celery jobs monitoring. +- **Neo4j** graph database, including integration into the FastAPI base project. +- Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers. +- Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation. + +## Who is it for? + +This project is a rock-solid foundation on which to build complex web applications which need parallel processing, scheduled event management, and a range of relational and graph database support. The base deployment - with PostgreSQL and Neo4j - takes up about 10Gb, and requires about 2Gb of memory to run. + +This is **not** a light-weight system to deploy a blog or simple content-management-system. + +It is for developers looking to build and maintain full feature progressive web applications that can run online, or offline, want the complex-but-routine aspects of auth 'n auth, and component and deployment configuration taken care of. + +## What does it look like? + +### App landing page + +![Landing page](../img/landing.png) + +### Dashboard Login + +![Magic-link login](../img/login.png) + +### Dashboard User Management + +![Moderator user management](../img/dashboard.png) + +### Interactive API documentation + +![Interactive API docs](../img/redoc.png) + +### Enabling two-factor security (TOTP) + +![Enabling TOTP](../img/totp.png) + +## How to use it + +### Installing for local development + +Running Cookiecutter to customise the deployment with your settings, and then building with Docker compose, takes about 20 minutes. + +- [Development and installation](development-guide.md) + +### Deploying for production + +This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it is designed to be used in a cluster controlled with pure Docker in Swarm Mode with a Traefik main load balancer proxy handling automatic HTTPS certificates, using the ideas from [DockerSwarm.rocks](https://dockerswarm.rocks). + +- [Deployment for production](deployment-guide.md) + +### Authentication with magic and TOTP + +Time-based One-Time Password (TOTP) authentication extends the login process to include a challenge-response component where the user needs to enter a time-based token after their preferred login method. + +- [Authentication and magic tokens](authentication-guide.md) + +### More details + +After using this generator, your new project will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](../{{cookiecutter.project_slug}}/README.md). + +## Release Notes + +See notes and [releases](https://github.com/whythawk/full-stack-fastapi-postgresql/releases). The last four release notes are listed here: + +## 0.9.0 + +Updates to `backend`: +- FastAPI 0.99 -> 0.109 (Inboard 0.51 -> 0.68) +- Pydantic 1.10 -> 2.7.1 + +Updates to `frontend`: +- NuxtJS 3.6.5 -> 3.11.2 +- Nuxtjs i18n 8.0.0 RC -> 8.3.1 + +The Pydantic change is dramatic, so please revise their [migration guide](https://docs.pydantic.dev/2.7/migration/). Similarly, [nuxt/i18n](https://i18n.nuxtjs.org/docs/getting-started) has some major quality of life improvements. + +## 0.8.2 + +Fixing [#39](https://github.com/whythawk/full-stack-fastapi-postgresql/issues/39), thanks to @a-vorobyoff: + +- Exposing port 24678 for Vite on frontend in development mode. +- Ensuring Nuxt content on /api/_content doesn't interfere with backend /api/v routes. +- Checking for password before hashing on user creation. +- Updating generated README for Hatch (after Poetry deprecation). +- Minor fixes. + +### 0.8.1 + +- Minor updates to Docker scripts for `build`. + +### 0.8.0 + +- Updates to `frontend`, [#37](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/37) by @turukawa: + - `@nuxtjs/i18n` for internationalisation, along with language selection component. + - `@vite-pwa/nuxt` along with button components for install and refreshing the app and service workers, and a CLI icon generator. + - `@nuxtjs/robots` for simple control of `robots.txt` permissions from `nuxt.config.ts`. + +### 0.7.4 + +- Updates: Complete update of stack to latest long-term releases. [#35](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/35) by @turukawa, review by @br3ndonland + - `frontend`: + - Node 16 -> 18 + - Nuxt 3.2 -> 3.6.5 + - Latest Pinia requires changes in stores, where imports are not required (cause actual errors), and parameter declaration must happen in functions. + - `backend` and `celeryworker`: + - Python 3.9 -> 3.11 + - FastAPI 0.88 -> 0.99 (Inboard 0.37 -> 0.51) + - Poetry -> Hatch + - Postgres 14 -> 15 +- Fixed: Updated token url in deps.py [#29](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/29) by @vusa +- Docs: Reorganised documentation [#21](https://github.com/whythawk/full-stack-fastapi-postgresql/pull/21) by @turukawa + +## License + +This project is licensed under the terms of the MIT license. diff --git a/docs/websocket-guide.md b/docs/websocket-guide.md new file mode 100644 index 0000000000..2fc814e9d5 --- /dev/null +++ b/docs/websocket-guide.md @@ -0,0 +1,252 @@ +# Websockets for interactive communication + +[Websockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) permit synchronous interactive communication between a user's browser and a server. A series of messages are sent between each end, triggering response events. + +While Websockets support multi-user sessions, this documentation is mainly focused on a single-user session. + +--- + +1. [Getting started](getting-started.md) +2. [Development and installation](development-guide.md) +3. [Deployment for production](deployment-guide.md) +4. [Authentication and magic tokens](authentication-guide.md) +5. [Websockets for interactive communication](websocket-guide.md) + +--- +## Contents + +- [Why Websockets?](#why-websockets) +- [High-level architecture and workflow](#high-level-architecture-and-workflow) +- [Requirements](#requirements) +- [Setting up the Nuxt `frontend`](#setting-up-the-nuxt-frontend) +- [Setting up the FastAPI `backend`](#setting-up-the-fastapi-backend) + +## Why Websockets? + +Web applications sessions are not persistent. You can maintain state at the back- _or_ frontend, but not both simultaneously. It's great that `Nuxt Pinia` allows your browser to store what you've been doing, but that will need to be communicated via your API to the backend on every page change. + +There are ways around this, such as automatically polling the API (known as [long polling](https://ably.com/topic/long-polling)), but Websockets create a bidirectional session between the front- and backends, allowing you to run a synchronous interactive process. + +This is great for interactive chat services, or where your app offers complex software which is impractical (or outright impossible) to run in a browser. + +Depending on the popularity of your app, concurrency may become a problem. Stateless polling means you can have millions of users on relatively limited infrastructure, because they load information, and then read it. People are using your app simultaneously, not polling your infrastructure simultaneously. + +Websockets are active sessions, so be sparing with the amount of information you send back and forth. + +## High-level architecture and workflow + +The following general conditions apply: + +- If a session requires user-authentication, then you need to manually perform this. You may be used to FastAPI routes handling this for you normally, but Websockets don't. +- Messages sent between front- and backend are `JSON-encoded`. If you use variables that aren't easily stringified (Pydantic, for example, doesn't automatically stringify UUIDs), you'll need to deliberately check for this. + +Here's how the workflow tends to play out: + +- `frontend` - initialise a socket by opening a session and sending an initial payload `request`, which may include a user token (if authentication is required). +- `backend` - receive a socket `request`, validate the session (user or other), send a `response` to the `frontend`, and then enter a `while True` loop to keep the session open and keep listening for `requests`. +- `frontend` - each `response` triggers an event, updating the view. +- `frontend` - send an instruction to close the socket when the user ends the sessions or, as fallback, when the user changes the page. +- `backend` - can also end the session based on user activity. + +If the user is joining a multi-user session, then each update to the session is communicated to all users. + +## Requirements + +- [FastAPI](https://fastapi.tiangolo.com/advanced/websockets/) requires the installation of the WebSockets library: + +``` +pip install websockets +``` + +- There are multiple JavaScript libraries for Websockets, but I like [WebSocketAs Promised](https://github.com/vitalets/websocket-as-promised#readme): + +``` +yarn install websocket-as-promised +``` + +## Setting up the Nuxt `frontend` + +The API `backend` is reached at `ws://localhost/api/v1` (or `wss:///api/v1` in production). + +Create an appropriate `websocketAPI.ts` file: + +``` +import WebSocketAsPromised from "websocket-as-promised" +import { apiCore } from "./core" + +export const apiSockets = { + socketRequest() { + return new WebSocketAsPromised(`${apiCore.wsurl()}/socket`, { + packMessage: (data) => JSON.stringify(data), + unpackMessage: (data) => JSON.parse(data as string), + }) + }, +} +``` + +Then, in the relevant `page.ts` (or component), you create a `websocket` variable (`wsp`) and attach a `watcher` to it so that you can respond to events as it is updated: + +``` + +``` + +The `response` is a `switch` statement which identifies and responds to the appropriate event. + +## Setting up the FastAPI `backend` + +At the `backend` you already have a [sockets.py](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/0.8.2/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/app/api/sockets.py) which handles serialising and deserialising the Websocket requests and responses. Now you create a route in `/endpoints`: + +``` +from fastapi import APIRouter, Depends, WebSocket, HTTPException, WebSocketException +from starlette.websockets import WebSocketDisconnect +from websockets.exceptions import ConnectionClosedError +from app import crud, models, schema_types, schemas +from app.api import deps, sockets + +router = APIRouter() + +@router.websocket("/socket") +async def some_websocket_session(*, db: Session = Depends(deps.get_db), websocket: WebSocket): + current_user = None + initialised = False + success = False + # 1. Open the socket and validate current user + await websocket.accept() + request = await sockets.receive_request(websocket=websocket) + response = {"state": "error", "error": "Could not validate credentials."} + if request.get("token"): + try: + current_user = deps.get_active_websocket_user(db=db, token=request["token"]) + response = {"state": "initialised", "data": {}} + except ValidationError: + pass + success = await sockets.send_response(websocket=websocket, response=response) + if response["state"] == "initialised" and success: + try: + while True and success: + # LOOP ################################################################# + request = await sockets.receive_request(websocket=websocket) + if not request: + break + state = request.get("state") + data = request.get("data", {}) + data = sockets.sanitize_data_request(data) + response = {"state": state} + try: + # ALL THE STATES ################################################### + if state == "startThings": + # Do some stuff + data = {"something": "yes, something"} + response["data"] = data + initialised = True + # SAVE AND CLOSE THE SESSION ####################################### + if state == "save" and initialised: + # This will close the socket, if it succeeds + response["data"] = {} + break + except ValidationError as e: + response = {"state": "error", "error": e} + success = await sockets.send_response(websocket=websocket, response=response) + # LOOP ################################################################# + except (WebSocketDisconnect, WebSocketException, ConnectionClosedError) as e: + response = {"state": "error", "error": e} + try: + await sockets.send_response(websocket=websocket, response=response) + await websocket.close(code=1000) + except (WebSocketDisconnect, ConnectionClosedError, RuntimeError, WebSocketException): + pass +``` + +And that's - very simplistically - basically it. \ No newline at end of file diff --git a/img/dashboard.png b/img/dashboard.png index ab3f86357c..092bb07dd0 100644 Binary files a/img/dashboard.png and b/img/dashboard.png differ diff --git a/img/landing.png b/img/landing.png new file mode 100644 index 0000000000..92b8454371 Binary files /dev/null and b/img/landing.png differ diff --git a/img/login.png b/img/login.png index b731bd238f..499139faed 100644 Binary files a/img/login.png and b/img/login.png differ diff --git a/img/redoc.png b/img/redoc.png index 09243741d0..1662efe899 100644 Binary files a/img/redoc.png and b/img/redoc.png differ diff --git a/img/totp.png b/img/totp.png new file mode 100644 index 0000000000..0a32cc020f Binary files /dev/null and b/img/totp.png differ diff --git a/scripts/discard-dev-files.sh b/scripts/discard-dev-files.sh index 7a07a70bb3..a20a85f198 100644 --- a/scripts/discard-dev-files.sh +++ b/scripts/discard-dev-files.sh @@ -5,7 +5,7 @@ set -e rm -rf \{\{cookiecutter.project_slug\}\}/.git rm -rf \{\{cookiecutter.project_slug\}\}/backend/app/poetry.lock rm -rf \{\{cookiecutter.project_slug\}\}/frontend/node_modules -rm -rf \{\{cookiecutter.project_slug\}\}/frontend/dist +rm -rf \{\{cookiecutter.project_slug\}\}/frontend/.nuxt git checkout \{\{cookiecutter.project_slug\}\}/README.md git checkout \{\{cookiecutter.project_slug\}\}/.gitlab-ci.yml git checkout \{\{cookiecutter.project_slug\}\}/cookiecutter-config-file.yml diff --git a/{{cookiecutter.project_slug}}/.dockerignore b/{{cookiecutter.project_slug}}/.dockerignore new file mode 100644 index 0000000000..a9e057a6ab --- /dev/null +++ b/{{cookiecutter.project_slug}}/.dockerignore @@ -0,0 +1,4 @@ +# Get rid of .venv when copying +# https://docs.docker.com/engine/reference/builder/#dockerignore-file +*/.venv +*/*/.venv \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.env b/{{cookiecutter.project_slug}}/.env index f6540eeff7..ee69a7131a 100644 --- a/{{cookiecutter.project_slug}}/.env +++ b/{{cookiecutter.project_slug}}/.env @@ -14,19 +14,24 @@ DOCKER_IMAGE_CELERYWORKER={{cookiecutter.docker_image_celeryworker}} DOCKER_IMAGE_FRONTEND={{cookiecutter.docker_image_frontend}} # Backend +BACKEND_APP_MODULE=app.main:app BACKEND_CORS_ORIGINS={{cookiecutter.backend_cors_origins}} +BACKEND_PRE_START_PATH=/app/prestart.sh PROJECT_NAME={{cookiecutter.project_name}} SECRET_KEY={{cookiecutter.secret_key}} +TOTP_SECRET_KEY={{cookiecutter.totp_secret_key}} FIRST_SUPERUSER={{cookiecutter.first_superuser}} FIRST_SUPERUSER_PASSWORD={{cookiecutter.first_superuser_password}} -SMTP_TLS=True +SMTP_TLS={{cookiecutter.smtp_tls}} SMTP_PORT={{cookiecutter.smtp_port}} SMTP_HOST={{cookiecutter.smtp_host}} SMTP_USER={{cookiecutter.smtp_user}} SMTP_PASSWORD={{cookiecutter.smtp_password}} EMAILS_FROM_EMAIL={{cookiecutter.smtp_emails_from_email}} +EMAILS_FROM_NAME={{cookiecutter.smtp_emails_from_name}} +EMAILS_TO_EMAIL={{cookiecutter.smtp_emails_to_email}} -USERS_OPEN_REGISTRATION=False +USERS_OPEN_REGISTRATION=True SENTRY_DSN={{cookiecutter.sentry_dsn}} @@ -43,3 +48,10 @@ POSTGRES_DB=app PGADMIN_LISTEN_PORT=5050 PGADMIN_DEFAULT_EMAIL={{cookiecutter.pgadmin_default_user}} PGADMIN_DEFAULT_PASSWORD={{cookiecutter.pgadmin_default_user_password}} + +# Neo4j +NEO4J_SERVER=neo4j +NEO4J_USERNAME=neo4j +NEO4J_PASSWORD={{cookiecutter.neo4j_password}} +NEO4J_AUTH=neo4j:{{cookiecutter.neo4j_password}} +NEO4J_BOLT=bolt \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.gitattributes b/{{cookiecutter.project_slug}}/.gitattributes new file mode 100644 index 0000000000..e7c1d93d39 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text eol=lf diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore index 1230031ec4..108cdb9bfd 100755 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -1,3 +1,7 @@ .vscode .mypy_cache docker-stack.yml +*.txt +.env +*.code-workspace +.s3cfg \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index 5594dd546d..8a4eca7870 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -1,24 +1,13 @@ # {{cookiecutter.project_name}} -## Backend Requirements +## Documentation for development -* [Docker](https://www.docker.com/). -* [Docker Compose](https://docs.docker.com/compose/install/). -* [Poetry](https://python-poetry.org/) for Python package and environment management. +- [Getting started](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/getting-started.md) +- [Development and installation](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/development-guide.md) +- [Deployment for production](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/deployment-guide.md) +- [Authentication and magic tokens](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/authentication-guide.md) -## Frontend Requirements - -* Node.js (with `npm`). - -## Backend local development - -* Start the stack with Docker Compose: - -```bash -docker-compose up -d -``` - -* Now you can open your browser and interact with these URLs: +* Local development URLs: Frontend, built with Docker, with routes handled based on the path: http://localhost @@ -30,6 +19,8 @@ Alternative automatic documentation with ReDoc (from the OpenAPI backend): http: PGAdmin, PostgreSQL web administration: http://localhost:5050 +Neo4j web administration: http://localhost:7474 + Flower, administration of Celery tasks: http://localhost:5555 Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090 @@ -39,13 +30,13 @@ Traefik UI, to see how the routes are being handled by the proxy: http://localho To check the logs, run: ```bash -docker-compose logs +docker compose logs ``` To check the logs of a specific service, add the name of the service, e.g.: ```bash -docker-compose logs backend +docker compose logs backend ``` If your Docker is not running in `localhost` (the URLs above wouldn't work) check the sections below on **Development with Docker Toolbox** and **Development with a custom IP**. @@ -54,21 +45,26 @@ If your Docker is not running in `localhost` (the URLs above wouldn't work) chec ### General workflow -By default, the dependencies are managed with [Poetry](https://python-poetry.org/), go there and install it. +By default, the dependencies are managed with [Hatch](https://hatch.pypa.io/latest/), go there and install it. From `./backend/app/` you can install all the dependencies with: ```console -$ poetry install +$ hatch env prune +$ hatch env create production ``` -Then you can start a shell session with the new environment with: +Because Hatch doesn't have a version lock file (like Poetry), it is helpful to `prune` when you rebuild to avoid any sort of dependency hell. Then you can start a shell session with the new environment with: ```console -$ poetry shell +$ hatch shell ``` -Next, open your editor at `./backend/app/` (instead of the project root: `./`), so that you see an `./app/` directory with your code inside. That way, your editor will be able to find all the imports, etc. Make sure your editor uses the environment you just created with Poetry. +Next, open your editor at `./backend/app/` (instead of the project root: `./`), so that you see an `./app/` directory with your code inside. That way, your editor will be able to find all the imports, etc. Make sure your editor uses the environment you just created with Hatch. For Visual Studio Code, from the shell, launch an appropriate development environment with: + +```console +$ code . +``` Modify or add SQLAlchemy models in `./backend/app/app/models/`, Pydantic schemas in `./backend/app/app/schemas/`, API endpoints in `./backend/app/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/app/crud/`. The easiest might be to copy the ones for Items (models, endpoints, and CRUD utils) and update them to your needs. @@ -82,12 +78,10 @@ During development, you can change Docker Compose settings that will only affect The changes to that file only affect the local development environment, not the production environment. So, you can add "temporary" changes that help the development workflow. -For example, the directory with the backend code is mounted as a Docker "host volume", mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast. - -There is also a command override that runs `/start-reload.sh` (included in the base image) instead of the default `/start.sh` (also included in the base image). It starts a single server process (instead of multiple, as would be for production) and reloads the process whenever the code changes. Have in mind that if you have a syntax error and save the Python file, it will break and exit, and the container will stop. After that, you can restart the container by fixing the error and running again: +For example, the directory with the backend code is mounted as a Docker "host volume", mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast. Have in mind that if you have a syntax error and save the Python file, it will break and exit, and the container will stop. After that, you can restart the container by fixing the error and running again: ```console -$ docker-compose up -d +$ docker compose up -d ``` There is also a commented out `command` override, you can uncomment it and comment the default one. It makes the backend container run a process that does "nothing", but keeps the container alive. That allows you to get inside your running container and execute commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detects changes, or start a Jupyter Notebook session. @@ -95,13 +89,13 @@ There is also a commented out `command` override, you can uncomment it and comme To get inside the container with a `bash` session you can start the stack with: ```console -$ docker-compose up -d +$ docker compose up -d ``` and then `exec` inside the running container: ```console -$ docker-compose exec backend bash +$ docker compose exec backend bash ``` You should see an output like: @@ -112,26 +106,10 @@ root@7f2607af31c3:/app# that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory. -There you can use the script `/start-reload.sh` to run the debug live reloading server. You can run that script from inside the container with: - -```console -$ bash /start-reload.sh -``` - -...it will look like: - -```console -root@7f2607af31c3:/app# bash /start-reload.sh -``` - -and then hit enter. That runs the live reloading server that auto reloads when it detects code changes. - -Nevertheless, if it doesn't detect a change but a syntax error, it will just stop with an error. But as the container is still alive and you are in a Bash session, you can quickly restart it after fixing the error, running the same command ("up arrow" and "Enter"). - -...this previous detail is what makes it useful to have the container alive doing nothing and then, in a Bash session, make it run the live reload server. - ### Backend tests +> NOTE: Tests have not been updated on the current version, so these are likely to fail. + To test the backend run: ```console @@ -155,7 +133,7 @@ The `./backend/app` directory is mounted as a "host volume" inside the docker co You can rerun the test on live code: ```Bash -docker-compose exec backend /app/tests-start.sh +docker compose exec backend /app/tests-start.sh ``` #### Test running stack @@ -163,7 +141,7 @@ docker-compose exec backend /app/tests-start.sh If your stack is already up and you just want to run the tests, you can use: ```bash -docker-compose exec backend /app/tests-start.sh +docker compose exec backend /app/tests-start.sh ``` That `/app/tests-start.sh` script just calls `pytest` after making sure that the rest of the stack is running. If you need to pass extra arguments to `pytest`, you can pass them to that command and they will be forwarded. @@ -171,7 +149,7 @@ That `/app/tests-start.sh` script just calls `pytest` after making sure that the For example, to stop on first error: ```bash -docker-compose exec backend bash /app/tests-start.sh -x +docker compose exec backend bash /app/tests-start.sh -x ``` #### Test Coverage @@ -187,7 +165,7 @@ DOMAIN=backend sh ./scripts/test-local.sh --cov-report=html To run the tests in a running stack with coverage HTML reports: ```bash -docker-compose exec backend bash /app/tests-start.sh --cov-report=html +docker compose exec backend bash /app/tests-start.sh --cov-report=html ``` ### Live development with Python Jupyter Notebooks @@ -199,7 +177,7 @@ The `docker-compose.override.yml` file sends a variable `env` with a value `dev` So, you can enter into the running Docker container: ```bash -docker-compose exec backend bash +docker compose exec backend bash ``` And use the environment variable `$JUPYTER` to run a Jupyter Notebook with everything configured to listen on the public port (so that you can use it from your browser). @@ -240,7 +218,7 @@ Make sure you create a "revision" of your models and that you "upgrade" your dat * Start an interactive session in the backend container: ```console -$ docker-compose exec backend bash +$ docker compose exec backend bash ``` * If you created a new model in `./backend/app/app/models/`, make sure to import it in `./backend/app/app/db/base.py`, that Python module (`base.py`) that imports all the models will be used by Alembic. @@ -371,44 +349,14 @@ That variable will make your frontend communicate with that domain when interact After changing the two lines, you can re-start your stack with: ```bash -docker-compose up -d +docker compose up -d ``` and check all the corresponding available URLs in the section at the end. ## Frontend development -* Enter the `frontend` directory, install the NPM packages and start the live server using the `npm` scripts: - -```bash -cd frontend -npm install -npm run serve -``` - -Then open your browser at http://localhost:8080 - -Notice that this live server is not running inside Docker, it is for local development, and that is the recommended workflow. Once you are happy with your frontend, you can build the frontend Docker image and start it, to test it in a production-like environment. But compiling the image at every change will not be as productive as running the local development server with live reload. - -Check the file `package.json` to see other available options. - -If you have Vue CLI installed, you can also run `vue ui` to control, configure, serve, and analyze your application using a nice local web user interface. - -If you are only developing the frontend (e.g. other team members are developing the backend) and there is a staging environment already deployed, you can make your local development code use that staging API instead of a full local Docker Compose stack. - -To do that, modify the file `./frontend/.env`, there's a section with: - -``` -VUE_APP_ENV=development -# VUE_APP_ENV=staging -``` - -* Switch the comment, to: - -``` -# VUE_APP_ENV=development -VUE_APP_ENV=staging -``` +See the [frontend README](./frontend/README.md) for instructions. ### Removing the frontend @@ -639,11 +587,11 @@ TAG=${TAG?Variable not set} \ # a default value of "production" if nothing else was passed FRONTEND_ENV=${FRONTEND_ENV-production?Variable not set} \ # The actual comand that does the work: docker-compose -docker-compose \ +docker compose \ # Pass the file that should be used, setting explicitly docker-compose.yml avoids the # default of also using docker-compose.override.yml -f docker-compose.yml \ -# Use the docker-compose sub command named "config", it just uses the docker-compose.yml +# Use the docker compose sub command named "config", it just uses the docker-compose.yml # file passed to it and prints their combined contents # Put those contents in a file "docker-stack.yml", with ">" config > docker-stack.yml @@ -805,11 +753,11 @@ Traefik UI: http://localhost.tiangolo.com:8090 ## Project generation and updating, or re-generating -This project was generated using https://github.com/tiangolo/full-stack-fastapi-postgresql with: +This project was generated using https://github.com/whythawk/full-stack-fastapi-postgresql with: ```bash pip install cookiecutter -cookiecutter https://github.com/tiangolo/full-stack-fastapi-postgresql +cookiecutter https://github.com/whythawk/full-stack-fastapi-postgresql ``` You can check the variables used during generation in the file `cookiecutter-config-file.yml`. @@ -827,7 +775,7 @@ You can use that file while generating a new project to reuse all those variable For example, run: ```console -$ cookiecutter --config-file ./cookiecutter-config-file.yml --output-dir ../project-copy https://github.com/tiangolo/full-stack-fastapi-postgresql +$ cookiecutter --config-file ./cookiecutter-config-file.yml --output-dir ../project-copy https://github.com/whythawk/full-stack-fastapi-postgresql ``` That will use the file `cookiecutter-config-file.yml` in the current directory (in this project) to generate a new project inside a sibling directory `project-copy`. diff --git a/{{cookiecutter.project_slug}}/backend/.dockerignore b/{{cookiecutter.project_slug}}/backend/.dockerignore new file mode 100644 index 0000000000..a9e057a6ab --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/.dockerignore @@ -0,0 +1,4 @@ +# Get rid of .venv when copying +# https://docs.docker.com/engine/reference/builder/#dockerignore-file +*/.venv +*/*/.venv \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/.flake8 b/{{cookiecutter.project_slug}}/backend/app/.flake8 index 710dc9c030..01ecba739b 100644 --- a/{{cookiecutter.project_slug}}/backend/app/.flake8 +++ b/{{cookiecutter.project_slug}}/backend/app/.flake8 @@ -1,3 +1,5 @@ [flake8] -max-line-length = 88 +ignore = E302, E305, E203, E501, W503 +select = C,E,F,W,B,B950 +max-line-length = 120 exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache diff --git a/{{cookiecutter.project_slug}}/backend/app/.gitignore b/{{cookiecutter.project_slug}}/backend/app/.gitignore index f511683016..3663d8296a 100644 --- a/{{cookiecutter.project_slug}}/backend/app/.gitignore +++ b/{{cookiecutter.project_slug}}/backend/app/.gitignore @@ -1,3 +1,23 @@ .mypy_cache .coverage htmlcov +.venv +.vscode +*.py[co] +*.egg +*.egg-info +*.ipynb +*.code-workspace +dist +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg +pip-log.txt +.coverage +.tox +*.mo +.s3cfg \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/.python-version b/{{cookiecutter.project_slug}}/backend/app/.python-version new file mode 100644 index 0000000000..afad818663 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/.python-version @@ -0,0 +1 @@ +3.11.0 diff --git a/{{cookiecutter.project_slug}}/backend/app/README.md b/{{cookiecutter.project_slug}}/backend/app/README.md new file mode 100644 index 0000000000..687295824e --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/README.md @@ -0,0 +1,781 @@ +# Base Project + +## Documentation for development + +- [Getting started](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/getting-started.md) +- [Development and installation](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/development-guide.md) +- [Deployment for production](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/deployment-guide.md) +- [Authentication and magic tokens](https://github.com/whythawk/full-stack-fastapi-postgresql/blob/master/docs/authentication-guide.md) + +* Local development URLs: + +Frontend, built with Docker, with routes handled based on the path: http://localhost + +Backend, JSON based web API based on OpenAPI: http://localhost/api/ + +Automatic interactive documentation with Swagger UI (from the OpenAPI backend): http://localhost/docs + +Alternative automatic documentation with ReDoc (from the OpenAPI backend): http://localhost/redoc + +PGAdmin, PostgreSQL web administration: http://localhost:5050 + +Neo4j web administration: http://localhost:7474 + +Flower, administration of Celery tasks: http://localhost:5555 + +Traefik UI, to see how the routes are being handled by the proxy: http://localhost:8090 + +**Note**: The first time you start your stack, it might take a minute for it to be ready. While the backend waits for the database to be ready and configures everything. You can check the logs to monitor it. + +To check the logs, run: + +```bash +docker compose logs +``` + +To check the logs of a specific service, add the name of the service, e.g.: + +```bash +docker compose logs backend +``` + +If your Docker is not running in `localhost` (the URLs above wouldn't work) check the sections below on **Development with Docker Toolbox** and **Development with a custom IP**. + +## Backend local development, additional details + +### General workflow + +By default, the dependencies are managed with [Hatch](https://hatch.pypa.io/latest/), go there and install it. + +From `./backend/app/` you can install all the dependencies with: + +```console +$ hatch env prune +$ hatch env create production +``` + +Because Hatch doesn't have a version lock file (like Poetry), it is helpful to `prune` when you rebuild to avoid any sort of dependency hell. Then you can start a shell session with the new environment with: + +```console +$ hatch shell +``` + +Next, open your editor at `./backend/app/` (instead of the project root: `./`), so that you see an `./app/` directory with your code inside. That way, your editor will be able to find all the imports, etc. Make sure your editor uses the environment you just created with Hatch. For Visual Studio Code, from the shell, launch an appropriate development environment with: + +```console +$ code . +``` + +Modify or add SQLAlchemy models in `./backend/app/app/models/`, Pydantic schemas in `./backend/app/app/schemas/`, API endpoints in `./backend/app/app/api/`, CRUD (Create, Read, Update, Delete) utils in `./backend/app/app/crud/`. The easiest might be to copy the ones for Items (models, endpoints, and CRUD utils) and update them to your needs. + +Add and modify tasks to the Celery worker in `./backend/app/app/worker.py`. + +If you need to install any additional package to the worker, add it to the file `./backend/app/celeryworker.dockerfile`. + +### Docker Compose Override + +During development, you can change Docker Compose settings that will only affect the local development environment, in the file `docker-compose.override.yml`. + +The changes to that file only affect the local development environment, not the production environment. So, you can add "temporary" changes that help the development workflow. + +For example, the directory with the backend code is mounted as a Docker "host volume", mapping the code you change live to the directory inside the container. That allows you to test your changes right away, without having to build the Docker image again. It should only be done during development, for production, you should build the Docker image with a recent version of the backend code. But during development, it allows you to iterate very fast. Have in mind that if you have a syntax error and save the Python file, it will break and exit, and the container will stop. After that, you can restart the container by fixing the error and running again: + +```console +$ docker compose up -d +``` + +There is also a commented out `command` override, you can uncomment it and comment the default one. It makes the backend container run a process that does "nothing", but keeps the container alive. That allows you to get inside your running container and execute commands inside, for example a Python interpreter to test installed dependencies, or start the development server that reloads when it detects changes, or start a Jupyter Notebook session. + +To get inside the container with a `bash` session you can start the stack with: + +```console +$ docker compose up -d +``` + +and then `exec` inside the running container: + +```console +$ docker compose exec backend bash +``` + +You should see an output like: + +```console +root@7f2607af31c3:/app# +``` + +that means that you are in a `bash` session inside your container, as a `root` user, under the `/app` directory. + +### Backend tests + +> NOTE: Tests have not been updated on the current version, so these are likely to fail. + +To test the backend run: + +```console +$ DOMAIN=backend sh ./scripts/test.sh +``` + +The file `./scripts/test.sh` has the commands to generate a testing `docker-stack.yml` file, start the stack and test it. + +The tests run with Pytest, modify and add tests to `./backend/app/app/tests/`. + +If you use GitLab CI the tests will run automatically. + +#### Local tests + +Start the stack with this command: + +```Bash +DOMAIN=backend sh ./scripts/test-local.sh +``` +The `./backend/app` directory is mounted as a "host volume" inside the docker container (set in the file `docker-compose.dev.volumes.yml`). +You can rerun the test on live code: + +```Bash +docker compose exec backend /app/tests-start.sh +``` + +#### Test running stack + +If your stack is already up and you just want to run the tests, you can use: + +```bash +docker compose exec backend /app/tests-start.sh +``` + +That `/app/tests-start.sh` script just calls `pytest` after making sure that the rest of the stack is running. If you need to pass extra arguments to `pytest`, you can pass them to that command and they will be forwarded. + +For example, to stop on first error: + +```bash +docker compose exec backend bash /app/tests-start.sh -x +``` + +#### Test Coverage + +Because the test scripts forward arguments to `pytest`, you can enable test coverage HTML report generation by passing `--cov-report=html`. + +To run the local tests with coverage HTML reports: + +```Bash +DOMAIN=backend sh ./scripts/test-local.sh --cov-report=html +``` + +To run the tests in a running stack with coverage HTML reports: + +```bash +docker compose exec backend bash /app/tests-start.sh --cov-report=html +``` + +### Live development with Python Jupyter Notebooks + +If you know about Python [Jupyter Notebooks](http://jupyter.org/), you can take advantage of them during local development. + +The `docker-compose.override.yml` file sends a variable `env` with a value `dev` to the build process of the Docker image (during local development) and the `Dockerfile` has steps to then install and configure Jupyter inside your Docker container. + +So, you can enter into the running Docker container: + +```bash +docker compose exec backend bash +``` + +And use the environment variable `$JUPYTER` to run a Jupyter Notebook with everything configured to listen on the public port (so that you can use it from your browser). + +It will output something like: + +```console +root@73e0ec1f1ae6:/app# $JUPYTER +[I 12:02:09.975 NotebookApp] Writing notebook server cookie secret to /root/.local/share/jupyter/runtime/notebook_cookie_secret +[I 12:02:10.317 NotebookApp] Serving notebooks from local directory: /app +[I 12:02:10.317 NotebookApp] The Jupyter Notebook is running at: +[I 12:02:10.317 NotebookApp] http://(73e0ec1f1ae6 or 127.0.0.1):8888/?token=f20939a41524d021fbfc62b31be8ea4dd9232913476f4397 +[I 12:02:10.317 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). +[W 12:02:10.317 NotebookApp] No web browser found: could not locate runnable browser. +[C 12:02:10.317 NotebookApp] + + Copy/paste this URL into your browser when you connect for the first time, + to login with a token: + http://(73e0ec1f1ae6 or 127.0.0.1):8888/?token=f20939a41524d021fbfc62b31be8ea4dd9232913476f4397 +``` + +you can copy that URL and modify the "host" to be `localhost` or the domain you are using for development (e.g. `local.dockertoolbox.tiangolo.com`), in the case above, it would be, e.g.: + +``` +http://localhost:8888/token=f20939a41524d021fbfc62b31be8ea4dd9232913476f4397 +``` + + and then open it in your browser. + +You will have a full Jupyter Notebook running inside your container that has direct access to your database by the container name (`db`), etc. So, you can just run sections of your backend code directly, for example with [VS Code Python Jupyter Interactive Window](https://code.visualstudio.com/docs/python/jupyter-support-py) or [Hydrogen](https://github.com/nteract/hydrogen). + +### Migrations + +As during local development your app directory is mounted as a volume inside the container, you can also run the migrations with `alembic` commands inside the container and the migration code will be in your app directory (instead of being only inside the container). So you can add it to your git repository. + +Make sure you create a "revision" of your models and that you "upgrade" your database with that revision every time you change them. As this is what will update the tables in your database. Otherwise, your application will have errors. + +* Start an interactive session in the backend container: + +```console +$ docker compose exec backend bash +``` + +* If you created a new model in `./backend/app/app/models/`, make sure to import it in `./backend/app/app/db/base.py`, that Python module (`base.py`) that imports all the models will be used by Alembic. + +* After changing a model (for example, adding a column), inside the container, create a revision, e.g.: + +```console +$ alembic revision --autogenerate -m "Add column last_name to User model" +``` + +* Commit to the git repository the files generated in the alembic directory. + +* After creating the revision, run the migration in the database (this is what will actually change the database): + +```console +$ alembic upgrade head +``` + +If you don't want to use migrations at all, uncomment the line in the file at `./backend/app/app/db/init_db.py` with: + +```python +Base.metadata.create_all(bind=engine) +``` + +and comment the line in the file `prestart.sh` that contains: + +```console +$ alembic upgrade head +``` + +If you don't want to start with the default models and want to remove them / modify them, from the beginning, without having any previous revision, you can remove the revision files (`.py` Python files) under `./backend/app/alembic/versions/`. And then create a first migration as described above. + +### Development with Docker Toolbox + +If you are using **Docker Toolbox** in Windows or macOS instead of **Docker for Windows** or **Docker for Mac**, Docker will be running in a VirtualBox Virtual Machine, and it will have a local IP different than `127.0.0.1`, which is the IP address for `localhost` in your machine. + +The address of your Docker Toolbox virtual machine would probably be `192.168.99.100` (that is the default). + +As this is a common case, the domain `local.dockertoolbox.tiangolo.com` points to that (private) IP, just to help with development (actually `dockertoolbox.tiangolo.com` and all its subdomains point to that IP). That way, you can start the stack in Docker Toolbox, and use that domain for development. You will be able to open that URL in Chrome and it will communicate with your local Docker Toolbox directly as if it was a cloud server, including CORS (Cross Origin Resource Sharing). + +If you used the default CORS enabled domains while generating the project, `local.dockertoolbox.tiangolo.com` was configured to be allowed. If you didn't, you will need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file. + +To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `local.dockertoolbox.tiangolo.com`. + +After performing those steps you should be able to open: http://local.dockertoolbox.tiangolo.com and it will be server by your stack in your Docker Toolbox virtual machine. + +Check all the corresponding available URLs in the section at the end. + +### Development in `localhost` with a custom domain + +You might want to use something different than `localhost` as the domain. For example, if you are having problems with cookies that need a subdomain, and Chrome is not allowing you to use `localhost`. + +In that case, you have two options: you could use the instructions to modify your system `hosts` file with the instructions below in **Development with a custom IP** or you can just use `localhost.tiangolo.com`, it is set up to point to `localhost` (to the IP `127.0.0.1`) and all its subdomains too. And as it is an actual domain, the browsers will store the cookies you set during development, etc. + +If you used the default CORS enabled domains while generating the project, `localhost.tiangolo.com` was configured to be allowed. If you didn't, you will need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file. + +To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `localhost.tiangolo.com`. + +After performing those steps you should be able to open: http://localhost.tiangolo.com and it will be server by your stack in `localhost`. + +Check all the corresponding available URLs in the section at the end. + +### Development with a custom IP + +If you are running Docker in an IP address different than `127.0.0.1` (`localhost`) and `192.168.99.100` (the default of Docker Toolbox), you will need to perform some additional steps. That will be the case if you are running a custom Virtual Machine, a secondary Docker Toolbox or your Docker is located in a different machine in your network. + +In that case, you will need to use a fake local domain (`dev.base-project.com`) and make your computer think that the domain is is served by the custom IP (e.g. `192.168.99.150`). + +If you used the default CORS enabled domains, `dev.base-project.com` was configured to be allowed. If you want a custom one, you need to add it to the list in the variable `BACKEND_CORS_ORIGINS` in the `.env` file. + +* Open your `hosts` file with administrative privileges using a text editor: + * **Note for Windows**: If you are in Windows, open the main Windows menu, search for "notepad", right click on it, and select the option "open as Administrator" or similar. Then click the "File" menu, "Open file", go to the directory `c:\Windows\System32\Drivers\etc\`, select the option to show "All files" instead of only "Text (.txt) files", and open the `hosts` file. + * **Note for Mac and Linux**: Your `hosts` file is probably located at `/etc/hosts`, you can edit it in a terminal running `sudo nano /etc/hosts`. + +* Additional to the contents it might have, add a new line with the custom IP (e.g. `192.168.99.150`) a space character, and your fake local domain: `dev.base-project.com`. + +The new line might look like: + +``` +192.168.99.100 dev.base-project.com +``` + +* Save the file. + * **Note for Windows**: Make sure you save the file as "All files", without an extension of `.txt`. By default, Windows tries to add the extension. Make sure the file is saved as is, without extension. + +...that will make your computer think that the fake local domain is served by that custom IP, and when you open that URL in your browser, it will talk directly to your locally running server when it is asked to go to `dev.base-project.com` and think that it is a remote server while it is actually running in your computer. + +To configure it in your stack, follow the section **Change the development "domain"** below, using the domain `dev.base-project.com`. + +After performing those steps you should be able to open: http://dev.base-project.com and it will be server by your stack in `localhost`. + +Check all the corresponding available URLs in the section at the end. + +### Change the development "domain" + +If you need to use your local stack with a different domain than `localhost`, you need to make sure the domain you use points to the IP where your stack is set up. See the different ways to achieve that in the sections above (i.e. using Docker Toolbox with `local.dockertoolbox.tiangolo.com`, using `localhost.tiangolo.com` or using `dev.base-project.com`). + +To simplify your Docker Compose setup, for example, so that the API docs (Swagger UI) knows where is your API, you should let it know you are using that domain for development. You will need to edit 1 line in 2 files. + +* Open the file located at `./.env`. It would have a line like: + +``` +DOMAIN=localhost +``` + +* Change it to the domain you are going to use, e.g.: + +``` +DOMAIN=localhost.tiangolo.com +``` + +That variable will be used by the Docker Compose files. + +* Now open the file located at `./frontend/.env`. It would have a line like: + +``` +VUE_APP_DOMAIN_DEV=localhost +``` + +* Change that line to the domain you are going to use, e.g.: + +``` +VUE_APP_DOMAIN_DEV=localhost.tiangolo.com +``` + +That variable will make your frontend communicate with that domain when interacting with your backend API, when the other variable `VUE_APP_ENV` is set to `development`. + +After changing the two lines, you can re-start your stack with: + +```bash +docker compose up -d +``` + +and check all the corresponding available URLs in the section at the end. + +## Frontend development + +See the [frontend README](./frontend/README.md) for instructions. + +### Removing the frontend + +If you are developing an API-only app and want to remove the frontend, you can do it easily: + +* Remove the `./frontend` directory. +* In the `docker-compose.yml` file, remove the whole service / section `frontend`. +* In the `docker-compose.override.yml` file, remove the whole service / section `frontend`. + +Done, you have a frontend-less (api-only) app. 🔥 🚀 + +--- + +If you want, you can also remove the `FRONTEND` environment variables from: + +* `.env` +* `.gitlab-ci.yml` +* `./scripts/*.sh` + +But it would be only to clean them up, leaving them won't really have any effect either way. + +## Deployment + +You can deploy the stack to a Docker Swarm mode cluster with a main Traefik proxy, set up using the ideas from DockerSwarm.rocks, to get automatic HTTPS certificates, etc. + +And you can use CI (continuous integration) systems to do it automatically. + +But you have to configure a couple things first. + +### Traefik network + +This stack expects the public Traefik network to be named `traefik-public`, just as in the tutorials in DockerSwarm.rocks. + +If you need to use a different Traefik public network name, update it in the `docker-compose.yml` files, in the section: + +```YAML +networks: + traefik-public: + external: true +``` + +Change `traefik-public` to the name of the used Traefik network. And then update it in the file `.env`: + +```bash +TRAEFIK_PUBLIC_NETWORK=traefik-public +``` + +### Persisting Docker named volumes + +You need to make sure that each service (Docker container) that uses a volume is always deployed to the same Docker "node" in the cluster, that way it will preserve the data. Otherwise, it could be deployed to a different node each time, and each time the volume would be created in that new node before starting the service. As a result, it would look like your service was starting from scratch every time, losing all the previous data. + +That's specially important for a service running a database. But the same problem would apply if you were saving files in your main backend service (for example, if those files were uploaded by your users, or if they were created by your system). + +To solve that, you can put constraints in the services that use one or more data volumes (like databases) to make them be deployed to a Docker node with a specific label. And of course, you need to have that label assigned to one (only one) of your nodes. + +#### Adding services with volumes + +For each service that uses a volume (databases, services with uploaded files, etc) you should have a label constraint in your `docker-compose.yml` file. + +To make sure that your labels are unique per volume per stack (for example, that they are not the same for `prod` and `stag`) you should prefix them with the name of your stack and then use the same name of the volume. + +Then you need to have those constraints in your `docker-compose.yml` file for the services that need to be fixed with each volume. + +To be able to use different environments, like `prod` and `stag`, you should pass the name of the stack as an environment variable. Like: + +```bash +STACK_NAME=stag-base-project-com sh ./scripts/deploy.sh +``` + +To use and expand that environment variable inside the `docker-compose.yml` files you can add the constraints to the services like: + +```yaml +version: '3' +services: + db: + volumes: + - 'app-db-data:/var/lib/postgresql/data/pgdata' + deploy: + placement: + constraints: + - node.labels.${STACK_NAME?Variable not set}.app-db-data == true +``` + +note the `${STACK_NAME?Variable not set}`. In the script `./scripts/deploy.sh`, the `docker-compose.yml` would be converted, and saved to a file `docker-stack.yml` containing: + +```yaml +version: '3' +services: + db: + volumes: + - 'app-db-data:/var/lib/postgresql/data/pgdata' + deploy: + placement: + constraints: + - node.labels.base-project-com.app-db-data == true +``` + +**Note**: The `${STACK_NAME?Variable not set}` means "use the environment variable `STACK_NAME`, but if it is not set, show an error `Variable not set`". + +If you add more volumes to your stack, you need to make sure you add the corresponding constraints to the services that use that named volume. + +Then you have to create those labels in some nodes in your Docker Swarm mode cluster. You can use `docker-auto-labels` to do it automatically. + +#### `docker-auto-labels` + +You can use [`docker-auto-labels`](https://github.com/tiangolo/docker-auto-labels) to automatically read the placement constraint labels in your Docker stack (Docker Compose file) and assign them to a random Docker node in your Swarm mode cluster if those labels don't exist yet. + +To do that, you can install `docker-auto-labels`: + +```bash +pip install docker-auto-labels +``` + +And then run it passing your `docker-stack.yml` file as a parameter: + +```bash +docker-auto-labels docker-stack.yml +``` + +You can run that command every time you deploy, right before deploying, as it doesn't modify anything if the required labels already exist. + +#### (Optionally) adding labels manually + +If you don't want to use `docker-auto-labels` or for any reason you want to manually assign the constraint labels to specific nodes in your Docker Swarm mode cluster, you can do the following: + +* First, connect via SSH to your Docker Swarm mode cluster. + +* Then check the available nodes with: + +```console +$ docker node ls + + +// you would see an output like: + +ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS +nfa3d4df2df34as2fd34230rm * dog.example.com Ready Active Reachable +2c2sd2342asdfasd42342304e cat.example.com Ready Active Leader +c4sdf2342asdfasd4234234ii snake.example.com Ready Active Reachable +``` + +then chose a node from the list. For example, `dog.example.com`. + +* Add the label to that node. Use as label the name of the stack you are deploying followed by a dot (`.`) followed by the named volume, and as value, just `true`, e.g.: + +```bash +docker node update --label-add base-project-com.app-db-data=true dog.example.com +``` + +* Then you need to do the same for each stack version you have. For example, for staging you could do: + +```bash +docker node update --label-add stag-base-project-com.app-db-data=true cat.example.com +``` + +### Deploy to a Docker Swarm mode cluster + +There are 3 steps: + +1. **Build** your app images +2. Optionally, **push** your custom images to a Docker Registry +3. **Deploy** your stack + +--- + +Here are the steps in detail: + +1. **Build your app images** + +* Set these environment variables, right before the next command: + * `TAG=prod` + * `FRONTEND_ENV=production` +* Use the provided `scripts/build.sh` file with those environment variables: + +```bash +TAG=prod FRONTEND_ENV=production bash ./scripts/build.sh +``` + +2. **Optionally, push your images to a Docker Registry** + +**Note**: if the deployment Docker Swarm mode "cluster" has more than one server, you will have to push the images to a registry or build the images in each server, so that when each of the servers in your cluster tries to start the containers it can get the Docker images for them, pulling them from a Docker Registry or because it has them already built locally. + +If you are using a registry and pushing your images, you can omit running the previous script and instead using this one, in a single shot. + +* Set these environment variables: + * `TAG=prod` + * `FRONTEND_ENV=production` +* Use the provided `scripts/build-push.sh` file with those environment variables: + +```bash +TAG=prod FRONTEND_ENV=production bash ./scripts/build-push.sh +``` + +3. **Deploy your stack** + +* Set these environment variables: + * `DOMAIN=base-project.com` + * `TRAEFIK_TAG=base-project.com` + * `STACK_NAME=base-project-com` + * `TAG=prod` +* Use the provided `scripts/deploy.sh` file with those environment variables: + +```bash +DOMAIN=base-project.com \ +TRAEFIK_TAG=base-project.com \ +STACK_NAME=base-project-com \ +TAG=prod \ +bash ./scripts/deploy.sh +``` + +--- + +If you change your mind and, for example, want to deploy everything to a different domain, you only have to change the `DOMAIN` environment variable in the previous commands. If you wanted to add a different version / environment of your stack, like "`preproduction`", you would only have to set `TAG=preproduction` in your command and update these other environment variables accordingly. And it would all work, that way you could have different environments and deployments of the same app in the same cluster. + +#### Deployment Technical Details + +Building and pushing is done with the `docker-compose.yml` file, using the `docker-compose` command. The file `docker-compose.yml` uses the file `.env` with default environment variables. And the scripts set some additional environment variables as well. + +The deployment requires using `docker stack` instead of `docker-swarm`, and it can't read environment variables or `.env` files. Because of that, the `deploy.sh` script generates a file `docker-stack.yml` with the configurations from `docker-compose.yml` and injecting the environment variables in it. And then uses it to deploy the stack. + +You can do the process by hand based on those same scripts if you wanted. The general structure is like this: + +```bash +# Use the environment variables passed to this script, as TAG and FRONTEND_ENV +# And re-create those variables as environment variables for the next command +TAG=${TAG?Variable not set} \ +# Set the environment variable FRONTEND_ENV to the same value passed to this script with +# a default value of "production" if nothing else was passed +FRONTEND_ENV=${FRONTEND_ENV-production?Variable not set} \ +# The actual comand that does the work: docker-compose +docker compose \ +# Pass the file that should be used, setting explicitly docker-compose.yml avoids the +# default of also using docker-compose.override.yml +-f docker-compose.yml \ +# Use the docker compose sub command named "config", it just uses the docker-compose.yml +# file passed to it and prints their combined contents +# Put those contents in a file "docker-stack.yml", with ">" +config > docker-stack.yml + +# The previous only generated a docker-stack.yml file, +# but didn't do anything with it yet + +# docker-auto-labels makes sure the labels used for constraints exist in the cluster +docker-auto-labels docker-stack.yml + +# Now this command uses that same file to deploy it +docker stack deploy -c docker-stack.yml --with-registry-auth "${STACK_NAME?Variable not set}" +``` + +### Continuous Integration / Continuous Delivery + +If you use GitLab CI, the included `.gitlab-ci.yml` can automatically deploy it. You may need to update it according to your GitLab configurations. + +If you use any other CI / CD provider, you can base your deployment from that `.gitlab-ci.yml` file, as all the actual script steps are performed in `bash` scripts that you can easily re-use. + +GitLab CI is configured assuming 2 environments following GitLab flow: + +* `prod` (production) from the `production` branch. +* `stag` (staging) from the `master` branch. + +If you need to add more environments, for example, you could imagine using a client-approved `preprod` branch, you can just copy the configurations in `.gitlab-ci.yml` for `stag` and rename the corresponding variables. The Docker Compose file and environment variables are configured to support as many environments as you need, so that you only need to modify `.gitlab-ci.yml` (or whichever CI system configuration you are using). + +## Docker Compose files and env vars + +There is a main `docker-compose.yml` file with all the configurations that apply to the whole stack, it is used automatically by `docker-compose`. + +And there's also a `docker-compose.override.yml` with overrides for development, for example to mount the source code as a volume. It is used automatically by `docker-compose` to apply overrides on top of `docker-compose.yml`. + +These Docker Compose files use the `.env` file containing configurations to be injected as environment variables in the containers. + +They also use some additional configurations taken from environment variables set in the scripts before calling the `docker-compose` command. + +It is all designed to support several "stages", like development, building, testing, and deployment. Also, allowing the deployment to different environments like staging and production (and you can add more environments very easily). + +They are designed to have the minimum repetition of code and configurations, so that if you need to change something, you have to change it in the minimum amount of places. That's why files use environment variables that get auto-expanded. That way, if for example, you want to use a different domain, you can call the `docker-compose` command with a different `DOMAIN` environment variable instead of having to change the domain in several places inside the Docker Compose files. + +Also, if you want to have another deployment environment, say `preprod`, you just have to change environment variables, but you can keep using the same Docker Compose files. + +### The .env file + +The `.env` file is the one that contains all your configurations, generated keys and passwords, etc. + +Depending on your workflow, you could want to exclude it from Git, for example if your project is public. In that case, you would have to make sure to set up a way for your CI tools to obtain it while building or deploying your project. + +One way to do it could be to add each environment variable to your CI/CD system, and updating the `docker-compose.yml` file to read that specific env var instead of reading the `.env` file. + +## URLs + +These are the URLs that will be used and generated by the project. + +### Production URLs + +Production URLs, from the branch `production`. + +Frontend: https://base-project.com + +Backend: https://base-project.com/api/ + +Automatic Interactive Docs (Swagger UI): https://base-project.com/docs + +Automatic Alternative Docs (ReDoc): https://base-project.com/redoc + +PGAdmin: https://pgadmin.base-project.com + +Flower: https://flower.base-project.com + +### Staging URLs + +Staging URLs, from the branch `master`. + +Frontend: https://stag.base-project.com + +Backend: https://stag.base-project.com/api/ + +Automatic Interactive Docs (Swagger UI): https://stag.base-project.com/docs + +Automatic Alternative Docs (ReDoc): https://stag.base-project.com/redoc + +PGAdmin: https://pgadmin.stag.base-project.com + +Flower: https://flower.stag.base-project.com + +### Development URLs + +Development URLs, for local development. + +Frontend: http://localhost + +Backend: http://localhost/api/ + +Automatic Interactive Docs (Swagger UI): https://localhost/docs + +Automatic Alternative Docs (ReDoc): https://localhost/redoc + +PGAdmin: http://localhost:5050 + +Flower: http://localhost:5555 + +Traefik UI: http://localhost:8090 + +### Development with Docker Toolbox URLs + +Development URLs, for local development. + +Frontend: http://local.dockertoolbox.tiangolo.com + +Backend: http://local.dockertoolbox.tiangolo.com/api/ + +Automatic Interactive Docs (Swagger UI): https://local.dockertoolbox.tiangolo.com/docs + +Automatic Alternative Docs (ReDoc): https://local.dockertoolbox.tiangolo.com/redoc + +PGAdmin: http://local.dockertoolbox.tiangolo.com:5050 + +Flower: http://local.dockertoolbox.tiangolo.com:5555 + +Traefik UI: http://local.dockertoolbox.tiangolo.com:8090 + +### Development with a custom IP URLs + +Development URLs, for local development. + +Frontend: http://dev.base-project.com + +Backend: http://dev.base-project.com/api/ + +Automatic Interactive Docs (Swagger UI): https://dev.base-project.com/docs + +Automatic Alternative Docs (ReDoc): https://dev.base-project.com/redoc + +PGAdmin: http://dev.base-project.com:5050 + +Flower: http://dev.base-project.com:5555 + +Traefik UI: http://dev.base-project.com:8090 + +### Development in localhost with a custom domain URLs + +Development URLs, for local development. + +Frontend: http://localhost.tiangolo.com + +Backend: http://localhost.tiangolo.com/api/ + +Automatic Interactive Docs (Swagger UI): https://localhost.tiangolo.com/docs + +Automatic Alternative Docs (ReDoc): https://localhost.tiangolo.com/redoc + +PGAdmin: http://localhost.tiangolo.com:5050 + +Flower: http://localhost.tiangolo.com:5555 + +Traefik UI: http://localhost.tiangolo.com:8090 + +## Project generation and updating, or re-generating + +This project was generated using https://github.com/whythawk/full-stack-fastapi-postgresql with: + +```bash +pip install cookiecutter +cookiecutter https://github.com/whythawk/full-stack-fastapi-postgresql +``` + +You can check the variables used during generation in the file `cookiecutter-config-file.yml`. + +You can generate the project again with the same configurations used the first time. + +That would be useful if, for example, the project generator (`tiangolo/full-stack-fastapi-postgresql`) was updated and you wanted to integrate or review the changes. + +You could generate a new project with the same configurations as this one in a parallel directory. And compare the differences between the two, without having to overwrite your current code but being able to use the same variables used for your current project. + +To achieve that, the generated project includes the file `cookiecutter-config-file.yml` with the current variables used. + +You can use that file while generating a new project to reuse all those variables. + +For example, run: + +```console +$ cookiecutter --config-file ./cookiecutter-config-file.yml --output-dir ../project-copy https://github.com/whythawk/full-stack-fastapi-postgresql +``` + +That will use the file `cookiecutter-config-file.yml` in the current directory (in this project) to generate a new project inside a sibling directory `project-copy`. diff --git a/{{cookiecutter.project_slug}}/backend/app/alembic/env.py b/{{cookiecutter.project_slug}}/backend/app/alembic/env.py index 3ba3420643..d95a52b2aa 100755 --- a/{{cookiecutter.project_slug}}/backend/app/alembic/env.py +++ b/{{cookiecutter.project_slug}}/backend/app/alembic/env.py @@ -35,7 +35,7 @@ def get_url(): password = os.getenv("POSTGRES_PASSWORD", "") server = os.getenv("POSTGRES_SERVER", "db") db = os.getenv("POSTGRES_DB", "app") - return f"postgresql://{user}:{password}@{server}/{db}" + return f"postgresql+psycopg://{user}:{password}@{server}/{db}" def run_migrations_offline(): diff --git a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/8188d671489a_deeper_authentication.py b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/8188d671489a_deeper_authentication.py new file mode 100644 index 0000000000..d0e4a1994f --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/8188d671489a_deeper_authentication.py @@ -0,0 +1,40 @@ +"""Deeper authentication + +Revision ID: 8188d671489a +Revises: c4f38069dc24 +Create Date: 2023-01-01 15:31:40.986707 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '8188d671489a' +down_revision = 'c4f38069dc24' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('user', sa.Column('created', sa.DateTime(), server_default=sa.text('now()'), nullable=False)) + op.add_column('user', sa.Column('modified', sa.DateTime(), server_default=sa.text('now()'), nullable=False)) + op.add_column('user', sa.Column('totp_secret', sa.String(), nullable=True)) + op.add_column('user', sa.Column('totp_counter', sa.Integer(), nullable=True)) + op.alter_column('user', 'hashed_password', + existing_type=sa.VARCHAR(), + nullable=True) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column('user', 'hashed_password', + existing_type=sa.VARCHAR(), + nullable=False) + op.drop_column('user', 'totp_counter') + op.drop_column('user', 'totp_secret') + op.drop_column('user', 'modified') + op.drop_column('user', 'created') + # ### end Alembic commands ### diff --git a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/c4f38069dc24_first_revision.py b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/c4f38069dc24_first_revision.py new file mode 100644 index 0000000000..73f711e579 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/c4f38069dc24_first_revision.py @@ -0,0 +1,53 @@ +"""First revision + +Revision ID: c4f38069dc24 +Revises: +Create Date: 2022-12-16 08:09:54.834747 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'c4f38069dc24' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('user', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('full_name', sa.String(), nullable=True), + sa.Column('email', sa.String(), nullable=False), + sa.Column('hashed_password', sa.String(), nullable=False), + sa.Column('email_validated', sa.Boolean(), nullable=True), + sa.Column('is_active', sa.Boolean(), nullable=True), + sa.Column('is_superuser', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=True) + op.create_index(op.f('ix_user_full_name'), 'user', ['full_name'], unique=False) + op.create_index(op.f('ix_user_id'), 'user', ['id'], unique=False) + op.create_table('token', + sa.Column('token', sa.String(), nullable=False), + sa.Column('is_valid', sa.Boolean(), nullable=True), + sa.Column('authenticates_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.ForeignKeyConstraint(['authenticates_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('token') + ) + op.create_index(op.f('ix_token_token'), 'token', ['token'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_token_token'), table_name='token') + op.drop_table('token') + op.drop_index(op.f('ix_user_id'), table_name='user') + op.drop_index(op.f('ix_user_full_name'), table_name='user') + op.drop_index(op.f('ix_user_email'), table_name='user') + op.drop_table('user') + # ### end Alembic commands ### diff --git a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py deleted file mode 100644 index a43bf9d2fd..0000000000 --- a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/d4867f3a4c0a_first_revision.py +++ /dev/null @@ -1,59 +0,0 @@ -"""First revision - -Revision ID: d4867f3a4c0a -Revises: -Create Date: 2019-04-17 13:53:32.978401 - -""" -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision = "d4867f3a4c0a" -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "user", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("full_name", sa.String(), nullable=True), - sa.Column("email", sa.String(), nullable=True), - sa.Column("hashed_password", sa.String(), nullable=True), - sa.Column("is_active", sa.Boolean(), nullable=True), - sa.Column("is_superuser", sa.Boolean(), nullable=True), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_user_email"), "user", ["email"], unique=True) - op.create_index(op.f("ix_user_full_name"), "user", ["full_name"], unique=False) - op.create_index(op.f("ix_user_id"), "user", ["id"], unique=False) - op.create_table( - "item", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("title", sa.String(), nullable=True), - sa.Column("description", sa.String(), nullable=True), - sa.Column("owner_id", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(["owner_id"], ["user.id"],), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index(op.f("ix_item_description"), "item", ["description"], unique=False) - op.create_index(op.f("ix_item_id"), "item", ["id"], unique=False) - op.create_index(op.f("ix_item_title"), "item", ["title"], unique=False) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index(op.f("ix_item_title"), table_name="item") - op.drop_index(op.f("ix_item_id"), table_name="item") - op.drop_index(op.f("ix_item_description"), table_name="item") - op.drop_table("item") - op.drop_index(op.f("ix_user_id"), table_name="user") - op.drop_index(op.f("ix_user_full_name"), table_name="user") - op.drop_index(op.f("ix_user_email"), table_name="user") - op.drop_table("user") - # ### end Alembic commands ### diff --git a/{{cookiecutter.project_slug}}/backend/app/alembic/versions/fb120f8fc198_token_remove_to_invalidate.py b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/fb120f8fc198_token_remove_to_invalidate.py new file mode 100644 index 0000000000..3c0a63fb50 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/alembic/versions/fb120f8fc198_token_remove_to_invalidate.py @@ -0,0 +1,72 @@ +"""Token remove to invalidate + +Revision ID: fb120f8fc198 +Revises: 8188d671489a +Create Date: 2023-07-25 11:39:26.423122 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "fb120f8fc198" +down_revision = "8188d671489a" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column("token", "authenticates_id", + existing_type=sa.UUID(), + nullable=False) + op.drop_column("token", "is_valid") + op.alter_column("user", "created", + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False, + existing_server_default=sa.text("now()")) + op.alter_column("user", "modified", + existing_type=postgresql.TIMESTAMP(), + type_=sa.DateTime(timezone=True), + existing_nullable=False, + existing_server_default=sa.text("now()")) + op.alter_column("user", "email_validated", + existing_type=sa.BOOLEAN(), + nullable=False) + op.alter_column("user", "is_active", + existing_type=sa.BOOLEAN(), + nullable=False) + op.alter_column("user", "is_superuser", + existing_type=sa.BOOLEAN(), + nullable=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.alter_column("user", "is_superuser", + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column("user", "is_active", + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column("user", "email_validated", + existing_type=sa.BOOLEAN(), + nullable=True) + op.alter_column("user", "modified", + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False, + existing_server_default=sa.text("now()")) + op.alter_column("user", "created", + existing_type=sa.DateTime(timezone=True), + type_=postgresql.TIMESTAMP(), + existing_nullable=False, + existing_server_default=sa.text("now()")) + op.add_column("token", sa.Column("is_valid", sa.BOOLEAN(), autoincrement=False, nullable=True)) + op.alter_column("token", "authenticates_id", + existing_type=sa.UUID(), + nullable=True) + # ### end Alembic commands ### diff --git a/{{cookiecutter.project_slug}}/backend/app/app/__version__.py b/{{cookiecutter.project_slug}}/backend/app/app/__version__.py new file mode 100644 index 0000000000..d82ddf94d3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/__version__.py @@ -0,0 +1 @@ +__version__="0.1.0" \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py index 2163017268..ca1b1176df 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/api.py @@ -1,9 +1,12 @@ from fastapi import APIRouter -from app.api.api_v1.endpoints import items, login, users, utils +from app.api.api_v1.endpoints import ( + login, + users, + proxy, +) api_router = APIRouter() -api_router.include_router(login.router, tags=["login"]) +api_router.include_router(login.router, prefix="/login", tags=["login"]) api_router.include_router(users.router, prefix="/users", tags=["users"]) -api_router.include_router(utils.router, prefix="/utils", tags=["utils"]) -api_router.include_router(items.router, prefix="/items", tags=["items"]) +api_router.include_router(proxy.router, prefix="/proxy", tags=["proxy"]) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py deleted file mode 100644 index e88885cd80..0000000000 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/items.py +++ /dev/null @@ -1,99 +0,0 @@ -from typing import Any, List - -from fastapi import APIRouter, Depends, HTTPException -from sqlalchemy.orm import Session - -from app import crud, models, schemas -from app.api import deps - -router = APIRouter() - - -@router.get("/", response_model=List[schemas.Item]) -def read_items( - db: Session = Depends(deps.get_db), - skip: int = 0, - limit: int = 100, - current_user: models.User = Depends(deps.get_current_active_user), -) -> Any: - """ - Retrieve items. - """ - if crud.user.is_superuser(current_user): - items = crud.item.get_multi(db, skip=skip, limit=limit) - else: - items = crud.item.get_multi_by_owner( - db=db, owner_id=current_user.id, skip=skip, limit=limit - ) - return items - - -@router.post("/", response_model=schemas.Item) -def create_item( - *, - db: Session = Depends(deps.get_db), - item_in: schemas.ItemCreate, - current_user: models.User = Depends(deps.get_current_active_user), -) -> Any: - """ - Create new item. - """ - item = crud.item.create_with_owner(db=db, obj_in=item_in, owner_id=current_user.id) - return item - - -@router.put("/{id}", response_model=schemas.Item) -def update_item( - *, - db: Session = Depends(deps.get_db), - id: int, - item_in: schemas.ItemUpdate, - current_user: models.User = Depends(deps.get_current_active_user), -) -> Any: - """ - Update an item. - """ - item = crud.item.get(db=db, id=id) - if not item: - raise HTTPException(status_code=404, detail="Item not found") - if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id): - raise HTTPException(status_code=400, detail="Not enough permissions") - item = crud.item.update(db=db, db_obj=item, obj_in=item_in) - return item - - -@router.get("/{id}", response_model=schemas.Item) -def read_item( - *, - db: Session = Depends(deps.get_db), - id: int, - current_user: models.User = Depends(deps.get_current_active_user), -) -> Any: - """ - Get item by ID. - """ - item = crud.item.get(db=db, id=id) - if not item: - raise HTTPException(status_code=404, detail="Item not found") - if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id): - raise HTTPException(status_code=400, detail="Not enough permissions") - return item - - -@router.delete("/{id}", response_model=schemas.Item) -def delete_item( - *, - db: Session = Depends(deps.get_db), - id: int, - current_user: models.User = Depends(deps.get_current_active_user), -) -> Any: - """ - Delete an item. - """ - item = crud.item.get(db=db, id=id) - if not item: - raise HTTPException(status_code=404, detail="Item not found") - if not crud.user.is_superuser(current_user) and (item.owner_id != current_user.id): - raise HTTPException(status_code=400, detail="Not enough permissions") - item = crud.item.remove(db=db, id=id) - return item diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py index 4dc3a9b248..b573ab6695 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/login.py @@ -1,5 +1,4 @@ -from datetime import timedelta -from typing import Any +from typing import Annotated, Any, Union from fastapi import APIRouter, Body, Depends, HTTPException from fastapi.security import OAuth2PasswordRequestForm @@ -9,88 +8,251 @@ from app.api import deps from app.core import security from app.core.config import settings -from app.core.security import get_password_hash -from app.utils import ( - generate_password_reset_token, +from app.utilities import ( send_reset_password_email, - verify_password_reset_token, + send_magic_login_email, ) router = APIRouter() +""" +https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md +Specifies minimum criteria: + - Change password must require current password verification to ensure that it's the legitimate user. + - Login page and all subsequent authenticated pages must be exclusively accessed over TLS or other strong transport. + - An application should respond with a generic error message regardless of whether: + - The user ID or password was incorrect. + - The account does not exist. + - The account is locked or disabled. + - Code should go through the same process, no matter what, allowing the application to return in approximately + the same response time. + - In the words of George Orwell, break these rules sooner than do something truly barbaric. -@router.post("/login/access-token", response_model=schemas.Token) -def login_access_token( - db: Session = Depends(deps.get_db), form_data: OAuth2PasswordRequestForm = Depends() +See `security.py` for other requirements. +""" + + +@router.post("/magic/{email}", response_model=schemas.WebToken) +def login_with_magic_link(*, db: Annotated[Session, Depends(deps.get_db)], email: str) -> Any: + """ + First step of a 'magic link' login. Check if the user exists and generate a magic link. Generates two short-duration + jwt tokens, one for validation, one for email. Creates user if not exist. + """ + user = crud.user.get_by_email(db, email=email) + if not user: + user_in = schemas.UserCreate(**{"email": email}) + user = crud.user.create(db, obj_in=user_in) + if not crud.user.is_active(user): + # Still permits a timed-attack, but does create ambiguity. + raise HTTPException(status_code=400, detail="A link to activate your account has been emailed.") + tokens = security.create_magic_tokens(subject=user.id) + if settings.emails_enabled and user.email: + # Send email with user.email as subject + send_magic_login_email(email_to=user.email, token=tokens[0]) + return {"claim": tokens[1]} + + +@router.post("/claim", response_model=schemas.Token) +def validate_magic_link( + *, + db: Annotated[Session, Depends(deps.get_db)], + obj_in: schemas.WebToken, + magic_in: Annotated[bool, Depends(deps.get_magic_token)], ) -> Any: """ - OAuth2 compatible token login, get an access token for future requests + Second step of a 'magic link' login. """ - user = crud.user.authenticate( - db, email=form_data.username, password=form_data.password + claim_in = deps.get_magic_token(token=obj_in.claim) + # Get the user + user = crud.user.get(db, id=magic_in.sub) + # Test the claims + if ( + (claim_in.sub == magic_in.sub) + or (claim_in.fingerprint != magic_in.fingerprint) + or not user + or not crud.user.is_active(user) + ): + raise HTTPException(status_code=400, detail="Login failed; invalid claim.") + # Validate that the email is the user's + if not user.email_validated: + crud.user.validate_email(db=db, db_obj=user) + # Check if totp active + refresh_token = None + force_totp = True + if not user.totp_secret: + # No TOTP, so this concludes the login validation + force_totp = False + refresh_token = security.create_refresh_token(subject=user.id) + crud.token.create(db=db, obj_in=refresh_token, user_obj=user) + return { + "access_token": security.create_access_token(subject=user.id, force_totp=force_totp), + "refresh_token": refresh_token, + "token_type": "bearer", + } + + +@router.post("/oauth", response_model=schemas.Token) +def login_with_oauth2( + db: Annotated[Session, Depends(deps.get_db)], form_data: Annotated[OAuth2PasswordRequestForm, Depends()] +) -> Any: + """ + First step with OAuth2 compatible token login, get an access token for future requests. + """ + user = crud.user.authenticate(db, email=form_data.username, password=form_data.password) + if not form_data.password or not user or not crud.user.is_active(user): + raise HTTPException(status_code=400, detail="Login failed; incorrect email or password") + # Check if totp active + refresh_token = None + force_totp = True + if not user.totp_secret: + # No TOTP, so this concludes the login validation + force_totp = False + refresh_token = security.create_refresh_token(subject=user.id) + crud.token.create(db=db, obj_in=refresh_token, user_obj=user) + return { + "access_token": security.create_access_token(subject=user.id, force_totp=force_totp), + "refresh_token": refresh_token, + "token_type": "bearer", + } + + +@router.post("/totp", response_model=schemas.Token) +def login_with_totp( + *, + db: Annotated[Session, Depends(deps.get_db)], + totp_data: schemas.WebToken, + current_user: Annotated[models.User, Depends(deps.get_totp_user)], +) -> Any: + """ + Final validation step, using TOTP. + """ + new_counter = security.verify_totp( + token=totp_data.claim, secret=current_user.totp_secret, last_counter=current_user.totp_counter ) - if not user: - raise HTTPException(status_code=400, detail="Incorrect email or password") - elif not crud.user.is_active(user): - raise HTTPException(status_code=400, detail="Inactive user") - access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) + if not new_counter: + raise HTTPException(status_code=400, detail="Login failed; unable to verify TOTP.") + # Save the new counter to prevent reuse + current_user = crud.user.update_totp_counter(db=db, db_obj=current_user, new_counter=new_counter) + refresh_token = security.create_refresh_token(subject=current_user.id) + crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user) return { - "access_token": security.create_access_token( - user.id, expires_delta=access_token_expires - ), + "access_token": security.create_access_token(subject=current_user.id), + "refresh_token": refresh_token, "token_type": "bearer", } -@router.post("/login/test-token", response_model=schemas.User) -def test_token(current_user: models.User = Depends(deps.get_current_user)) -> Any: +@router.put("/totp", response_model=schemas.Msg) +def enable_totp_authentication( + *, + db: Annotated[Session, Depends(deps.get_db)], + data_in: schemas.EnableTOTP, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], +) -> Any: """ - Test access token + For validation of token before enabling TOTP. """ - return current_user + if current_user.hashed_password: + user = crud.user.authenticate(db, email=current_user.email, password=data_in.password) + if not data_in.password or not user: + raise HTTPException(status_code=400, detail="Unable to authenticate or activate TOTP.") + totp_in = security.create_new_totp(label=current_user.email, uri=data_in.uri) + new_counter = security.verify_totp( + token=data_in.claim, secret=totp_in.secret, last_counter=current_user.totp_counter + ) + if not new_counter: + raise HTTPException(status_code=400, detail="Unable to authenticate or activate TOTP.") + # Enable TOTP and save the new counter to prevent reuse + current_user = crud.user.activate_totp(db=db, db_obj=current_user, totp_in=totp_in) + current_user = crud.user.update_totp_counter(db=db, db_obj=current_user, new_counter=new_counter) + return {"msg": "TOTP enabled. Do not lose your recovery code."} -@router.post("/password-recovery/{email}", response_model=schemas.Msg) -def recover_password(email: str, db: Session = Depends(deps.get_db)) -> Any: +@router.delete("/totp", response_model=schemas.Msg) +def disable_totp_authentication( + *, + db: Annotated[Session, Depends(deps.get_db)], + data_in: schemas.UserUpdate, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], +) -> Any: + """ + Disable TOTP. + """ + if current_user.hashed_password: + user = crud.user.authenticate(db, email=current_user.email, password=data_in.original) + if not data_in.original or not user: + raise HTTPException(status_code=400, detail="Unable to authenticate or deactivate TOTP.") + crud.user.deactivate_totp(db=db, db_obj=current_user) + return {"msg": "TOTP disabled."} + + +@router.post("/refresh", response_model=schemas.Token) +def refresh_token( + db: Annotated[Session, Depends(deps.get_db)], + current_user: Annotated[models.User, Depends(deps.get_refresh_user)], +) -> Any: + """ + Refresh tokens for future requests + """ + refresh_token = security.create_refresh_token(subject=current_user.id) + crud.token.create(db=db, obj_in=refresh_token, user_obj=current_user) + return { + "access_token": security.create_access_token(subject=current_user.id), + "refresh_token": refresh_token, + "token_type": "bearer", + } + + +@router.post("/revoke", response_model=schemas.Msg) +def revoke_token( + db: Annotated[Session, Depends(deps.get_db)], + current_user: Annotated[models.User, Depends(deps.get_refresh_user)], +) -> Any: + """ + Revoke a refresh token + """ + return {"msg": "Token revoked"} + + +@router.post("/recover/{email}", response_model=Union[schemas.WebToken, schemas.Msg]) +def recover_password(email: str, db: Annotated[Session, Depends(deps.get_db)]) -> Any: """ Password Recovery """ user = crud.user.get_by_email(db, email=email) - - if not user: - raise HTTPException( - status_code=404, - detail="The user with this username does not exist in the system.", - ) - password_reset_token = generate_password_reset_token(email=email) - send_reset_password_email( - email_to=user.email, email=email, token=password_reset_token - ) - return {"msg": "Password recovery email sent"} + if user and crud.user.is_active(user): + tokens = security.create_magic_tokens(subject=user.id) + if settings.emails_enabled: + send_reset_password_email(email_to=user.email, email=email, token=tokens[0]) + return {"claim": tokens[1]} + return {"msg": "If that login exists, we'll send you an email to reset your password."} -@router.post("/reset-password/", response_model=schemas.Msg) +@router.post("/reset", response_model=schemas.Msg) def reset_password( - token: str = Body(...), + *, + db: Annotated[Session, Depends(deps.get_db)], new_password: str = Body(...), - db: Session = Depends(deps.get_db), + claim: str = Body(...), + magic_in: Annotated[bool, Depends(deps.get_magic_token)], ) -> Any: """ Reset password """ - email = verify_password_reset_token(token) - if not email: - raise HTTPException(status_code=400, detail="Invalid token") - user = crud.user.get_by_email(db, email=email) - if not user: - raise HTTPException( - status_code=404, - detail="The user with this username does not exist in the system.", - ) - elif not crud.user.is_active(user): - raise HTTPException(status_code=400, detail="Inactive user") - hashed_password = get_password_hash(new_password) + claim_in = deps.get_magic_token(token=claim) + # Get the user + user = crud.user.get(db, id=magic_in.sub) + # Test the claims + if ( + (claim_in.sub == magic_in.sub) + or (claim_in.fingerprint != magic_in.fingerprint) + or not user + or not crud.user.is_active(user) + ): + raise HTTPException(status_code=400, detail="Password update failed; invalid claim.") + # Update the password + hashed_password = security.get_password_hash(new_password) user.hashed_password = hashed_password db.add(user) db.commit() - return {"msg": "Password updated successfully"} + return {"msg": "Password updated successfully."} diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/proxy.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/proxy.py new file mode 100644 index 0000000000..8e52065925 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/proxy.py @@ -0,0 +1,60 @@ +from typing import Annotated, Any +from pydantic import AnyHttpUrl +from fastapi import APIRouter, Depends, HTTPException, Request, Response +import httpx + +from app import models +from app.api import deps + + +router = APIRouter() + +""" +A proxy for the frontend client when hitting cors issues with axios requests. Adjust as required. This version has +a user-login dependency to reduce the risk of leaking the server as a random proxy. +""" + + +@router.post("/{path:path}") +async def proxy_post_request( + *, + path: AnyHttpUrl, + request: Request, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], +) -> Any: + # https://www.starlette.io/requests/ + # https://www.python-httpx.org/quickstart/ + # https://github.com/tiangolo/fastapi/issues/1788#issuecomment-698698884 + # https://fastapi.tiangolo.com/tutorial/path-params/#__code_13 + try: + data = await request.json() + headers = { + "Content-Type": request.headers.get("Content-Type"), + "Authorization": request.headers.get("Authorization"), + } + async with httpx.AsyncClient() as client: + proxy = await client.post(f"{path}", headers=headers, data=data) + response = Response(content=proxy.content, status_code=proxy.status_code) + return response + except Exception as e: + raise HTTPException(status_code=403, detail=str(e)) + + +@router.get("/{path:path}") +async def proxy_get_request( + *, + path: AnyHttpUrl, + request: Request, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], +) -> Any: + try: + headers = { + "Content-Type": request.headers.get("Content-Type", "application/x-www-form-urlencoded"), + "Authorization": request.headers.get("Authorization"), + } + async with httpx.AsyncClient() as client: + proxy = await client.get(f"{path}", headers=headers) + response = Response(content=proxy.content, status_code=proxy.status_code) + return response + except Exception as e: + raise HTTPException(status_code=403, detail=str(e)) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/services.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/services.py new file mode 100644 index 0000000000..f0cde93c17 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/services.py @@ -0,0 +1,18 @@ +from typing import Any + +from fastapi import APIRouter + +from app import schemas +from app.utilities import send_web_contact_email +from app.schemas import EmailContent + +router = APIRouter() + + +@router.post("/contact", response_model=schemas.Msg, status_code=201) +def send_email(*, data: EmailContent) -> Any: + """ + Standard app contact us. + """ + send_web_contact_email(data=data) + return {"msg": "Web contact email sent"} diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py index c8f89b63d8..958aba0479 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/users.py @@ -1,4 +1,4 @@ -from typing import Any, List +from typing import Annotated, Any, List from fastapi import APIRouter, Body, Depends, HTTPException from fastapi.encoders import jsonable_encoder @@ -8,77 +8,73 @@ from app import crud, models, schemas from app.api import deps from app.core.config import settings -from app.utils import send_new_account_email +from app.core import security +from app.utilities import ( + send_new_account_email, +) router = APIRouter() -@router.get("/", response_model=List[schemas.User]) -def read_users( - db: Session = Depends(deps.get_db), - skip: int = 0, - limit: int = 100, - current_user: models.User = Depends(deps.get_current_active_superuser), -) -> Any: - """ - Retrieve users. - """ - users = crud.user.get_multi(db, skip=skip, limit=limit) - return users - - @router.post("/", response_model=schemas.User) -def create_user( +def create_user_profile( *, - db: Session = Depends(deps.get_db), - user_in: schemas.UserCreate, - current_user: models.User = Depends(deps.get_current_active_superuser), + db: Annotated[Session, Depends(deps.get_db)], + password: str = Body(...), + email: EmailStr = Body(...), + full_name: str = Body(None), ) -> Any: """ - Create new user. + Create new user without the need to be logged in. """ - user = crud.user.get_by_email(db, email=user_in.email) + user = crud.user.get_by_email(db, email=email) if user: raise HTTPException( status_code=400, - detail="The user with this username already exists in the system.", + detail="This username is not available.", ) + # Create user auth + user_in = schemas.UserCreate(password=password, email=email, full_name=full_name) user = crud.user.create(db, obj_in=user_in) - if settings.EMAILS_ENABLED and user_in.email: - send_new_account_email( - email_to=user_in.email, username=user_in.email, password=user_in.password - ) return user -@router.put("/me", response_model=schemas.User) -def update_user_me( +@router.put("/", response_model=schemas.User) +def update_user( *, - db: Session = Depends(deps.get_db), - password: str = Body(None), - full_name: str = Body(None), - email: EmailStr = Body(None), - current_user: models.User = Depends(deps.get_current_active_user), + db: Annotated[Session, Depends(deps.get_db)], + obj_in: schemas.UserUpdate, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], ) -> Any: """ - Update own user. + Update user. """ + if current_user.hashed_password: + user = crud.user.authenticate(db, email=current_user.email, password=obj_in.original) + if not obj_in.original or not user: + raise HTTPException(status_code=400, detail="Unable to authenticate this update.") current_user_data = jsonable_encoder(current_user) user_in = schemas.UserUpdate(**current_user_data) - if password is not None: - user_in.password = password - if full_name is not None: - user_in.full_name = full_name - if email is not None: - user_in.email = email + if obj_in.password is not None: + user_in.password = obj_in.password + if obj_in.full_name is not None: + user_in.full_name = obj_in.full_name + if obj_in.email is not None: + check_user = crud.user.get_by_email(db, email=obj_in.email) + if check_user and check_user.email != current_user.email: + raise HTTPException( + status_code=400, + detail="This username is not available.", + ) + user_in.email = obj_in.email user = crud.user.update(db, db_obj=current_user, obj_in=user_in) return user -@router.get("/me", response_model=schemas.User) -def read_user_me( - db: Session = Depends(deps.get_db), - current_user: models.User = Depends(deps.get_current_active_user), +@router.get("/", response_model=schemas.User) +def read_user( + *, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], ) -> Any: """ Get current user. @@ -86,68 +82,77 @@ def read_user_me( return current_user -@router.post("/open", response_model=schemas.User) -def create_user_open( +@router.get("/all", response_model=List[schemas.User]) +def read_all_users( *, - db: Session = Depends(deps.get_db), - password: str = Body(...), - email: EmailStr = Body(...), - full_name: str = Body(None), + db: Annotated[Session, Depends(deps.get_db)], + page: int = 0, + current_user: Annotated[models.User, Depends(deps.get_current_active_superuser)], ) -> Any: """ - Create new user without the need to be logged in. + Retrieve all current users. """ - if not settings.USERS_OPEN_REGISTRATION: - raise HTTPException( - status_code=403, - detail="Open user registration is forbidden on this server", - ) - user = crud.user.get_by_email(db, email=email) - if user: - raise HTTPException( - status_code=400, - detail="The user with this username already exists in the system", - ) - user_in = schemas.UserCreate(password=password, email=email, full_name=full_name) - user = crud.user.create(db, obj_in=user_in) - return user + return crud.user.get_multi(db=db, page=page) + + +@router.post("/new-totp", response_model=schemas.NewTOTP) +def request_new_totp( + *, + current_user: Annotated[models.User, Depends(deps.get_current_active_user)], +) -> Any: + """ + Request new keys to enable TOTP on the user account. + """ + obj_in = security.create_new_totp(label=current_user.email) + # Remove the secret ... + obj_in.secret = None + return obj_in -@router.get("/{user_id}", response_model=schemas.User) -def read_user_by_id( - user_id: int, - current_user: models.User = Depends(deps.get_current_active_user), - db: Session = Depends(deps.get_db), +@router.post("/toggle-state", response_model=schemas.Msg) +def toggle_state( + *, + db: Annotated[Session, Depends(deps.get_db)], + user_in: schemas.UserUpdate, + current_user: Annotated[models.User, Depends(deps.get_current_active_superuser)], ) -> Any: """ - Get a specific user by id. + Toggle user state (moderator function) """ - user = crud.user.get(db, id=user_id) - if user == current_user: - return user - if not crud.user.is_superuser(current_user): + response = crud.user.toggle_user_state(db=db, obj_in=user_in) + if not response: raise HTTPException( - status_code=400, detail="The user doesn't have enough privileges" + status_code=400, + detail="Invalid request.", ) - return user + return {"msg": "User state toggled successfully."} -@router.put("/{user_id}", response_model=schemas.User) -def update_user( +@router.post("/create", response_model=schemas.User) +def create_user( *, - db: Session = Depends(deps.get_db), - user_id: int, - user_in: schemas.UserUpdate, - current_user: models.User = Depends(deps.get_current_active_superuser), + db: Annotated[Session, Depends(deps.get_db)], + user_in: schemas.UserCreate, + current_user: Annotated[models.User, Depends(deps.get_current_active_superuser)], ) -> Any: """ - Update a user. + Create new user (moderator function). """ - user = crud.user.get(db, id=user_id) - if not user: + user = crud.user.get_by_email(db, email=user_in.email) + if user: raise HTTPException( - status_code=404, - detail="The user with this username does not exist in the system", + status_code=400, + detail="The user with this username already exists in the system.", ) - user = crud.user.update(db, db_obj=user, obj_in=user_in) + user = crud.user.create(db, obj_in=user_in) + if settings.emails_enabled and user_in.email: + send_new_account_email(email_to=user_in.email, username=user_in.email, password=user_in.password) return user + + +@router.get("/tester", response_model=schemas.Msg) +def test_endpoint() -> Any: + """ + Test current endpoint. + """ + return {"msg": "Message returned ok."} diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py b/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py deleted file mode 100644 index 71fe68d0ce..0000000000 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/api_v1/endpoints/utils.py +++ /dev/null @@ -1,35 +0,0 @@ -from typing import Any - -from fastapi import APIRouter, Depends -from pydantic.networks import EmailStr - -from app import models, schemas -from app.api import deps -from app.core.celery_app import celery_app -from app.utils import send_test_email - -router = APIRouter() - - -@router.post("/test-celery/", response_model=schemas.Msg, status_code=201) -def test_celery( - msg: schemas.Msg, - current_user: models.User = Depends(deps.get_current_active_superuser), -) -> Any: - """ - Test Celery worker. - """ - celery_app.send_task("app.worker.test_celery", args=[msg.msg]) - return {"msg": "Word received"} - - -@router.post("/test-email/", response_model=schemas.Msg, status_code=201) -def test_email( - email_to: EmailStr, - current_user: models.User = Depends(deps.get_current_active_superuser), -) -> Any: - """ - Test emails. - """ - send_test_email(email_to=email_to) - return {"msg": "Test email sent"} diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/deps.py b/{{cookiecutter.project_slug}}/backend/app/app/api/deps.py index a0109afe84..d8879e741b 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/api/deps.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/deps.py @@ -1,4 +1,4 @@ -from typing import Generator +from typing import Generator, Annotated from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer @@ -7,13 +7,10 @@ from sqlalchemy.orm import Session from app import crud, models, schemas -from app.core import security from app.core.config import settings from app.db.session import SessionLocal -reusable_oauth2 = OAuth2PasswordBearer( - tokenUrl=f"{settings.API_V1_STR}/login/access-token" -) +reusable_oauth2 = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/login/oauth") def get_db() -> Generator: @@ -24,27 +21,90 @@ def get_db() -> Generator: db.close() +def get_token_payload(token: str) -> schemas.TokenPayload: + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.JWT_ALGO]) + token_data = schemas.TokenPayload(**payload) + except (jwt.JWTError, ValidationError): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Could not validate credentials", + ) + return token_data + + def get_current_user( - db: Session = Depends(get_db), token: str = Depends(reusable_oauth2) + db: Annotated[Session, Depends(get_db)], token: Annotated[str, Depends(reusable_oauth2)] ) -> models.User: - try: - payload = jwt.decode( - token, settings.SECRET_KEY, algorithms=[security.ALGORITHM] + token_data = get_token_payload(token) + if token_data.refresh or token_data.totp: + # Refresh token is not a valid access token and TOTP True can only be used to validate TOTP + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Could not validate credentials", ) - token_data = schemas.TokenPayload(**payload) + user = crud.user.get(db, id=token_data.sub) + if not user: + raise HTTPException(status_code=404, detail="User not found") + return user + + +def get_totp_user( + db: Annotated[Session, Depends(get_db)], token: Annotated[str, Depends(reusable_oauth2)] +) -> models.User: + token_data = get_token_payload(token) + if token_data.refresh or not token_data.totp: + # Refresh token is not a valid access token and TOTP False cannot be used to validate TOTP + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Could not validate credentials", + ) + user = crud.user.get(db, id=token_data.sub) + if not user: + raise HTTPException(status_code=404, detail="User not found") + return user + + +def get_magic_token(token: Annotated[str, Depends(reusable_oauth2)]) -> schemas.MagicTokenPayload: + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.JWT_ALGO]) + token_data = schemas.MagicTokenPayload(**payload) except (jwt.JWTError, ValidationError): raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Could not validate credentials", ) + return token_data + + +def get_refresh_user( + db: Annotated[Session, Depends(get_db)], token: Annotated[str, Depends(reusable_oauth2)] +) -> models.User: + token_data = get_token_payload(token) + if not token_data.refresh: + # Access token is not a valid refresh token + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Could not validate credentials", + ) user = crud.user.get(db, id=token_data.sub) if not user: raise HTTPException(status_code=404, detail="User not found") + if not crud.user.is_active(user): + raise HTTPException(status_code=400, detail="Inactive user") + # Check and revoke this refresh token + token_obj = crud.token.get(token=token, user=user) + if not token_obj: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Could not validate credentials", + ) + crud.token.remove(db, db_obj=token_obj) return user def get_current_active_user( - current_user: models.User = Depends(get_current_user), + current_user: Annotated[models.User, Depends(get_current_user)], ) -> models.User: if not crud.user.is_active(current_user): raise HTTPException(status_code=400, detail="Inactive user") @@ -52,10 +112,25 @@ def get_current_active_user( def get_current_active_superuser( - current_user: models.User = Depends(get_current_user), + current_user: Annotated[models.User, Depends(get_current_user)], ) -> models.User: if not crud.user.is_superuser(current_user): - raise HTTPException( - status_code=400, detail="The user doesn't have enough privileges" - ) + raise HTTPException(status_code=400, detail="The user doesn't have enough privileges") return current_user + + +def get_active_websocket_user(*, db: Session, token: str) -> models.User: + try: + payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.JWT_ALGO]) + token_data = schemas.TokenPayload(**payload) + except (jwt.JWTError, ValidationError): + raise ValidationError("Could not validate credentials") + if token_data.refresh: + # Refresh token is not a valid access token + raise ValidationError("Could not validate credentials") + user = crud.user.get(db, id=token_data.sub) + if not user: + raise ValidationError("User not found") + if not crud.user.is_active(user): + raise ValidationError("Inactive user") + return user diff --git a/{{cookiecutter.project_slug}}/backend/app/app/api/sockets.py b/{{cookiecutter.project_slug}}/backend/app/app/api/sockets.py new file mode 100644 index 0000000000..9e30390e90 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/api/sockets.py @@ -0,0 +1,34 @@ +from __future__ import annotations +from fastapi import WebSocket +from starlette.websockets import WebSocketDisconnect, WebSocketException + +# from websockets.exceptions import ConnectionClosedError + + +async def send_response(*, websocket: WebSocket, response: dict): + try: + await websocket.send_json(response) + return True + except (WebSocketDisconnect, WebSocketException): + return False + + +async def receive_request(*, websocket: WebSocket) -> dict: + try: + return await websocket.receive_json() + except (WebSocketDisconnect, WebSocketException): + return {} + + +def sanitize_data_request(data: any) -> any: + # Putting here for want of a better place + if isinstance(data, (list, tuple, set)): + return type(data)(sanitize_data_request(x) for x in data if x or isinstance(x, bool)) + elif isinstance(data, dict): + return type(data)( + (sanitize_data_request(k), sanitize_data_request(v)) + for k, v in data.items() + if k and v or isinstance(v, bool) + ) + else: + return data diff --git a/{{cookiecutter.project_slug}}/backend/app/app/backend_pre_start.py b/{{cookiecutter.project_slug}}/backend/app/app/backend_pre_start.py index 3363a41542..c8a08841e3 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/backend_pre_start.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/backend_pre_start.py @@ -1,6 +1,7 @@ import logging from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed +from sqlalchemy.sql import text from app.db.session import SessionLocal @@ -21,7 +22,7 @@ def init() -> None: try: db = SessionLocal() # Try to create session to check if DB is awake - db.execute("SELECT 1") + db.execute(text("SELECT 1")) except Exception as e: logger.error(e) raise e diff --git a/{{cookiecutter.project_slug}}/backend/app/app/celeryworker_pre_start.py b/{{cookiecutter.project_slug}}/backend/app/app/celeryworker_pre_start.py index 81de37134f..75f7f32f09 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/celeryworker_pre_start.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/celeryworker_pre_start.py @@ -1,6 +1,7 @@ import logging from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed +from sqlalchemy.sql import text from app.db.session import SessionLocal @@ -21,7 +22,7 @@ def init() -> None: try: # Try to create session to check if DB is awake db = SessionLocal() - db.execute("SELECT 1") + db.execute(text("SELECT 1")) except Exception as e: logger.error(e) raise e diff --git a/{{cookiecutter.project_slug}}/backend/app/app/core/celery_app.py b/{{cookiecutter.project_slug}}/backend/app/app/core/celery_app.py index 0477d14589..31fdb0f2a0 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/core/celery_app.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/core/celery_app.py @@ -2,4 +2,4 @@ celery_app = Celery("worker", broker="amqp://guest@queue//") -celery_app.conf.task_routes = {"app.worker.test_celery": "main-queue"} +celery_app.conf.task_routes = {"app.worker.*": "main-queue"} diff --git a/{{cookiecutter.project_slug}}/backend/app/app/core/config.py b/{{cookiecutter.project_slug}}/backend/app/app/core/config.py index 8b07276dac..6c4796e4cd 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/core/config.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/core/config.py @@ -1,23 +1,35 @@ import secrets -from typing import Any, Dict, List, Optional, Union +from typing import Any, List, Optional +from typing_extensions import Self -from pydantic import AnyHttpUrl, BaseSettings, EmailStr, HttpUrl, PostgresDsn, validator +from pydantic import field_validator, AnyHttpUrl, EmailStr, HttpUrl, PostgresDsn, computed_field, model_validator +from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic_core import MultiHostUrl class Settings(BaseSettings): + model_config = SettingsConfigDict( + env_file=".env", env_ignore_empty=True, extra="ignore" + ) API_V1_STR: str = "/api/v1" SECRET_KEY: str = secrets.token_urlsafe(32) + TOTP_SECRET_KEY: str = secrets.token_urlsafe(32) # 60 minutes * 24 hours * 8 days = 8 days - ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 8 + ACCESS_TOKEN_EXPIRE_SECONDS: int = 60 * 30 + REFRESH_TOKEN_EXPIRE_SECONDS: int = 60 * 60 * 24 * 30 + JWT_ALGO: str = "HS512" + TOTP_ALGO: str = "SHA-1" SERVER_NAME: str SERVER_HOST: AnyHttpUrl + SERVER_BOT: str = "Symona" # BACKEND_CORS_ORIGINS is a JSON-formatted list of origins # e.g: '["http://localhost", "http://localhost:4200", "http://localhost:3000", \ # "http://localhost:8080", "http://local.dockertoolbox.tiangolo.com"]' BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = [] - @validator("BACKEND_CORS_ORIGINS", pre=True) - def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]: + @field_validator("BACKEND_CORS_ORIGINS", mode="before") + @classmethod + def assemble_cors_origins(cls, v: Any) -> list[str] | str: if isinstance(v, str) and not v.startswith("["): return [i.strip() for i in v.split(",")] elif isinstance(v, (list, str)): @@ -27,28 +39,28 @@ def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str PROJECT_NAME: str SENTRY_DSN: Optional[HttpUrl] = None - @validator("SENTRY_DSN", pre=True) - def sentry_dsn_can_be_blank(cls, v: str) -> Optional[str]: - if len(v) == 0: - return None - return v + # GENERAL SETTINGS + + MULTI_MAX: int = 20 + + # COMPONENT SETTINGS POSTGRES_SERVER: str POSTGRES_USER: str POSTGRES_PASSWORD: str - POSTGRES_DB: str - SQLALCHEMY_DATABASE_URI: Optional[PostgresDsn] = None - - @validator("SQLALCHEMY_DATABASE_URI", pre=True) - def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any: - if isinstance(v, str): - return v - return PostgresDsn.build( - scheme="postgresql", - user=values.get("POSTGRES_USER"), - password=values.get("POSTGRES_PASSWORD"), - host=values.get("POSTGRES_SERVER"), - path=f"/{values.get('POSTGRES_DB') or ''}", + POSTGRES_PORT: int = 5432 + POSTGRES_DB: str = "" + + @computed_field # type: ignore[misc] + @property + def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn: + return MultiHostUrl.build( + scheme="postgresql+psycopg", + username=self.POSTGRES_USER, + password=self.POSTGRES_PASSWORD, + host=self.POSTGRES_SERVER, + port=self.POSTGRES_PORT, + path=self.POSTGRES_DB, ) SMTP_TLS: bool = True @@ -58,32 +70,48 @@ def assemble_db_connection(cls, v: Optional[str], values: Dict[str, Any]) -> Any SMTP_PASSWORD: Optional[str] = None EMAILS_FROM_EMAIL: Optional[EmailStr] = None EMAILS_FROM_NAME: Optional[str] = None + EMAILS_TO_EMAIL: Optional[EmailStr] = None - @validator("EMAILS_FROM_NAME") - def get_project_name(cls, v: Optional[str], values: Dict[str, Any]) -> str: - if not v: - return values["PROJECT_NAME"] - return v + @model_validator(mode="after") + def _set_default_emails_from(self) -> Self: + if not self.EMAILS_FROM_NAME: + self.EMAILS_FROM_NAME = self.PROJECT_NAME + return self EMAIL_RESET_TOKEN_EXPIRE_HOURS: int = 48 EMAIL_TEMPLATES_DIR: str = "/app/app/email-templates/build" - EMAILS_ENABLED: bool = False - @validator("EMAILS_ENABLED", pre=True) - def get_emails_enabled(cls, v: bool, values: Dict[str, Any]) -> bool: + @computed_field # type: ignore[misc] + @property + def emails_enabled(self) -> bool: return bool( - values.get("SMTP_HOST") - and values.get("SMTP_PORT") - and values.get("EMAILS_FROM_EMAIL") + self.SMTP_HOST + and self.SMTP_PORT + and self.EMAILS_FROM_EMAIL ) EMAIL_TEST_USER: EmailStr = "test@example.com" # type: ignore FIRST_SUPERUSER: EmailStr FIRST_SUPERUSER_PASSWORD: str - USERS_OPEN_REGISTRATION: bool = False - - class Config: - case_sensitive = True + USERS_OPEN_REGISTRATION: bool = True + + # NEO4J + NEO4J_FORCE_TIMEZONE: Optional[bool] = True + NEO4J_AUTO_INSTALL_LABELS: Optional[bool] = True + NEO4J_MAX_CONNECTION_POOL_SIZE: Optional[int] = 50 + NEO4J_SERVER: Optional[str] = "localhost" + NEO4J_USERNAME: str + NEO4J_PASSWORD: str + NEO4J_AUTH: str + NEO4J_BOLT: str + NEO4J_BOLT_URL: Optional[str] = None + NEO4J_SUGGESTION_LIMIT: int = 8 + NEO4J_RESULTS_LIMIT: int = 100 + + @model_validator(mode="after") + def _set_neo4j_bolt_url(self) -> Self: + self.NEO4J_BOLT_URL = f"{self.NEO4J_BOLT}://{self.NEO4J_USERNAME}:{self.NEO4J_PASSWORD}@{self.NEO4J_SERVER}:7687" + return self settings = Settings() diff --git a/{{cookiecutter.project_slug}}/backend/app/app/core/security.py b/{{cookiecutter.project_slug}}/backend/app/app/core/security.py index 6c6ee8bc30..8e1a5f6720 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/core/security.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/core/security.py @@ -1,32 +1,98 @@ from datetime import datetime, timedelta -from typing import Any, Union +from typing import Any, Union, Optional from jose import jwt from passlib.context import CryptContext +from passlib.totp import TOTP +from passlib.exc import TokenError, MalformedTokenError +import uuid from app.core.config import settings +from app.schemas import NewTOTP -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") +""" +https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md +https://passlib.readthedocs.io/en/stable/lib/passlib.hash.argon2.html +https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Password_Storage_Cheat_Sheet.md +https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/ +https://passlib.readthedocs.io/en/stable/lib/passlib.pwd.html +Specifies minimum criteria: + - Use Argon2id with a minimum configuration of 15 MiB of memory, an iteration count of 2, and 1 degree of parallelism. + - Passwords shorter than 8 characters are considered to be weak (NIST SP800-63B). + - Maximum password length of 64 prevents long password Denial of Service attacks. + - Do not silently truncate passwords. + - Allow usage of all characters including unicode and whitespace. +""" +pwd_context = CryptContext( + schemes=["argon2", "bcrypt"], deprecated="auto" +) # current defaults: $argon2id$v=19$m=65536,t=3,p=4, "bcrypt" is deprecated +totp_factory = TOTP.using(secrets={"1": settings.TOTP_SECRET_KEY}, issuer=settings.SERVER_NAME, alg=settings.TOTP_ALGO) -ALGORITHM = "HS256" +def create_access_token(*, subject: Union[str, Any], expires_delta: timedelta = None, force_totp: bool = False) -> str: + if expires_delta: + expire = datetime.now(datetime.UTC) + expires_delta + else: + expire = datetime.now(datetime.UTC) + timedelta(seconds=settings.ACCESS_TOKEN_EXPIRE_SECONDS) + to_encode = {"exp": expire, "sub": str(subject), "totp": force_totp} + encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.JWT_ALGO) + return encoded_jwt -def create_access_token( - subject: Union[str, Any], expires_delta: timedelta = None -) -> str: +def create_refresh_token(*, subject: Union[str, Any], expires_delta: timedelta = None) -> str: if expires_delta: - expire = datetime.utcnow() + expires_delta + expire = datetime.now(datetime.UTC) + expires_delta else: - expire = datetime.utcnow() + timedelta( - minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES - ) - to_encode = {"exp": expire, "sub": str(subject)} - encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM) + expire = datetime.now(datetime.UTC) + timedelta(seconds=settings.REFRESH_TOKEN_EXPIRE_SECONDS) + to_encode = {"exp": expire, "sub": str(subject), "refresh": True} + encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.JWT_ALGO) return encoded_jwt -def verify_password(plain_password: str, hashed_password: str) -> bool: +def create_magic_tokens(*, subject: Union[str, Any], expires_delta: timedelta = None) -> list[str]: + if expires_delta: + expire = datetime.now(datetime.UTC) + expires_delta + else: + expire = datetime.now(datetime.UTC) + timedelta(seconds=settings.ACCESS_TOKEN_EXPIRE_SECONDS) + fingerprint = str(uuid.uuid4()) + magic_tokens = [] + # First sub is the user.id, to be emailed. Second is the disposable id. + for sub in [subject, uuid.uuid4()]: + to_encode = {"exp": expire, "sub": str(sub), "fingerprint": fingerprint} + encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.JWT_ALGO) + magic_tokens.append(encoded_jwt) + return magic_tokens + + +def create_new_totp(*, label: str, uri: Optional[str] = None) -> NewTOTP: + if not uri: + totp = totp_factory.new() + else: + totp = totp_factory.from_source(uri) + return NewTOTP( + **{ + "secret": totp.to_json(), + "key": totp.pretty_key(), + "uri": totp.to_uri(issuer=settings.SERVER_NAME, label=label), + } + ) + + +def verify_totp(*, token: str, secret: str, last_counter: int = None) -> Union[str, bool]: + """ + token: from user + secret: totp security string from user in db + last_counter: int from user in db (may be None) + """ + try: + match = totp_factory.verify(token, secret, last_counter=last_counter) + except (MalformedTokenError, TokenError): + return False + else: + return match.counter + + +def verify_password(*, plain_password: str, hashed_password: str) -> bool: return pwd_context.verify(plain_password, hashed_password) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py index 40e2c673b4..edcbf92c06 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/__init__.py @@ -1,5 +1,6 @@ -from .crud_item import item from .crud_user import user +from .crud_token import token + # For a new basic set of CRUD operations you could just do diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/base.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/base.py index 2b6f1f10cd..7f43489da3 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/crud/base.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/base.py @@ -1,10 +1,11 @@ -from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, Union +from typing import Any, Dict, Generic, Optional, Type, TypeVar, Union from fastapi.encoders import jsonable_encoder from pydantic import BaseModel from sqlalchemy.orm import Session from app.db.base_class import Base +from app.core.config import settings ModelType = TypeVar("ModelType", bound=Base) CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) @@ -26,10 +27,13 @@ def __init__(self, model: Type[ModelType]): def get(self, db: Session, id: Any) -> Optional[ModelType]: return db.query(self.model).filter(self.model.id == id).first() - def get_multi( - self, db: Session, *, skip: int = 0, limit: int = 100 - ) -> List[ModelType]: - return db.query(self.model).offset(skip).limit(limit).all() + def get_multi(self, db: Session, *, page: int = 0, page_break: bool = False) -> list[ModelType]: + db_objs = db.query(self.model) + if not page_break: + if page > 0: + db_objs = db_objs.offset(page * settings.MULTI_MAX) + db_objs = db_objs.limit(settings.MULTI_MAX) + return db_objs.all() def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType: obj_in_data = jsonable_encoder(obj_in) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_item.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_item.py deleted file mode 100644 index dcb87cdc5c..0000000000 --- a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_item.py +++ /dev/null @@ -1,34 +0,0 @@ -from typing import List - -from fastapi.encoders import jsonable_encoder -from sqlalchemy.orm import Session - -from app.crud.base import CRUDBase -from app.models.item import Item -from app.schemas.item import ItemCreate, ItemUpdate - - -class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]): - def create_with_owner( - self, db: Session, *, obj_in: ItemCreate, owner_id: int - ) -> Item: - obj_in_data = jsonable_encoder(obj_in) - db_obj = self.model(**obj_in_data, owner_id=owner_id) - db.add(db_obj) - db.commit() - db.refresh(db_obj) - return db_obj - - def get_multi_by_owner( - self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100 - ) -> List[Item]: - return ( - db.query(self.model) - .filter(Item.owner_id == owner_id) - .offset(skip) - .limit(limit) - .all() - ) - - -item = CRUDItem(Item) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_token.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_token.py new file mode 100644 index 0000000000..e15c9cf664 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_token.py @@ -0,0 +1,35 @@ +from __future__ import annotations +from sqlalchemy.orm import Session + +from app.crud.base import CRUDBase +from app.models import User, Token +from app.schemas import RefreshTokenCreate, RefreshTokenUpdate +from app.core.config import settings + + +class CRUDToken(CRUDBase[Token, RefreshTokenCreate, RefreshTokenUpdate]): + # Everything is user-dependent + def create(self, db: Session, *, obj_in: str, user_obj: User) -> Token: + db_obj = db.query(self.model).filter(self.model.token == obj_in).first() + if db_obj and db_obj.authenticates != user_obj: + raise ValueError("Token mismatch between key and user.") + obj_in = RefreshTokenCreate(**{"token": obj_in, "authenticates_id": user_obj.id}) + return super().create(db=db, obj_in=obj_in) + + def get(self, *, user: User, token: str) -> Token: + return user.refresh_tokens.filter(self.model.token == token).first() + + def get_multi(self, *, user: User, page: int = 0, page_break: bool = False) -> list[Token]: + db_objs = user.refresh_tokens + if not page_break: + if page > 0: + db_objs = db_objs.offset(page * settings.MULTI_MAX) + db_objs = db_objs.limit(settings.MULTI_MAX) + return db_objs.all() + + def remove(self, db: Session, *, db_obj: Token) -> None: + db.delete(db_obj) + db.commit() + return None + +token = CRUDToken(Token) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py index 14525d326f..990da49cad 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/crud_user.py @@ -5,7 +5,8 @@ from app.core.security import get_password_hash, verify_password from app.crud.base import CRUDBase from app.models.user import User -from app.schemas.user import UserCreate, UserUpdate +from app.schemas.user import UserCreate, UserInDB, UserUpdate +from app.schemas.totp import NewTOTP class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]): @@ -15,7 +16,7 @@ def get_by_email(self, db: Session, *, email: str) -> Optional[User]: def create(self, db: Session, *, obj_in: UserCreate) -> User: db_obj = User( email=obj_in.email, - hashed_password=get_password_hash(obj_in.password), + hashed_password=get_password_hash(obj_in.password) if obj_in.password is not None else None, full_name=obj_in.full_name, is_superuser=obj_in.is_superuser, ) @@ -24,32 +25,70 @@ def create(self, db: Session, *, obj_in: UserCreate) -> User: db.refresh(db_obj) return db_obj - def update( - self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]] - ) -> User: + def update(self, db: Session, *, db_obj: User, obj_in: Union[UserUpdate, Dict[str, Any]]) -> User: if isinstance(obj_in, dict): update_data = obj_in else: - update_data = obj_in.dict(exclude_unset=True) - if update_data["password"]: + update_data = obj_in.model_dump(exclude_unset=True) + if update_data.get("password"): hashed_password = get_password_hash(update_data["password"]) del update_data["password"] update_data["hashed_password"] = hashed_password + if update_data.get("email") and db_obj.email != update_data["email"]: + update_data["email_validated"] = False return super().update(db, db_obj=db_obj, obj_in=update_data) def authenticate(self, db: Session, *, email: str, password: str) -> Optional[User]: user = self.get_by_email(db, email=email) if not user: return None - if not verify_password(password, user.hashed_password): + if not verify_password(plain_password=password, hashed_password=user.hashed_password): return None return user + def validate_email(self, db: Session, *, db_obj: User) -> User: + obj_in = UserUpdate(**UserInDB.model_validate(db_obj).model_dump()) + obj_in.email_validated = True + return self.update(db=db, db_obj=db_obj, obj_in=obj_in) + + def activate_totp(self, db: Session, *, db_obj: User, totp_in: NewTOTP) -> User: + obj_in = UserUpdate(**UserInDB.model_validate(db_obj).model_dump()) + obj_in = obj_in.model_dump(exclude_unset=True) + obj_in["totp_secret"] = totp_in.secret + return self.update(db=db, db_obj=db_obj, obj_in=obj_in) + + def deactivate_totp(self, db: Session, *, db_obj: User) -> User: + obj_in = UserUpdate(**UserInDB.model_validate(db_obj).model_dump()) + obj_in = obj_in.model_dump(exclude_unset=True) + obj_in["totp_secret"] = None + obj_in["totp_counter"] = None + return self.update(db=db, db_obj=db_obj, obj_in=obj_in) + + def update_totp_counter(self, db: Session, *, db_obj: User, new_counter: int) -> User: + obj_in = UserUpdate(**UserInDB.model_validate(db_obj).model_dump()) + obj_in = obj_in.model_dump(exclude_unset=True) + obj_in["totp_counter"] = new_counter + return self.update(db=db, db_obj=db_obj, obj_in=obj_in) + + def toggle_user_state(self, db: Session, *, obj_in: Union[UserUpdate, Dict[str, Any]]) -> User: + db_obj = self.get_by_email(db, email=obj_in.email) + if not db_obj: + return None + return self.update(db=db, db_obj=db_obj, obj_in=obj_in) + + def has_password(self, user: User) -> bool: + if user.hashed_password: + return True + return False + def is_active(self, user: User) -> bool: return user.is_active def is_superuser(self, user: User) -> bool: return user.is_superuser + def is_email_validated(self, user: User) -> bool: + return user.email_validated + user = CRUDUser(User) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/crud/neo_base.py b/{{cookiecutter.project_slug}}/backend/app/app/crud/neo_base.py new file mode 100644 index 0000000000..b6733392c5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/crud/neo_base.py @@ -0,0 +1,103 @@ +from typing import Generic, List, Optional, Type, TypeVar, Union +from pydantic import BaseModel +from neomodel import db, Q, One, CardinalityViolation +from uuid import UUID +import inspect +from neomodel.relationship_manager import RelationshipDefinition, ZeroOrMore + +from app.gdb import NeomodelConfig, NodeBase + +ModelType = TypeVar("ModelType", bound=NodeBase) +CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel) +UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel) + +NeomodelConfig().ready() + + +class NeoCRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): + def __init__(self, model: Type[ModelType]): + """ + Neomodel CRUD object with default methods to Create, Read, Update, Delete (CRUD). + + **Parameters** + + * `model`: A Neo4j Neomodel model class + * `schema`: A Pydantic model (schema) class + """ + self.model = model + + def _hexify(self, id: Union[UUID, str]) -> str: + if isinstance(id, str): + id = UUID(id) + return id.hex + + def get_relationships(self, db_objs: Union[ModelType, List[ModelType]]) -> Union[ModelType, List[ModelType]]: + if not db_objs: + return db_objs + is_list = isinstance(db_objs, list) + if not is_list: + db_objs = [db_objs] + for db_obj in db_objs: + # Get members of the object by getting its class type + for key, relation in inspect.getmembers(db_obj.__class__): + # an object can be accessed as a dict to subscript its attributes + relation = db_obj.__dict__.get(key) + if isinstance(relation, ZeroOrMore): + if db_obj.__dict__[key].all(): + db_obj.__dict__[key] = db_obj.__dict__[key].all() + else: + db_obj.__dict__[key] = [] + elif isinstance(relation, One): + try: + db_obj.__dict__[key] = UUID(db_obj.__dict__[key].single().identifier) + except CardinalityViolation: + db_obj.__dict__[key] = None + if not is_list: + return db_obj + return db_objs + + def _response( + self, *, db_objs: Union[ModelType, List[ModelType]], get_raw: bool = False, get_first: bool = True + ) -> Union[ModelType, List[ModelType]]: + # create_or_update is used to create multiples, so returns a list (of 1) + if get_first and isinstance(db_objs, list): + db_objs = db_objs[0] + if get_raw: + return db_objs + return self.get_relationships(db_objs) + + @db.read_transaction + def get(self, *, id: UUID, get_raw: bool = False) -> Optional[ModelType]: + id = self._hexify(id) + db_obj = self.model.nodes.get_or_none(identifier=id) + return self._response(db_objs=db_obj, get_raw=get_raw) + + @db.read_transaction + def get_multi(self, *, skip: int = 0, limit: int = 100) -> List[ModelType]: + db_objs = self.model.nodes.all()[skip : limit + skip] + return self.get_relationships(db_objs) + + @db.read_transaction + def get_multi_by_identifiers(self, *, skip: int = 0, limit: int = 100, ids_in=List[UUID]) -> List[ModelType]: + ids_in = [self._hexify(id) for id in ids_in] + db_objs = self.model.nodes.filter(Q(identifier__in=ids_in))[skip : limit + skip] + return self.get_relationships(db_objs) + + @db.write_transaction + def create(self, *, obj_in: CreateSchemaType, get_raw: bool = False, get_first: bool = True) -> ModelType: + obj_in_data = obj_in.as_neo_dict + db_obj = self.model.create_or_update(obj_in_data) # type: ignore + return self._response(db_objs=db_obj, get_raw=get_raw, get_first=get_first) + + @db.write_transaction + def update(self, *, obj_in: UpdateSchemaType, get_raw: bool = False, get_first: bool = True) -> ModelType: + obj_in_data = obj_in.as_neo_dict + db_obj = self.model.create_or_update(obj_in_data) # type: ignore + return self._response(db_objs=db_obj, get_raw=get_raw, get_first=get_first) + + @db.write_transaction + def remove(self, *, id: UUID) -> bool: + id = self._hexify(id) + obj = self.model.nodes.get(identifier=id) + obj = obj.delete() + return obj diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db/base.py b/{{cookiecutter.project_slug}}/backend/app/app/db/base.py index b7ec44fec1..cd5045031e 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/db/base.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/db/base.py @@ -1,5 +1,5 @@ # Import all the models, so that Base has them before being # imported by Alembic from app.db.base_class import Base # noqa -from app.models.item import Item # noqa from app.models.user import User # noqa +from app.models.token import Token # noqa diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db/base_class.py b/{{cookiecutter.project_slug}}/backend/app/app/db/base_class.py index 3b759110b6..85589e0eda 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/db/base_class.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/db/base_class.py @@ -1,12 +1,12 @@ from typing import Any -from sqlalchemy.ext.declarative import as_declarative, declared_attr +from sqlalchemy.orm import DeclarativeBase, declared_attr -@as_declarative() -class Base: +class Base(DeclarativeBase): id: Any __name__: str + # Generate __tablename__ automatically @declared_attr def __tablename__(cls) -> str: diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py b/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py index bc1dd2e06c..3a5db981f9 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/db/init_db.py @@ -17,6 +17,7 @@ def init_db(db: Session) -> None: user = crud.user.get_by_email(db, email=settings.FIRST_SUPERUSER) if not user: + # Create user auth user_in = schemas.UserCreate( email=settings.FIRST_SUPERUSER, password=settings.FIRST_SUPERUSER_PASSWORD, diff --git a/{{cookiecutter.project_slug}}/backend/app/app/db/session.py b/{{cookiecutter.project_slug}}/backend/app/app/db/session.py index 9edb2fa1d0..43c248aba5 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/db/session.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/db/session.py @@ -3,5 +3,5 @@ from app.core.config import settings -engine = create_engine(settings.SQLALCHEMY_DATABASE_URI, pool_pre_ping=True) +engine = create_engine(str(settings.SQLALCHEMY_DATABASE_URI), pool_pre_ping=True) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/confirm_email.html b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/confirm_email.html new file mode 100644 index 0000000000..46306169fc --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/confirm_email.html @@ -0,0 +1,24 @@ +

Confirm your email address
Hi,
You'll need to validate this email address before you can offer your work. All you need to do is click this button.
Confirm your email
Or open the following link:
If you have no idea what this is about, then don't worry. You can safely ignore and delete this email.
For any concerns or queries, just email me.
{{ server_bot }} @ {{ server_name }}
\ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/magic_login.html b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/magic_login.html new file mode 100644 index 0000000000..4c698356eb --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/magic_login.html @@ -0,0 +1,25 @@ +

Log in to {{ project_name }}
Welcome! Login to your account by clicking the button below within the next {{ valid_minutes }} minutes:
Login
Or copy the following link and paste it in your browser:

Make sure you use this code on the same device and in the same browser where you made this request or it won't work.
For any concerns or queries, especially if you didn't make this request or feel you received it by mistake, just email me.
{{ server_bot }} @ {{ server_name }}
\ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/new_account.html b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/new_account.html index 395c7bd156..a8eee9f24a 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/new_account.html +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/new_account.html @@ -1,26 +1,24 @@ -

{{ project_name }} - New Account
You have a new account:
Username: {{ username }}
Password: {{ password }}
Go to Dashboard

\ No newline at end of file +}

{{ project_name }} - New Account
Hi,
You have a new account:
Username: {{ username }}
Password: {{ password }}
Go to Dashboard
If you have no idea what this is about, then don't worry. You can safely ignore and delete this email.
For any concerns or queries, just email me.
{{ server_bot }} @ {{ server_name }}
\ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/reset_password.html b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/reset_password.html index 7fbf368b9f..22d9cfa1c6 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/reset_password.html +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/reset_password.html @@ -1,26 +1,25 @@ -

{{ project_name }} - Password Recovery
We received a request to recover the password for user {{ username }} with email {{ email }}
Reset your password by clicking the button below:
Reset Password
Or open the following link:

The reset password link / button will expire in {{ valid_hours }} hours.
If you didn't request a password recovery you can disregard this email.
\ No newline at end of file +}

{{ project_name }} - Password Recovery
We received a request to recover the password for user {{ username }} with email {{ email }}
Reset your password by clicking the button below:
Reset Password
Or open the following link:

The reset password link / button will expire in {{ valid_hours }} hours.
If you didn't request a password recovery you can disregard this email.
For any concerns or queries, just email me.
{{ server_bot }} @ {{ server_name }}
\ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/web_contact_email.html b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/web_contact_email.html new file mode 100644 index 0000000000..5b6a70568f --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/build/web_contact_email.html @@ -0,0 +1,24 @@ +

Email from: {{ email }}
{{ content }}
\ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/confirm_email.mjml b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/confirm_email.mjml new file mode 100644 index 0000000000..15660b463b --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/confirm_email.mjml @@ -0,0 +1,52 @@ + + + + + + + Confirm your email address + Hi, + You'll need to validate this email address before you can offer your + work. All you need to do is click this button. + + Confirm your email + + Or open the following link: + {{ link }} + If you have no idea what this is about, then don't worry. You can + safely ignore and delete this email. + For any concerns or queries, just email me. + {{ server_bot }} @ {{ server_name }} + + + + diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/magic_login.mjml b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/magic_login.mjml new file mode 100644 index 0000000000..f7f44ca62e --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/magic_login.mjml @@ -0,0 +1,49 @@ + + + + + + + Log in to {{ project_name }} + Welcome! Login to your account by clicking the button below within the next {{ valid_minutes }} minutes: + + Login + + Or copy the following link and paste it in your browser: + {{ link }} + + Make sure you use this code on the same device and in the same browser where you made this request or it won't work. + + For any concerns or queries, especially if you didn't make this request or feel you received it by mistake, just email me. + {{ server_bot }} @ {{ server_name }} + + + + diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/new_account.mjml b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/new_account.mjml index 16c033b078..3b9751dfe3 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/new_account.mjml +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/new_account.mjml @@ -1,15 +1,43 @@ - + - - {{ project_name }} - New Account - You have a new account: - Username: {{ username }} - Password: {{ password }} - Go to Dashboard - + + + {{ project_name }} - New Account + Hi, + You have a new account: + Username: {{ username }} + Password: {{ password }} + + Go to Dashboard + + If you have no idea what this is about, then don't worry. You can + safely ignore and delete this email. + For any concerns or queries, just email me. + {{ server_bot }} @ {{ server_name }} - + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/reset_password.mjml b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/reset_password.mjml index 4f45ea285b..9cc4ab835d 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/reset_password.mjml +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/reset_password.mjml @@ -1,18 +1,56 @@ - + - - {{ project_name }} - Password Recovery - We received a request to recover the password for user {{ username }} - with email {{ email }} - Reset your password by clicking the button below: - Reset Password - Or open the following link: - {{ link }} - - The reset password link / button will expire in {{ valid_hours }} hours. - If you didn't request a password recovery you can disregard this email. + + + {{ project_name }} - Password Recovery + We received a request to recover the password for user {{ username }} + with email {{ email }} + Reset your password by clicking the button below: + + Reset Password + + Or open the following link: + {{ link }} + + The reset password link / button will expire in {{ valid_hours }} + hours. + If you didn't request a password recovery you can disregard this + email. + For any concerns or queries, just email me. + {{ server_bot }} @ {{ server_name }} diff --git a/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/web_contact_email.mjml b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/web_contact_email.mjml new file mode 100644 index 0000000000..f335f9505b --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/email-templates/src/web_contact_email.mjml @@ -0,0 +1,20 @@ + + + + + + + Email from: {{ email }} + {{ content }} + + + + diff --git a/{{cookiecutter.project_slug}}/backend/app/app/gdb/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/gdb/__init__.py new file mode 100644 index 0000000000..e1b8adfa32 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/gdb/__init__.py @@ -0,0 +1,3 @@ +from .base_node_class import NodeBase, MetadataBase +from .neomodel_config import NeomodelConfig +from .init_gdb import init_gdb diff --git a/{{cookiecutter.project_slug}}/backend/app/app/gdb/base_edge_class.py b/{{cookiecutter.project_slug}}/backend/app/app/gdb/base_edge_class.py new file mode 100644 index 0000000000..eaacfbfac8 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/gdb/base_edge_class.py @@ -0,0 +1,12 @@ +from neomodel import ( + StructuredRel, + BooleanProperty, + DateTimeProperty, +) +from datetime import datetime +import pytz + + +class ResourceRelationship(StructuredRel): + created = DateTimeProperty(default=lambda: datetime.now(pytz.utc)) + isActive = BooleanProperty(default=True) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/gdb/base_node_class.py b/{{cookiecutter.project_slug}}/backend/app/app/gdb/base_node_class.py new file mode 100644 index 0000000000..3a9595b419 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/gdb/base_node_class.py @@ -0,0 +1,105 @@ +from neomodel import ( + StructuredNode, + StringProperty, + BooleanProperty, + UniqueIdProperty, + DateTimeProperty, +) +from neomodel.util import classproperty +from datetime import datetime +import pytz + + +class NodeBase(StructuredNode): + @property + def as_dict(self): + return { + k: v + for k, v in self.__dict__.items() + if v is not None and k in [name for name, _ in self.__all_properties__] + } + + @classproperty + def __unique_indexed_properties__(cls): + return tuple( + name + for name, property in cls.defined_properties(aliases=False, rels=False).items() + if property.unique_index + ) + + @classmethod + def _build_merge_query(cls, merge_params, update_existing=False, lazy=False, relationship=None): + """ + Get a tuple of a CYPHER query and a params dict for the specified MERGE query. + :param merge_params: The target node match parameters, each node must have a "create" key and optional "update". + :type merge_params: list of dict + :param update_existing: True to update properties of existing nodes, default False to keep existing values. + :type update_existing: bool + :rtype: tuple + """ + # Modified as per https://github.com/neo4j-contrib/neomodel/issues/575 + query_params = dict(merge_params=merge_params) + required_properties = cls.__required_properties__ + if update_existing: + required_properties = cls.__unique_indexed_properties__ + n_merge = "n:{0} {{{1}}}".format( + ":".join(cls.inherited_labels()), + ", ".join("{0}: params.create.{0}".format(getattr(cls, p).db_property or p) for p in required_properties), + ) + if relationship is None: + # create "simple" unwind query + query = "UNWIND $merge_params as params\n MERGE ({0})\n ".format(n_merge) + else: + # validate relationship + if not isinstance(relationship.source, StructuredNode): + raise ValueError("relationship source [{0}] is not a StructuredNode".format(repr(relationship.source))) + relation_type = relationship.definition.get("relation_type") + if not relation_type: + raise ValueError("No relation_type is specified on provided relationship") + + from neomodel.sync_.match import _rel_helper + + query_params["source_id"] = relationship.source.id + query = "MATCH (source:{0}) WHERE ID(source) = $source_id\n ".format(relationship.source.__label__) + query += "WITH source\n UNWIND $merge_params as params \n " + query += "MERGE " + query += _rel_helper( + lhs="source", + rhs=n_merge, + ident=None, + relation_type=relation_type, + direction=relationship.definition["direction"], + ) + + query += "ON CREATE SET n = params.create\n " + # if update_existing, write properties on match as well + if update_existing is True: + query += "ON MATCH SET n += params.update\n" + + # close query + if lazy: + query += "RETURN id(n)" + else: + query += "RETURN n" + + return query, query_params + + +class MetadataBase(NodeBase): + # Receive via API + # https://www.dublincore.org/specifications/dublin-core/dcmi-terms/#section-3 + identifier = UniqueIdProperty() + title = StringProperty(help_text="A human-readable title given to the resource.") + description = StringProperty( + help_text="A short description of the resource.", + ) + # Node-specific labels + created = DateTimeProperty(default=lambda: datetime.now(pytz.utc)) + isActive = BooleanProperty( + default=True, + help_text="Is the resource currently updated or maintained.", + ) + isPrivate = BooleanProperty( + default=True, + help_text="Is the resource private to team members with appropriate authorisation.", + ) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/gdb/init_gdb.py b/{{cookiecutter.project_slug}}/backend/app/app/gdb/init_gdb.py new file mode 100644 index 0000000000..66a22d79a9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/gdb/init_gdb.py @@ -0,0 +1,61 @@ +import inspect +from neomodel import StructuredNode, install_labels, db +from neo4j.exceptions import ClientError + +# from neomodel import config +# from app.core.config import settings + +import app.models + +# from app.gdb import NeomodelConfig + +# NeomodelConfig().ready() + +# config.DATABASE_URL = "bolt://neo4j:neo4j@127.0.0.1:7687" +# config.FORCE_TIMEZONE = settings.NEO4J_FORCE_TIMEZONE +# config.MAX_CONNECTION_POOL_SIZE = settings.NEO4J_MAX_CONNECTION_POOL_SIZE + + +def createNodeIndices(): + """Create indexes for: + Node: field_name1, field_name2 + With analyzer: StandardAnalyzer ('standard') + Update as required. + """ + indices = [ + # ("indexname1", "Node", "field_name1", "simple"), + # ("indexname2", "Node", "field_name2" , "standard"), + ] + for index, node, key, analyzer in indices: + try: + q = f"CALL db.index.fulltext.createNodeIndex('{index}',['{node}'],['{key}'], {{analyzer: '{analyzer}'}})" + db.cypher_query(q) + except ClientError: + pass + + +def dropNodeIndices(): + indices = ["indexname1", "indexname2"] + for index in indices: + try: + q = f"CALL db.index.fulltext.drop('{index}')" + db.cypher_query(q) + except ClientError: + pass + + +def init_gdb() -> None: + # Neo4j / neomodel requires nodes to be created, but labels on the nodes + # can be created at run-time without a specific migration step + # https://stackoverflow.com/questions/1796180/how-can-i-get-a-list-of-all-classes-within-current-module-in-python + for node in [ + node + for _, node in inspect.getmembers(app.models) + if inspect.isclass(node) and issubclass(node, (StructuredNode)) + ]: + try: + install_labels(node) + except ClientError as e: + if not str(e.message).lower().startswith("an equivalent constraint already exists"): + raise e + # createNodeIndices() diff --git a/{{cookiecutter.project_slug}}/backend/app/app/gdb/neomodel_config.py b/{{cookiecutter.project_slug}}/backend/app/app/gdb/neomodel_config.py new file mode 100644 index 0000000000..c16662a43e --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/gdb/neomodel_config.py @@ -0,0 +1,18 @@ +from neomodel import config + +from app.core.config import settings + + +class NeomodelConfig: + def read_settings(self) -> None: + # https://stackoverflow.com/a/64309171/295606 + # https://stackoverflow.com/a/66408057/295606 + # https://community.neo4j.com/t/troubleshooting-connection-issues-to-neo4j/129/10 + # Docker very non-obvious ... reach neo4j container by calling the container name + config.DATABASE_URL = settings.NEO4J_BOLT_URL + config.FORCE_TIMEZONE = settings.NEO4J_FORCE_TIMEZONE + config.AUTO_INSTALL_LABELS = settings.NEO4J_AUTO_INSTALL_LABELS + config.MAX_CONNECTION_POOL_SIZE = settings.NEO4J_MAX_CONNECTION_POOL_SIZE + + def ready(self) -> None: + self.read_settings() diff --git a/{{cookiecutter.project_slug}}/backend/app/app/initial_data.py b/{{cookiecutter.project_slug}}/backend/app/app/initial_data.py index c50646d2df..702c1174ac 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/initial_data.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/initial_data.py @@ -1,11 +1,37 @@ import logging +from pathlib import Path +import json +from passlib.totp import generate_secret +from app.gdb.init_gdb import init_gdb from app.db.init_db import init_db from app.db.session import SessionLocal +from app.gdb import NeomodelConfig +from app.core.config import settings + +from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) +max_tries = 60 * 5 # 5 minutes +wait_seconds = 1 + + +@retry( + stop=stop_after_attempt(max_tries), + wait=wait_fixed(wait_seconds), + before=before_log(logger, logging.INFO), + after=after_log(logger, logging.WARN), +) +def initNeo4j() -> None: + try: + NeomodelConfig().ready() + init_gdb() + except Exception as e: + logger.error(e) + raise e + def init() -> None: db = SessionLocal() @@ -14,6 +40,7 @@ def init() -> None: def main() -> None: logger.info("Creating initial data") + initNeo4j() init() logger.info("Initial data created") diff --git a/{{cookiecutter.project_slug}}/backend/app/app/main.py b/{{cookiecutter.project_slug}}/backend/app/app/main.py index d5d0a79493..ed4fe188b9 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/main.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/main.py @@ -3,6 +3,7 @@ from app.api.api_v1.api import api_router from app.core.config import settings +# from app.gdb import NeomodelConfig app = FastAPI( title=settings.PROJECT_NAME, openapi_url=f"{settings.API_V1_STR}/openapi.json" @@ -12,10 +13,13 @@ if settings.BACKEND_CORS_ORIGINS: app.add_middleware( CORSMiddleware, - allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS], + allow_origins=[str(origin).strip("/") for origin in settings.BACKEND_CORS_ORIGINS], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) app.include_router(api_router, prefix=settings.API_V1_STR) + +# nmc = NeomodelConfig() +# nmc.ready() diff --git a/{{cookiecutter.project_slug}}/backend/app/app/models/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/models/__init__.py index a9c6bdb1ef..c81b276470 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/models/__init__.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/models/__init__.py @@ -1,2 +1,2 @@ -from .item import Item from .user import User +from .token import Token diff --git a/{{cookiecutter.project_slug}}/backend/app/app/models/item.py b/{{cookiecutter.project_slug}}/backend/app/app/models/item.py deleted file mode 100755 index 205535e4b4..0000000000 --- a/{{cookiecutter.project_slug}}/backend/app/app/models/item.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import TYPE_CHECKING - -from sqlalchemy import Column, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from app.db.base_class import Base - -if TYPE_CHECKING: - from .user import User # noqa: F401 - - -class Item(Base): - id = Column(Integer, primary_key=True, index=True) - title = Column(String, index=True) - description = Column(String, index=True) - owner_id = Column(Integer, ForeignKey("user.id")) - owner = relationship("User", back_populates="items") diff --git a/{{cookiecutter.project_slug}}/backend/app/app/models/token.py b/{{cookiecutter.project_slug}}/backend/app/app/models/token.py new file mode 100644 index 0000000000..7f0b68dd6c --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/models/token.py @@ -0,0 +1,16 @@ +from __future__ import annotations +from typing import TYPE_CHECKING +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy import ForeignKey +from sqlalchemy.dialects.postgresql import UUID + +from app.db.base_class import Base + +if TYPE_CHECKING: + from .user import User # noqa: F401 + + +class Token(Base): + token: Mapped[str] = mapped_column(primary_key=True, index=True) + authenticates_id: Mapped[UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("user.id")) + authenticates: Mapped["User"] = relationship(back_populates="refresh_tokens") diff --git a/{{cookiecutter.project_slug}}/backend/app/app/models/user.py b/{{cookiecutter.project_slug}}/backend/app/app/models/user.py index 1e8e1a011b..8ae94ada37 100755 --- a/{{cookiecutter.project_slug}}/backend/app/app/models/user.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/models/user.py @@ -1,19 +1,37 @@ -from typing import TYPE_CHECKING - -from sqlalchemy import Boolean, Column, Integer, String -from sqlalchemy.orm import relationship +from __future__ import annotations +from typing import TYPE_CHECKING, Optional +from datetime import datetime +from sqlalchemy.orm import Mapped, mapped_column, relationship +from sqlalchemy import DateTime +from sqlalchemy.sql import func +from sqlalchemy.dialects.postgresql import UUID +from uuid import uuid4 from app.db.base_class import Base if TYPE_CHECKING: - from .item import Item # noqa: F401 + from . import Token # noqa: F401 class User(Base): - id = Column(Integer, primary_key=True, index=True) - full_name = Column(String, index=True) - email = Column(String, unique=True, index=True, nullable=False) - hashed_password = Column(String, nullable=False) - is_active = Column(Boolean(), default=True) - is_superuser = Column(Boolean(), default=False) - items = relationship("Item", back_populates="owner") + id: Mapped[UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, index=True, default=uuid4) + created: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False) + modified: Mapped[datetime] = mapped_column( + DateTime(timezone=True), + server_default=func.now(), + server_onupdate=func.now(), + nullable=False, + ) + # METADATA + full_name: Mapped[str] = mapped_column(index=True, nullable=True) + email: Mapped[str] = mapped_column(unique=True, index=True, nullable=False) + hashed_password: Mapped[Optional[str]] = mapped_column(nullable=True) + # AUTHENTICATION AND PERSISTENCE + totp_secret: Mapped[Optional[str]] = mapped_column(nullable=True) + totp_counter: Mapped[Optional[int]] = mapped_column(nullable=True) + email_validated: Mapped[bool] = mapped_column(default=False) + is_active: Mapped[bool] = mapped_column(default=True) + is_superuser: Mapped[bool] = mapped_column(default=False) + refresh_tokens: Mapped[list["Token"]] = relationship( + foreign_keys="[Token.authenticates_id]", back_populates="authenticates", lazy="dynamic" + ) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schema_types/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/schema_types/__init__.py new file mode 100644 index 0000000000..54d989e4b5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/schema_types/__init__.py @@ -0,0 +1 @@ +from .base_type import BaseEnum diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schema_types/base_type.py b/{{cookiecutter.project_slug}}/backend/app/app/schema_types/base_type.py new file mode 100644 index 0000000000..39b43c8188 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/schema_types/base_type.py @@ -0,0 +1,25 @@ +from enum import Enum + + +class BaseEnum(str, Enum): + # noinspection PyMethodParameters + # cf https://gitter.im/tiangolo/fastapi?at=5d775f4050508949d30b6eec + def _generate_next_value_(name, start, count, last_values) -> str: # type: ignore + """ + Uses the name as the automatic value, rather than an integer + + See https://docs.python.org/3/library/enum.html#using-automatic-values for reference + """ + return name + + @classmethod + def as_dict(cls): + member_dict = {role: member.value for role, member in cls.__members__.items()} + return member_dict + + @classmethod + def _missing_(cls, value): + # https://stackoverflow.com/a/68311691/295606 + for member in cls: + if member.value.upper() == value.upper(): + return member diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/__init__.py index 6b41593dbb..395024f23d 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/schemas/__init__.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/schemas/__init__.py @@ -1,4 +1,14 @@ -from .item import Item, ItemCreate, ItemInDB, ItemUpdate +from .base_schema import BaseSchema, MetadataBaseSchema, MetadataBaseCreate, MetadataBaseUpdate, MetadataBaseInDBBase from .msg import Msg -from .token import Token, TokenPayload -from .user import User, UserCreate, UserInDB, UserUpdate +from .token import ( + RefreshTokenCreate, + RefreshTokenUpdate, + RefreshToken, + Token, + TokenPayload, + MagicTokenPayload, + WebToken, +) +from .user import User, UserCreate, UserInDB, UserUpdate, UserLogin +from .emails import EmailContent, EmailValidation +from .totp import NewTOTP, EnableTOTP diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/base_schema.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/base_schema.py new file mode 100644 index 0000000000..5950c91f5f --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/schemas/base_schema.py @@ -0,0 +1,68 @@ +from __future__ import annotations +from pydantic import ConfigDict, BaseModel, Field +from typing import Optional +from uuid import UUID +from datetime import date, datetime +import json + +from app.schema_types import BaseEnum + + +class BaseSchema(BaseModel): + @property + def as_db_dict(self): + to_db = self.model_dump(exclude_defaults=True, exclude_none=True, exclude={"identifier, id"}) + for key in ["id", "identifier"]: + if key in self.model_dump().keys(): + to_db[key] = self.model_dump()[key].hex + return to_db + + @property + def as_neo_dict(self): + to_db = self.json(exclude_defaults=True, exclude_none=True, exclude={"identifier, id"}) + to_db = json.loads(to_db) + self_dict = self.dict() + for key in self_dict.keys(): + if isinstance(self_dict[key], BaseEnum): + # Uppercase the Enum values + to_db[key] = to_db[key].upper() + if isinstance(self_dict[key], datetime): + to_db[key] = datetime.fromisoformat(to_db[key]) + if isinstance(self_dict[key], date): + to_db[key] = date.fromisoformat(to_db[key]) + if key in ["id", "identifier"]: + to_db[key] = self_dict[key].hex + return to_db + + +class MetadataBaseSchema(BaseSchema): + # Receive via API + # https://www.dublincore.org/specifications/dublin-core/dcmi-terms/#section-3 + title: Optional[str] = Field(None, description="A human-readable title given to the resource.") + description: Optional[str] = Field( + None, + description="A short description of the resource.", + ) + isActive: Optional[bool] = Field(default=True, description="Whether the resource is still actively maintained.") + isPrivate: Optional[bool] = Field( + default=True, description="Whether the resource is private to team members with appropriate authorisation." + ) + + +class MetadataBaseCreate(MetadataBaseSchema): + pass + + +class MetadataBaseUpdate(MetadataBaseSchema): + identifier: UUID = Field(..., description="Automatically generated unique identity for the resource.") + + +class MetadataBaseInDBBase(MetadataBaseSchema): + # Identifier managed programmatically + identifier: UUID = Field(..., description="Automatically generated unique identity for the resource.") + created: date = Field(..., description="Automatically generated date resource was created.") + isActive: bool = Field(..., description="Whether the resource is still actively maintained.") + isPrivate: bool = Field( + ..., description="Whether the resource is private to team members with appropriate authorisation." + ) + model_config = ConfigDict(from_attributes=True) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/emails.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/emails.py new file mode 100644 index 0000000000..7d365c1dbe --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/schemas/emails.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel, EmailStr + + +class EmailContent(BaseModel): + email: EmailStr + subject: str + content: str + + +class EmailValidation(BaseModel): + email: EmailStr + subject: str + token: str diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/item.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/item.py deleted file mode 100644 index ac992cf2b1..0000000000 --- a/{{cookiecutter.project_slug}}/backend/app/app/schemas/item.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel - - -# Shared properties -class ItemBase(BaseModel): - title: Optional[str] = None - description: Optional[str] = None - - -# Properties to receive on item creation -class ItemCreate(ItemBase): - title: str - - -# Properties to receive on item update -class ItemUpdate(ItemBase): - pass - - -# Properties shared by models stored in DB -class ItemInDBBase(ItemBase): - id: int - title: str - owner_id: int - - class Config: - orm_mode = True - - -# Properties to return to client -class Item(ItemInDBBase): - pass - - -# Properties properties stored in DB -class ItemInDB(ItemInDBBase): - pass diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/token.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/token.py index ea85b460da..c3a8023121 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/schemas/token.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/schemas/token.py @@ -1,12 +1,41 @@ from typing import Optional +from pydantic import ConfigDict, BaseModel +from uuid import UUID -from pydantic import BaseModel + +class RefreshTokenBase(BaseModel): + token: str + authenticates_id: Optional[UUID] = None + + +class RefreshTokenCreate(RefreshTokenBase): + authenticates_id: UUID + + +class RefreshTokenUpdate(RefreshTokenBase): + pass + + +class RefreshToken(RefreshTokenUpdate): + model_config = ConfigDict(from_attributes=True) class Token(BaseModel): access_token: str + refresh_token: Optional[str] = None token_type: str class TokenPayload(BaseModel): - sub: Optional[int] = None + sub: Optional[UUID] = None + refresh: Optional[bool] = False + totp: Optional[bool] = False + + +class MagicTokenPayload(BaseModel): + sub: Optional[UUID] = None + fingerprint: Optional[UUID] = None + + +class WebToken(BaseModel): + claim: str diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/totp.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/totp.py new file mode 100644 index 0000000000..a78e292b1f --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/schemas/totp.py @@ -0,0 +1,14 @@ +from typing import Optional +from pydantic import BaseModel + + +class NewTOTP(BaseModel): + secret: Optional[str] = None + key: str + uri: str + + +class EnableTOTP(BaseModel): + claim: str + uri: str + password: Optional[str] = None diff --git a/{{cookiecutter.project_slug}}/backend/app/app/schemas/user.py b/{{cookiecutter.project_slug}}/backend/app/app/schemas/user.py index 7f5c85ac68..a1e85bf82c 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/schemas/user.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/schemas/user.py @@ -1,39 +1,63 @@ from typing import Optional +from uuid import UUID +from pydantic import field_validator, StringConstraints, ConfigDict, BaseModel, Field, EmailStr +from typing_extensions import Annotated -from pydantic import BaseModel, EmailStr + +class UserLogin(BaseModel): + username: str + password: str # Shared properties class UserBase(BaseModel): email: Optional[EmailStr] = None + email_validated: Optional[bool] = False is_active: Optional[bool] = True - is_superuser: bool = False + is_superuser: Optional[bool] = False full_name: Optional[str] = None # Properties to receive via API on creation class UserCreate(UserBase): email: EmailStr - password: str + password: Optional[Annotated[str, StringConstraints(min_length=8, max_length=64)]] = None # Properties to receive via API on update class UserUpdate(UserBase): - password: Optional[str] = None + original: Optional[Annotated[str, StringConstraints(min_length=8, max_length=64)]] = None + password: Optional[Annotated[str, StringConstraints(min_length=8, max_length=64)]] = None class UserInDBBase(UserBase): - id: Optional[int] = None - - class Config: - orm_mode = True + id: Optional[UUID] = None + model_config = ConfigDict(from_attributes=True) # Additional properties to return via API class User(UserInDBBase): - pass + hashed_password: bool = Field(default=False, alias="password") + totp_secret: bool = Field(default=False, alias="totp") + model_config = ConfigDict(populate_by_name=True) + + @field_validator("hashed_password", mode="before") + @classmethod + def evaluate_hashed_password(cls, hashed_password): + if hashed_password: + return True + return False + + @field_validator("totp_secret", mode="before") + @classmethod + def evaluate_totp_secret(cls, totp_secret): + if totp_secret: + return True + return False # Additional properties stored in DB class UserInDB(UserInDBBase): - hashed_password: str + hashed_password: Optional[str] = None + totp_secret: Optional[str] = None + totp_counter: Optional[int] = None diff --git a/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py b/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py index 81de37134f..189c9b573d 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/tests_pre_start.py @@ -1,6 +1,7 @@ import logging from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed +from sqlalchemy.sql import text from app.db.session import SessionLocal @@ -21,7 +22,7 @@ def init() -> None: try: # Try to create session to check if DB is awake db = SessionLocal() - db.execute("SELECT 1") + db.execute(text("SELECT 1")) except Exception as e: logger.error(e) raise e @@ -34,4 +35,4 @@ def main() -> None: if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/app/app/utilities/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/utilities/__init__.py new file mode 100644 index 0000000000..29fb494237 --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/utilities/__init__.py @@ -0,0 +1,9 @@ +from .email import ( + send_email, + send_test_email, + send_web_contact_email, + send_magic_login_email, + send_reset_password_email, + send_new_account_email, + send_email_validation_email, +) diff --git a/{{cookiecutter.project_slug}}/backend/app/app/utils.py b/{{cookiecutter.project_slug}}/backend/app/app/utilities/email.py similarity index 56% rename from {{cookiecutter.project_slug}}/backend/app/app/utils.py rename to {{cookiecutter.project_slug}}/backend/app/app/utilities/email.py index b1aba6bc00..f40fa0f4f2 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/utils.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/utilities/email.py @@ -1,13 +1,12 @@ import logging -from datetime import datetime, timedelta from pathlib import Path -from typing import Any, Dict, Optional +from typing import Any, Dict import emails from emails.template import JinjaTemplate -from jose import jwt from app.core.config import settings +from app.schemas import EmailContent, EmailValidation def send_email( @@ -16,7 +15,7 @@ def send_email( html_template: str = "", environment: Dict[str, Any] = {}, ) -> None: - assert settings.EMAILS_ENABLED, "no provided configuration for email variables" + assert settings.emails_enabled, "no provided configuration for email variables" message = emails.Message( subject=JinjaTemplate(subject_template), html=JinjaTemplate(html_template), @@ -24,15 +23,46 @@ def send_email( ) smtp_options = {"host": settings.SMTP_HOST, "port": settings.SMTP_PORT} if settings.SMTP_TLS: - smtp_options["tls"] = True + # https://python-emails.readthedocs.io/en/latest/ + smtp_options["ssl"] = True if settings.SMTP_USER: smtp_options["user"] = settings.SMTP_USER if settings.SMTP_PASSWORD: smtp_options["password"] = settings.SMTP_PASSWORD + # Add common template environment elements + environment["server_host"] = settings.SERVER_HOST + environment["server_name"] = settings.SERVER_NAME + environment["server_bot"] = settings.SERVER_BOT response = message.send(to=email_to, render=environment, smtp=smtp_options) logging.info(f"send email result: {response}") +def send_email_validation_email(data: EmailValidation) -> None: + subject = f"{settings.PROJECT_NAME} - {data.subject}" + server_host = settings.SERVER_HOST + link = f"{server_host}?token={data.token}" + with open(Path(settings.EMAIL_TEMPLATES_DIR) / "confirm_email.html") as f: + template_str = f.read() + send_email( + email_to=data.email, + subject_template=subject, + html_template=template_str, + environment={"link": link}, + ) + + +def send_web_contact_email(data: EmailContent) -> None: + subject = f"{settings.PROJECT_NAME} - {data.subject}" + with open(Path(settings.EMAIL_TEMPLATES_DIR) / "web_contact_email.html") as f: + template_str = f.read() + send_email( + email_to=settings.EMAILS_TO_EMAIL, + subject_template=subject, + html_template=template_str, + environment={"content": data.content, "email": data.email}, + ) + + def send_test_email(email_to: str) -> None: project_name = settings.PROJECT_NAME subject = f"{project_name} - Test email" @@ -46,6 +76,25 @@ def send_test_email(email_to: str) -> None: ) +def send_magic_login_email(email_to: str, token: str) -> None: + project_name = settings.PROJECT_NAME + subject = f"Your {project_name} magic login" + with open(Path(settings.EMAIL_TEMPLATES_DIR) / "magic_login.html") as f: + template_str = f.read() + server_host = settings.SERVER_HOST + link = f"{server_host}?magic={token}" + send_email( + email_to=email_to, + subject_template=subject, + html_template=template_str, + environment={ + "project_name": settings.PROJECT_NAME, + "valid_minutes": int(settings.ACCESS_TOKEN_EXPIRE_SECONDS / 60), + "link": link, + }, + ) + + def send_reset_password_email(email_to: str, email: str, token: str) -> None: project_name = settings.PROJECT_NAME subject = f"{project_name} - Password recovery for user {email}" @@ -61,7 +110,7 @@ def send_reset_password_email(email_to: str, email: str, token: str) -> None: "project_name": settings.PROJECT_NAME, "username": email, "email": email_to, - "valid_hours": settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS, + "valid_hours": int(settings.ACCESS_TOKEN_EXPIRE_SECONDS / 60), "link": link, }, ) @@ -85,22 +134,3 @@ def send_new_account_email(email_to: str, username: str, password: str) -> None: "link": link, }, ) - - -def generate_password_reset_token(email: str) -> str: - delta = timedelta(hours=settings.EMAIL_RESET_TOKEN_EXPIRE_HOURS) - now = datetime.utcnow() - expires = now + delta - exp = expires.timestamp() - encoded_jwt = jwt.encode( - {"exp": exp, "nbf": now, "sub": email}, settings.SECRET_KEY, algorithm="HS256", - ) - return encoded_jwt - - -def verify_password_reset_token(token: str) -> Optional[str]: - try: - decoded_token = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"]) - return decoded_token["email"] - except jwt.JWTError: - return None diff --git a/{{cookiecutter.project_slug}}/backend/app/app/worker/__init__.py b/{{cookiecutter.project_slug}}/backend/app/app/worker/__init__.py new file mode 100644 index 0000000000..34da984e3a --- /dev/null +++ b/{{cookiecutter.project_slug}}/backend/app/app/worker/__init__.py @@ -0,0 +1,3 @@ +from app.core.celery_app import celery_app + +from .tests import test_celery diff --git a/{{cookiecutter.project_slug}}/backend/app/app/worker.py b/{{cookiecutter.project_slug}}/backend/app/app/worker/tests.py similarity index 72% rename from {{cookiecutter.project_slug}}/backend/app/app/worker.py rename to {{cookiecutter.project_slug}}/backend/app/app/worker/tests.py index 5fea53c961..4b26e81593 100644 --- a/{{cookiecutter.project_slug}}/backend/app/app/worker.py +++ b/{{cookiecutter.project_slug}}/backend/app/app/worker/tests.py @@ -1,4 +1,5 @@ from raven import Client +import asyncio from app.core.celery_app import celery_app from app.core.config import settings @@ -7,5 +8,6 @@ @celery_app.task(acks_late=True) -def test_celery(word: str) -> str: +async def test_celery(word: str) -> str: + await asyncio.sleep(5) return f"test task return {word}" diff --git a/{{cookiecutter.project_slug}}/backend/app/pyproject.toml b/{{cookiecutter.project_slug}}/backend/app/pyproject.toml index ea4e04e198..c1d5546c7e 100644 --- a/{{cookiecutter.project_slug}}/backend/app/pyproject.toml +++ b/{{cookiecutter.project_slug}}/backend/app/pyproject.toml @@ -1,46 +1,116 @@ -[tool.poetry] +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] name = "app" -version = "0.1.0" -description = "" -authors = ["Admin "] - -[tool.poetry.dependencies] -python = "^3.7" -uvicorn = "^0.11.3" -fastapi = "^0.54.1" -python-multipart = "^0.0.5" -email-validator = "^1.0.5" -requests = "^2.23.0" -celery = "^4.4.2" -passlib = {extras = ["bcrypt"], version = "^1.7.2"} -tenacity = "^6.1.0" -pydantic = "^1.4" -emails = "^0.5.15" -raven = "^6.10.0" -gunicorn = "^20.0.4" -jinja2 = "^2.11.2" -psycopg2-binary = "^2.8.5" -alembic = "^1.4.2" -sqlalchemy = "^1.3.16" -pytest = "^5.4.1" -python-jose = {extras = ["cryptography"], version = "^3.1.0"} - -[tool.poetry.dev-dependencies] -mypy = "^0.770" -black = "^19.10b0" -isort = "^4.3.21" -autoflake = "^1.3.1" -flake8 = "^3.7.9" -pytest = "^5.4.1" -sqlalchemy-stubs = "^0.3" -pytest-cov = "^2.8.1" +dynamic = ["version"] +description = '' +readme = "README.md" +requires-python = ">=3.11" +license = "MIT" +keywords = [] +authors = [ + { name = "U.N. Owen", email = "{{cookiecutter.first_superuser}}" }, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Programming Language :: Python", + "Programming Language :: Python :: 3.11", +] +dependencies = [ + "inboard[fastapi]==0.68.*", + "python-multipart>=0.0.9", + "email-validator>=1.3.0", + "requests>=2.31.0", + "celery>=5.4.0", + "passlib[bcrypt]>=1.7.4", + "tenacity>=8.2.3", + "pydantic>=2.7.1", + "pydantic-settings>=2.2.1", + "emails>=0.6.0", + "raven>=6.10.0", + "jinja2>=3.1.2", + "alembic>=1.13.1", + "sqlalchemy>=2.0.29", + "python-jose[cryptography]>=3.3.0", + "httpx>=0.27.0", + "neo4j>=5.19.0", + "neomodel>=5.3.0", + "psycopg[binary]>=3.1.18", + "setuptools>=69.5.1", + "pytest>=8.2.0", +] + +[project.optional-dependencies] +checks = [ + "black>=24.4.2", + "mypy>=1.10.0", + "isort>=5.13.2", + "autoflake>=2.3.1", + "flake8>=7.0.0", +] + +[project.urls] +Documentation = "https://github.com/unknown/app#readme" +Issues = "https://github.com/unknown/app/issues" +Source = "https://github.com/unknown/app" + +[tool.hatch.version] +path = "app/__version__.py" + +[dirs.env] +virtual = "./.venv" + +[tool.hatch.envs.default] +python="3.11" # <-- +dev-mode = true +dependencies = [] + +[tool.hatch.build.targets.sdist] +include = ["/app"] + +[tool.hatch.envs.production] +dev-mode = false +features = [] +path = ".venv" + +[tool.hatch.envs.lint] +detached = true +dependencies = [ + "black>=23.1.0", + "mypy>=1.0.0", + "isort>=5.11.2", +] +[tool.hatch.envs.lint.scripts] +style = [ + "isort --check --diff {args:.}", + "black --check --diff {args:.}", +] +fmt = [ + "black {args:.}", + "isort {args:.}", + "style", +] +all = [ + "style", + "typing", +] + +[tool.black] +target-version = ["py311"] +line-length = 120 [tool.isort] +profile = "black" multi_line_output = 3 include_trailing_comma = true force_grid_wrap = 0 -line_length = 88 -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +line_length = 120 +src_paths = ["app", "tests"] +[tool.mypy] +files = ["**/*.py"] +plugins = "pydantic.mypy" +show_error_codes = true +strict = true diff --git a/{{cookiecutter.project_slug}}/backend/app/scripts/test.sh b/{{cookiecutter.project_slug}}/backend/app/scripts/test.sh index fba8e95576..ab96c9df15 100755 --- a/{{cookiecutter.project_slug}}/backend/app/scripts/test.sh +++ b/{{cookiecutter.project_slug}}/backend/app/scripts/test.sh @@ -3,4 +3,4 @@ set -e set -x -pytest --cov=app --cov-report=term-missing app/tests "${@}" +pytest app/tests "${@}" diff --git a/{{cookiecutter.project_slug}}/backend/app/tests-start.sh b/{{cookiecutter.project_slug}}/backend/app/tests-start.sh index 099c2b3973..aaa5ca8ac0 100644 --- a/{{cookiecutter.project_slug}}/backend/app/tests-start.sh +++ b/{{cookiecutter.project_slug}}/backend/app/tests-start.sh @@ -1,6 +1,6 @@ #! /usr/bin/env bash set -e -python /app/app/tests_pre_start.py +hatch run python /app/app/tests_pre_start.py bash ./scripts/test.sh "$@" diff --git a/{{cookiecutter.project_slug}}/backend/app/worker-start.sh b/{{cookiecutter.project_slug}}/backend/app/worker-start.sh index 172f08e6f8..caa53948c4 100644 --- a/{{cookiecutter.project_slug}}/backend/app/worker-start.sh +++ b/{{cookiecutter.project_slug}}/backend/app/worker-start.sh @@ -1,6 +1,5 @@ #! /usr/bin/env bash set -e -python /app/app/celeryworker_pre_start.py - -celery worker -A app.worker -l info -Q main-queue -c 1 +hatch run python /app/app/celeryworker_pre_start.py +hatch run celery -A app.worker worker -l info -Q main-queue -c 1 diff --git a/{{cookiecutter.project_slug}}/backend/backend.dockerfile b/{{cookiecutter.project_slug}}/backend/backend.dockerfile index 8c39c502af..e73ddd13c3 100644 --- a/{{cookiecutter.project_slug}}/backend/backend.dockerfile +++ b/{{cookiecutter.project_slug}}/backend/backend.dockerfile @@ -1,25 +1,26 @@ -FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7 +FROM ghcr.io/br3ndonland/inboard:fastapi-0.68-python3.11 +# Use file.name* in case it doesn't exist in the repo +COPY ./app/ /app/ WORKDIR /app/ +ENV HATCH_ENV_TYPE_VIRTUAL_PATH=.venv +RUN hatch env prune && hatch env create production && pip install --upgrade setuptools -# Install Poetry -RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \ - cd /usr/local/bin && \ - ln -s /opt/poetry/bin/poetry && \ - poetry config virtualenvs.create false +# /start Project-specific dependencies +# RUN apt-get update && apt-get install -y --no-install-recommends \ +# && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +# WORKDIR /app/ +# /end Project-specific dependencies -# Copy poetry.lock* in case it doesn't exist in the repo -COPY ./app/pyproject.toml ./app/poetry.lock* /app/ - -# Allow installing dev dependencies to run tests -ARG INSTALL_DEV=false -RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --no-dev ; fi" - -# For development, Jupyter remote kernel, Hydrogen +# For development, Jupyter remote kernel # Using inside the container: # jupyter lab --ip=0.0.0.0 --allow-root --NotebookApp.custom_display_url=http://127.0.0.1:8888 ARG INSTALL_JUPYTER=false RUN bash -c "if [ $INSTALL_JUPYTER == 'true' ] ; then pip install jupyterlab ; fi" +RUN bash -c "pip install argon2_cffi" -COPY ./app /app -ENV PYTHONPATH=/app +ARG BACKEND_APP_MODULE=app.main:app +ARG BACKEND_PRE_START_PATH=/app/prestart.sh +ARG BACKEND_PROCESS_MANAGER=gunicorn +ARG BACKEND_WITH_RELOAD=false +ENV APP_MODULE=${BACKEND_APP_MODULE} PRE_START_PATH=${BACKEND_PRE_START_PATH} PROCESS_MANAGER=${BACKEND_PROCESS_MANAGER} WITH_RELOAD=${BACKEND_WITH_RELOAD} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile b/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile index 4695a7b7ec..4d1a8444c3 100644 --- a/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile +++ b/{{cookiecutter.project_slug}}/backend/celeryworker.dockerfile @@ -1,35 +1,35 @@ -FROM python:3.7 - +FROM python:3.11 WORKDIR /app/ - -# Install Poetry -RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \ - cd /usr/local/bin && \ - ln -s /opt/poetry/bin/poetry && \ - poetry config virtualenvs.create false - -# Copy poetry.lock* in case it doesn't exist in the repo -COPY ./app/pyproject.toml ./app/poetry.lock* /app/ - -# Allow installing dev dependencies to run tests -ARG INSTALL_DEV=false -RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --no-dev ; fi" +ARG \ + HATCH_VERSION=1.7.0 \ + PIPX_VERSION=1.2.0 +ENV \ + C_FORCE_ROOT=1 \ + HATCH_ENV_TYPE_VIRTUAL_PATH=.venv \ + HATCH_VERSION=$HATCH_VERSION \ + PATH=/opt/pipx/bin:/app/.venv/bin:$PATH \ + PIPX_BIN_DIR=/opt/pipx/bin \ + PIPX_HOME=/opt/pipx/home \ + PIPX_VERSION=$PIPX_VERSION \ + PYTHONPATH=/app +COPY ./app/ /app/ +RUN <(`${apiCore.url()}/users/tester`) + }, + // LOGIN WITH MAGIC LINK OR OAUTH2 (USERNAME/PASSWORD) + async loginWithMagicLink(email: string) { + return await useFetch(`${apiCore.url()}/login/magic/${email}`, + { + method: "POST", + } + ) + }, + async validateMagicLink(token: string, data: IWebToken) { + return await useFetch(`${apiCore.url()}/login/claim`, + { + method: "POST", + body: data, + headers: apiCore.headers(token) + } + ) + }, + async loginWithOauth(username: string, password: string) { + // Version of this: https://github.com/unjs/ofetch/issues/37#issuecomment-1262226065 + // useFetch is borked, so you'll need to ignore errors https://github.com/unjs/ofetch/issues/37 + const params = new URLSearchParams() + params.append("username", username) + params.append("password", password) + return await useFetch(`${apiCore.url()}/login/oauth`, + { + method: "POST", + body: params, + // @ts-ignore + headers: { "Content-Disposition": params } + } + ) + }, + // TOTP SETUP AND AUTHENTICATION + async loginWithTOTP(token: string, data: IWebToken) { + return await useFetch(`${apiCore.url()}/login/totp`, + { + method: "POST", + body: data, + headers: apiCore.headers(token) + } + ) + }, + async requestNewTOTP(token: string) { + return await useFetch(`${apiCore.url()}/users/new-totp`, + { + method: "POST", + headers: apiCore.headers(token) + } + ) + }, + async enableTOTPAuthentication(token: string, data: IEnableTOTP) { + return await useFetch(`${apiCore.url()}/login/totp`, + { + method: "PUT", + body: data, + headers: apiCore.headers(token) + } + ) + }, + async disableTOTPAuthentication(token: string, data: IUserProfileUpdate) { + return await useFetch(`${apiCore.url()}/login/totp`, + { + method: "DELETE", + body: data, + headers: apiCore.headers(token) + } + ) + }, + // MANAGE JWT TOKENS (REFRESH / REVOKE) + async getRefreshedToken(token: string) { + return await useFetch(`${apiCore.url()}/login/refresh`, + { + method: "POST", + headers: apiCore.headers(token) + } + ) + }, + async revokeRefreshedToken(token: string) { + return await useFetch(`${apiCore.url()}/login/revoke`, + { + method: "POST", + headers: apiCore.headers(token) + } + ) + }, + // USER PROFILE MANAGEMENT + async createProfile(data: IUserOpenProfileCreate) { + return await useFetch(`${apiCore.url()}/users/`, + { + method: "POST", + body: data, + } + ) + }, + async getProfile(token: string) { + return await useFetch(`${apiCore.url()}/users/`, + { + headers: apiCore.headers(token) + } + ) + }, + async updateProfile(token: string, data: IUserProfileUpdate) { + return await useFetch(`${apiCore.url()}/users/`, + { + method: "PUT", + body: data, + headers: apiCore.headers(token) + } + ) + }, + // ACCOUNT RECOVERY + async recoverPassword(email: string) { + return await useFetch(`${apiCore.url()}/login/recover/${email}`, + { + method: "POST", + } + ) + }, + async resetPassword(password: string, claim: string, token: string) { + return await useFetch(`${apiCore.url()}/login/reset`, + { + method: "POST", + body: { + new_password: password, + claim, + }, + headers: apiCore.headers(token) + } + ) + }, + async requestValidationEmail(token: string) { + return await useFetch(`${apiCore.url()}/users/send-validation-email`, + { + method: "POST", + headers: apiCore.headers(token) + } + ) + }, + async validateEmail(token: string, validation: string) { + return await useFetch(`${apiCore.url()}/users/validate-email`, + { + method: "POST", + body: { validation }, + headers: apiCore.headers(token) + } + ) + }, + // ADMIN USER MANAGEMENT + async getAllUsers(token: string) { + return await useFetch(`${apiCore.url()}/users/all`, + { + headers: apiCore.headers(token) + } + ) + }, + async toggleUserState(token: string, data: IUserProfileUpdate) { + return await useFetch(`${apiCore.url()}/users/toggle-state`, + { + method: "POST", + body: data, + headers: apiCore.headers(token) + } + ) + }, + async createUserProfile(token: string, data: IUserProfileCreate) { + return await useFetch(`${apiCore.url()}/users/create`, + { + method: "POST", + body: data, + headers: apiCore.headers(token) + } + ) + }, +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/api/core.ts b/{{cookiecutter.project_slug}}/frontend/api/core.ts new file mode 100644 index 0000000000..9ee9e17fca --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/api/core.ts @@ -0,0 +1,14 @@ +export const apiCore = { + url(): string { + return useRuntimeConfig().public.apiUrl + }, + // WS(): string { + // return useRuntimeConfig().public.apiWS + // }, + headers(token: string) { + return { + "Cache-Control": "no-cache", + Authorization: `Bearer ${token}` + } + } +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/api/index.ts b/{{cookiecutter.project_slug}}/frontend/api/index.ts new file mode 100644 index 0000000000..a9165df67a --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/api/index.ts @@ -0,0 +1,5 @@ +import { apiCore } from "./core" +import { apiAuth } from "./auth" +import { apiService } from "./services" + +export { apiCore, apiAuth, apiService } \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/api/services.ts b/{{cookiecutter.project_slug}}/frontend/api/services.ts new file mode 100644 index 0000000000..15c60678d3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/api/services.ts @@ -0,0 +1,14 @@ +import type { ISendEmail, IMsg } from "@/interfaces" +import { apiCore } from "./core" + +export const apiService = { + // USER CONTACT MESSAGE + async postEmailContact(data: ISendEmail) { + return await useFetch(`${apiCore.url()}/service/contact`, + { + method: "POST", + body: data, + } + ) + }, +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/app.vue b/{{cookiecutter.project_slug}}/frontend/app.vue new file mode 100644 index 0000000000..82cad78a00 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/app.vue @@ -0,0 +1,16 @@ + diff --git a/{{cookiecutter.project_slug}}/frontend/assets/css/main.css b/{{cookiecutter.project_slug}}/frontend/assets/css/main.css new file mode 100644 index 0000000000..cc7b82d24b --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/assets/css/main.css @@ -0,0 +1,9 @@ +@tailwind base; + +html,body, #__nuxt, #__layout{ + height:100%!important; + width: 100%!important; + } + +@tailwind components; +@tailwind utilities; \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/babel.config.js b/{{cookiecutter.project_slug}}/frontend/babel.config.js deleted file mode 100644 index 5902d7d136..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/babel.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - "presets": [ - [ - "@vue/cli-plugin-babel/preset", - { - "useBuiltIns": "entry" - } - ] - ] -} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/alerts/Button.vue b/{{cookiecutter.project_slug}}/frontend/components/alerts/Button.vue new file mode 100644 index 0000000000..0fae5f301a --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/alerts/Button.vue @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/authentication/MagicLoginCard.vue b/{{cookiecutter.project_slug}}/frontend/components/authentication/MagicLoginCard.vue new file mode 100644 index 0000000000..875085b531 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/authentication/MagicLoginCard.vue @@ -0,0 +1,64 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/authentication/Navigation.vue b/{{cookiecutter.project_slug}}/frontend/components/authentication/Navigation.vue new file mode 100644 index 0000000000..14d96049ff --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/authentication/Navigation.vue @@ -0,0 +1,60 @@ + + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/layouts/Notification.vue b/{{cookiecutter.project_slug}}/frontend/components/layouts/Notification.vue new file mode 100644 index 0000000000..0f16976b00 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/layouts/Notification.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/layouts/default/Footer.vue b/{{cookiecutter.project_slug}}/frontend/components/layouts/default/Footer.vue new file mode 100644 index 0000000000..f24cccde3c --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/layouts/default/Footer.vue @@ -0,0 +1,89 @@ + + + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/layouts/default/Navigation.vue b/{{cookiecutter.project_slug}}/frontend/components/layouts/default/Navigation.vue new file mode 100644 index 0000000000..c445181e4a --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/layouts/default/Navigation.vue @@ -0,0 +1,63 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/layouts/home/Navigation.vue b/{{cookiecutter.project_slug}}/frontend/components/layouts/home/Navigation.vue new file mode 100644 index 0000000000..0370f6c3d4 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/layouts/home/Navigation.vue @@ -0,0 +1,63 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/locale/LocaleDropdown.vue b/{{cookiecutter.project_slug}}/frontend/components/locale/LocaleDropdown.vue new file mode 100644 index 0000000000..0efa232604 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/locale/LocaleDropdown.vue @@ -0,0 +1,60 @@ + + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/locale/LocaleLink.vue b/{{cookiecutter.project_slug}}/frontend/components/locale/LocaleLink.vue new file mode 100644 index 0000000000..ada54a48e6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/locale/LocaleLink.vue @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/moderation/CheckState.vue b/{{cookiecutter.project_slug}}/frontend/components/moderation/CheckState.vue new file mode 100644 index 0000000000..796505b017 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/moderation/CheckState.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/moderation/CheckToggle.vue b/{{cookiecutter.project_slug}}/frontend/components/moderation/CheckToggle.vue new file mode 100644 index 0000000000..3665f708d6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/moderation/CheckToggle.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/moderation/CreateUser.vue b/{{cookiecutter.project_slug}}/frontend/components/moderation/CreateUser.vue new file mode 100644 index 0000000000..6cd78cdf3f --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/moderation/CreateUser.vue @@ -0,0 +1,79 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/moderation/ToggleActive.vue b/{{cookiecutter.project_slug}}/frontend/components/moderation/ToggleActive.vue new file mode 100644 index 0000000000..6683fdef4f --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/moderation/ToggleActive.vue @@ -0,0 +1,38 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/moderation/ToggleMod.vue b/{{cookiecutter.project_slug}}/frontend/components/moderation/ToggleMod.vue new file mode 100644 index 0000000000..2a04c2c05b --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/moderation/ToggleMod.vue @@ -0,0 +1,38 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/moderation/UserTable.vue b/{{cookiecutter.project_slug}}/frontend/components/moderation/UserTable.vue new file mode 100644 index 0000000000..eb1e15eee6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/moderation/UserTable.vue @@ -0,0 +1,58 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaBadge.client.vue b/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaBadge.client.vue new file mode 100644 index 0000000000..d5327d8679 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaBadge.client.vue @@ -0,0 +1,16 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaInstallPrompt.client.vue b/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaInstallPrompt.client.vue new file mode 100644 index 0000000000..f3c070c8af --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaInstallPrompt.client.vue @@ -0,0 +1,26 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaPrompt.client.vue b/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaPrompt.client.vue new file mode 100644 index 0000000000..21b973e866 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/pwa/PwaPrompt.client.vue @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/settings/Profile.vue b/{{cookiecutter.project_slug}}/frontend/components/settings/Profile.vue new file mode 100644 index 0000000000..91597e70c2 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/settings/Profile.vue @@ -0,0 +1,95 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/settings/Security.vue b/{{cookiecutter.project_slug}}/frontend/components/settings/Security.vue new file mode 100644 index 0000000000..719e7170b4 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/settings/Security.vue @@ -0,0 +1,220 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue b/{{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue new file mode 100644 index 0000000000..6aca5cb32c --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/components/settings/ValidateEmailButton.vue @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/config/i18n.ts b/{{cookiecutter.project_slug}}/frontend/config/i18n.ts new file mode 100644 index 0000000000..54572d59a2 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/config/i18n.ts @@ -0,0 +1,6 @@ +export default defineI18nConfig(() => ({ + // https://phrase.com/blog/posts/nuxt-js-tutorial-i18n/ + // https://v8.i18n.nuxtjs.org/ + // https://saimana.com/list-of-country-locale-code/ + legacy: false, +})) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/content/about.md b/{{cookiecutter.project_slug}}/frontend/content/about.md new file mode 100644 index 0000000000..7ab7026178 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/about.md @@ -0,0 +1,149 @@ +--- +title: Getting started with a base project +description: "Accelerate your next web development project with this FastAPI/Nuxt.js base project generator." +navigation: false +--- + +# Getting started with a base project + +Accelerate your next web development project with this FastAPI/Nuxt.js base project generator. + +This project is a fork of [Sebastián Ramírez's](https://github.com/tiangolo) [Full Stack FastAPI and PostgreSQL Base Project Generator](https://github.com/tiangolo/full-stack-fastapi-postgresql). FastAPI is updated to version 0.109 (April 2024), SQLAlchemy to version 2.0.29 (March 2024), and the frontend to Nuxt 3.11 (April 2024). + +--- + +- [Key features](#key-features) +- [How to use it](#how-to-use-it) + - [Generate passwords](#generate-passwords) + - [Input variables](#input-variables) +- [How to deploy](#how-to-deploy) +- [Fork differences](#fork-differences) +- [More details](#more-details) +- [Licence](#licence) + +--- + +## Key features + +- **Docker Compose** integration and optimization for local development. +- [**FastAPI**](https://github.com/tiangolo/fastapi) backend with [Inboard](https://inboard.bws.bio/) one-repo Docker images, using Python 3.11: + - **Authentication** user management schemas, models, crud and apis already built, with OAuth2 JWT token support & default hashing. + - **SQLAlchemy** version 2.0 support for models. + - **Pydantic** version 2.7 for schemas. + - **Metadata Schema** based on [Dublin Core](https://www.dublincore.org/specifications/dublin-core/dcmi-terms/#section-3) for inheritance. + - **Common CRUD** support via generic inheritance. + - **Standards-based**: Based on (and fully compatible with) the open standards for APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) and [JSON Schema](http://json-schema.org/). + - **MJML** templates for common email transactions. + - [**Many other features**]("https://fastapi.tiangolo.com/features/"): including automatic validation, serialization, interactive documentation, etc. +- [**Nuxt/Vue 3**](https://nuxt.com/) frontend using TypeScript: + - **Authentication** with JWT and cookie management, including `access` and `refresh` tokens, + - **Authorisation** via middleware for page access, including logged in or superuser. + - **Model blog** project, with [Nuxt Content](https://content.nuxtjs.org/) for writing Markdown pages. + - **Form validation** with [Vee-Validate 4](https://vee-validate.logaretm.com/v4/). + - **State management** with [Pinia](https://pinia.vuejs.org/), and persistance with [Pinia PersistedState](https://prazdevs.github.io/pinia-plugin-persistedstate/). + - **CSS and templates** with [TailwindCSS](https://tailwindcss.com/), [HeroIcons](https://heroicons.com/), and [HeadlessUI](https://headlessui.com/). +- **PostgreSQL** database. +- **PGAdmin** for PostgreSQL database management. +- **Celery** worker that can import and use models and code from the rest of the backend selectively. +- **Flower** for Celery jobs monitoring. +- **Neo4j** graph database, including integration into the FastAPI base project. +- Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers. +- Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation. + +## How to use it + +Go to the directory where you want to create your project and run: + +```bash +pip install cookiecutter +cookiecutter https://github.com/whythawk/full-stack-fastapi-postgresql +``` + +### Generate passwords + +You will be asked to provide passwords and secret keys for several components. Open another terminal and run: + +```bash +openssl rand -hex 32 +# Outputs something like: 99d3b1f01aa639e4a76f4fc281fc834747a543720ba4c8a8648ba755aef9be7f +``` + +Copy the contents and use that as password / secret key. And run that again to generate another secure key. + +### Input variables + +The generator (cookiecutter) will ask you for some data, you might want to have at hand before generating the project. + +The input variables, with their default values (some auto generated) are: + +- `project_name`: The name of the project +- `project_slug`: The development friendly name of the project. By default, based on the project name +- `domain_main`: The domain in where to deploy the project for production (from the branch `production`), used by the load balancer, backend, etc. By default, based on the project slug. +- `domain_staging`: The domain in where to deploy while staging (before production) (from the branch `master`). By default, based on the main domain. +- `domain_base_api_url`: The domain url used by the frontend app for backend api calls. If deploying a localhost development environment, likely to be `http://localhost/api/v1` +- `domain_base_ws_url`: The domain url used by the frontend app for backend websocket calls. If deploying a localhost development environment, likely to be `ws://localhost/api/v1` + +- `docker_swarm_stack_name_main`: The name of the stack while deploying to Docker in Swarm mode for production. By default, based on the domain. +- `docker_swarm_stack_name_staging`: The name of the stack while deploying to Docker in Swarm mode for staging. By default, based on the domain. + +- `secret_key`: Backend server secret key. Use the method above to generate it. +- `first_superuser`: The first superuser generated, with it you will be able to create more users, etc. By default, based on the domain. +- `first_superuser_password`: First superuser password. Use the method above to generate it. +- `backend_cors_origins`: Origins (domains, more or less) that are enabled for CORS (Cross Origin Resource Sharing). This allows a frontend in one domain (e.g. `https://dashboard.example.com`) to communicate with this backend, that could be living in another domain (e.g. `https://api.example.com`). It can also be used to allow your local frontend (with a custom `hosts` domain mapping, as described in the project's `README.md`) that could be living in `http://dev.example.com:8080` to communicate with the backend at `https://stag.example.com`. Notice the `http` vs `https` and the `dev.` prefix for local development vs the "staging" `stag.` prefix. By default, it includes origins for production, staging and development, with ports commonly used during local development by several popular frontend frameworks (Vue with `:8080`, React, Angular). +- `smtp_port`: Port to use to send emails via SMTP. By default `587`. +- `smtp_host`: Host to use to send emails, it would be given by your email provider, like Mailgun, Sparkpost, etc. +- `smtp_user`: The user to use in the SMTP connection. The value will be given by your email provider. +- `smtp_password`: The password to be used in the SMTP connection. The value will be given by the email provider. +- `smtp_emails_from_email`: The email account to use as the sender in the notification emails, it could be something like `info@your-custom-domain.com`. +- `smtp_emails_from_name`: The email account name to use as the sender in the notification emails, it could be something like `Symona Adaro`. +- `smtp_emails_to_email`: The email account to use as the recipient for `contact us` emails, it could be something like `requests@your-custom-domain.com`. + +- `postgres_password`: Postgres database password. Use the method above to generate it. (You could easily modify it to use MySQL, MariaDB, etc). +- `pgadmin_default_user`: PGAdmin default user, to log-in to the PGAdmin interface. +- `pgadmin_default_user_password`: PGAdmin default user password. Generate it with the method above. + +- `neo4j_password`: Neo4j database password. Use the method above to generate it. + +- `traefik_constraint_tag`: The tag to be used by the internal Traefik load balancer (for example, to divide requests between backend and frontend) for production. Used to separate this stack from any other stack you might have. This should identify each stack in each environment (production, staging, etc). +- `traefik_constraint_tag_staging`: The Traefik tag to be used while on staging. +- `traefik_public_constraint_tag`: The tag that should be used by stack services that should communicate with the public. + +- `flower_auth`: Basic HTTP authentication for flower, in the form`user:password`. By default: "`admin:changethis`". + +- `sentry_dsn`: Key URL (DSN) of Sentry, for live error reporting. You can use the open source version or a free account. E.g.: `https://1234abcd:5678ef@sentry.example.com/30`. + +- `docker_image_prefix`: Prefix to use for Docker image names. If you are using GitLab Docker registry it would be based on your code repository. E.g.: `git.example.com/development-team/my-awesome-project/`. +- `docker_image_backend`: Docker image name for the backend. By default, it will be based on your Docker image prefix, e.g.: `git.example.com/development-team/my-awesome-project/backend`. And depending on your environment, a different tag will be appended ( `prod`, `stag`, `branch` ). So, the final image names used will be like: `git.example.com/development-team/my-awesome-project/backend:prod`. +- `docker_image_celeryworker`: Docker image for the celery worker. By default, based on your Docker image prefix. +- `docker_image_frontend`: Docker image for the frontend. By default, based on your Docker image prefix. + +## How to deploy + +This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it is designed to be used in a cluster controlled with pure Docker in Swarm Mode with a Traefik main load balancer proxy handling automatic HTTPS certificates, using the ideas from DockerSwarm.rocks. + +Please refer to DockerSwarm.rocks to see how to deploy such a cluster in 20 minutes. + +## Fork differences + +The original objective of this fork was to maintain parity with the [Full Stack FastAPI and PostgreSQL Base Project Generator](https://github.com/tiangolo/full-stack-fastapi-postgresql) but update it to bring it up to current stack versions, fixes, and with a complete auth 'n auth system. + +With the most recent updates to the base stack, Sebastián has made some fairly dramatic changes and these two stacks are no longer compatible. This table presents a summary of the major differences: + +| This base stack | Tiangolo base stack | +| :------------------------------- | :---------------------- | +| SQLAlchemy & Pydantic | SqlModel | +| Postgresql 15 & PGAdmin | Postgresql 12 & Adminer | +| Celery & RabbitMQ task queue | - | +| NuxtJS frontend | React frontend | + +I use this stack to produce some fairly complex web-based applications and I need to get to the full APIs for SQLAlchemy and Pydantic, and SqlModel doesn't offer me that. I also need to run distributed asyncronous tasks, so Celery is important. Finally, I prefer Nuxt. + +This stack also has a much more sophisticated and feature-complete auth 'n auth system which is a requirement for any web app. + +## More details + +After using this generator, your new project (the directory created) will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](./base-project/README.md). + +## Licence + +This project is licensed under the terms of the MIT license. diff --git a/{{cookiecutter.project_slug}}/frontend/content/authentication.md b/{{cookiecutter.project_slug}}/frontend/content/authentication.md new file mode 100644 index 0000000000..9c5203face --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/authentication.md @@ -0,0 +1,104 @@ +--- +title: Authentication with Magic and Oauth2 +description: "Background and approach to authentication in this stack." +navigation: false +--- + +# Authentication with Magic and Oauth2 + +## Minimum security requirements + +The following is the baseline [recommended approach](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md) for ensuring safe app authentication: + +- Any user account change must require current password verification to ensure that it's the legitimate user. +- Login page and all subsequent authenticated pages must be exclusively accessed over TLS or other strong transport. +- Account recovery must ensure that the starting point is a logged-out account. +- Where a state is unclear, use two tokens (one emailed, one stored in the browser) with a handshaking / fingerprinting protocol to ensure a chain of custody. +- An application should respond with a generic error message regardless of whether: + - The user ID or password was incorrect. + - The account does not exist. + - The account is locked or disabled. +- Code should go through the same process, no matter what, allowing the application to return in approximately the same response time. +- In the words of [George Orwell](https://en.wikipedia.org/wiki/Politics_and_the_English_Language#Remedy_of_Six_Rules), "break any of these rules sooner than do anything outright barbarous". + +[On passwords](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Password_Storage_Cheat_Sheet.md): +- Use `Argon2id` with a minimum configuration of 15 MiB of memory, an iteration count of 2, and 1 degree of parallelism. +- Passwords shorter than 8 characters are considered to be weak (NIST SP800-63B). +- Maximum password length of 64 prevents long password Denial of Service attacks. +- Do not silently truncate passwords. +- Allow usage of all characters, including unicode and whitespace. + +## Authenticated email-based _magic_ login + +Most web applications permit account recovery through requesting a password reset via email. This is a weak point in the custodial chain _even_ assuming a savvy user adhering to best-practice password conventions. In which case, secure this and offer it as a login option ... a _magic_ login. + +> Any custodial changes to user-controlled information must be treated as requiring full authentication. Do **not** assume that a logged-in user is the authorised account holder. + +### Magic login workflow + +- Ensure the user is logged out. +- User enters email in `frontend` and submits via API to `backend`. +- Check if an account exists, if not silently create one, and get the user `id`. +- Generate two 30-second-duration magic JWT tokens, each with a randomly-generated UUID `sub` (subject) and `fingerprint`, where `sub1 != sub2` and `fingerprint1 == fingerprint2`. +- One is emailed to the user (with `sub = user.id`) and the other is returned to the `frontend` to be stored in the user's browser. +- Once the user clicks on (or pastes) the magic / emailed link and returns to the browser, check that the `fingerprint` in the stored and submitted tokens are the same, and submit both to the `backend`. +- Validate the tokens, and check compliance with `sub` and `fingerprint` rules. +- If the custodial chain is secure, return an `access_token` and `refresh_token` to the `frontend`. +- Note that the tokens provide no meaningful information to an adversary. No email address or personal information. + +### Oauth2 password login + +Users may not always have access to email, or use a password manager, making it easier (and faster) to login with a password. A fallback to a stored password must be offered. You could also choose to enforce passwords for admin users. + +### Account / password recovery and reset + +Clearly a user does not _need_ a password to login with this process, and so there is no enforcement in the core stack. However, an application may require evidence of a deliberate decision in the custodial chain. Enforcing a password is part of that chain, and that raises the need to reset a password if it is ever lost. + +Password recovery functions much the same as the magic workflow, with the same dual token validation process, except that the user now goes to a page that permits them to save a new password. + +The user can also change their password while logged in, but - mindful of the rules for validation - they will need to provide their original password before doing so. + +## Two-factor authentication + +Time-based One-Time Password (TOTP) authentication extends the login process to include a _challenge-response_ component where the user needs to enter a time-based token _after_ their preferred login method. + +This requires that the user: + +- Install an authenticator app. +- Generate a QR code or key and pair that with their app. +- Confirm that they are paired. + +After that, the user will be challenged to enter a 6-digit verification code to conclude each login. + +The login workflow is extended as follows: + +- TOTP requires the use of third-party token generators, and they seem to be stuck on `sha-1` hashing. That means deliberately dumbing down from `sha256`. +- After successful login (oauth2 or magic) instead of generating `access_token` and `refresh_token`, **instead** generate a special `access_token` with `totp = True` as a key in the token. +- Specifically test for this in each authentication check. `totp = True` can **only** be used to verify a TOTP submission, not for any other purpose. The user is not considered to be authenticated. +- When the user submits their verification code, `post` that, plus the `access_token` with `totp = True`, to complete authentication and receive the standard `access_token` and `refresh_token`. + +As before, enabling or disabling TOTP requires full authentication with a password. + +## Access and Refresh tokens + +Persisting the authentication `state` of a user requires a mechanism to respond to an authentication challenge which does not inconvenience the user, while still maintaining security. + +The standard method for doing so is via `access_token` and `refresh_token`, where: + +- The access token is of short duration (30 minutes, or even less). +- The refresh token is of long duration (3 months, and sometimes indefinite). +- An access token can only be used to authenticate the user, and a refresh token can only be used to generate new access tokens. +- Access tokens are not stored, and refresh tokens are maintained in the database ... meaning they must be deliberately deactivated on use. +- When a user logs out, deactivate their refresh tokens. + +Obviously, this still means that a long-living, active `refresh_token` is equivalent to authentication, which returns us to the caveat raised above: + +> Any custodial changes to user-controlled information must be treated as requiring full authentication. Do **not** assume that a logged-in user is the authorised account holder. + +## References + +- [Python JOSE](https://python-jose.readthedocs.io/) to generate JWT tokens. +- [PassLib](https://passlib.readthedocs.io/) to manage hashing and TOTP. +- [OWASP authentication cheat sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Authentication_Cheat_Sheet.md) +- [OWASP password storage cheat sheet](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Password_Storage_Cheat_Sheet.md) +- [Ensuring randomness](https://blog.cloudflare.com/ensuring-randomness-with-linuxs-random-number-generator/) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/content/blog/20160708-theranos-and-elitism.md b/{{cookiecutter.project_slug}}/frontend/content/blog/20160708-theranos-and-elitism.md new file mode 100644 index 0000000000..0a875e52eb --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/blog/20160708-theranos-and-elitism.md @@ -0,0 +1,55 @@ +--- +title: Theranos and the elitist belief in magical thinking +description: "If an African leader stood up at a meeting of European investors and declared that his country’s agricultural success could be attributed to traditional muthi, he would be regarded with an embarrassed sigh." +author: Gavin Chait +publishedAt: 2016-07-08 +categories: science, superstition +--- + +# Theranos and the elitist belief in magical thinking + +_Gavin Chait, 8 July 2016_ + +If an African leader stood up at a meeting of European investors and declared that his country’s agricultural success could be attributed to traditional _muthi_, he would be regarded with an embarrassed sigh. + +Except when it’s the British aristocrat, Prince Charles, and he’s talking about using homeopathy to treat his cows, then he’s treated with polite applause. + +Nowhere is that hypocrisy more visible than in the story of President Yahyah Jammeh of Gambia who claims his homeopathy can cure AIDS. He is supported by Ainsworths, a homeopathic dealer which operates under a royal seal of appointment from Prince Charles. + +Superstition is alive and well in the West, only instead of skins and furs, it wears a white lab coat and attempts to look respectable. + +There are two ways in which this is having a destructive effect on humanity. + +The first is in adaptation to Climate Change. + +There is near universal scientific support for the theory that global warming is real and caused by people. Greenpeace and other pressure groups are in full accord with scientific thinking here. + +Scientists also have near universal agreement on the benefits of genetically modified organisms. Greenpeace and other pressure groups refuse to accept scientific thinking on this topic, promoting the woolly world of ‘organic’ instead. + +Their thinking can be summarised as being that climate change confirms Greenpeace’s bias against large corporations as the cause of all evil, while accepting genetically modified crops as being healthy would contradict that belief, since it demonstrates that large corporations are key to solving the world’s problems. + +Each could be true, but Greenpeace insists that corporations can only be evil, hence their loathing of GM. + +This has become so worrying – being that it denies life-saving crops to African countries already suffering under drought and famine – that more than 109 Nobel Prize- winners have signed an open letter demanding that Greenpeace end their campaign against GM foods: “Scientific and regulatory agencies around the world have repeatedly and consistently found crops and foods improved through biotechnology to be as safe as, if not safer than those derived from any other method of production.” + +Magical thinking against real and working science prevents access for those people who would most benefit from it. + +The second destructive outcome is where magical thinking, dressed up in scientific garb, undermines real science. + +The worst and most recent of many such scandals involves Theranos, a US-based medical laboratory service. Theranos promised investors and medical professionals an end to painful and unpleasant needle-based blood specimen collection, and a world of cheap and easily available medical tests. + +The ‘secret’ was their heavily secret blood-testing device called Edison. Instead of traditional venepuncture (a needle, to the rest of us), they used a few drops of blood from a finger-stick puncture. Forget that actual medical professionals and scientists pointed out that such a small amount of blood, drawn from a peripheral part of the body, would produce wildly varying results no matter how clever the diagnostic machine, investors hurled $400 million at it. + +By 2014, the company – and its charming, blonde, blue-eyed CEO Elizabeth Holmes – was estimated to be worth $9 billion. + +Eventually, at the top of the hype train, the Food and Drug Administration began to look into the company, pointing out that there was almost no quality control and that - far from using some secret technology – most tests were being run on traditional devices. + +Theranos was forced to void all their test results. One of their main labs has been shut down. Last week Holmes was banned from operating any lab for two years. + +Along the way, Walmart fell for the hype and entered into a costly partnership, and hundreds of normally reasonable investors have lost their shirts. Theranos is now worth nothing. + +Magical thinking is not science and, given the range of challenges humanity faces, it’s time we took it a bit more seriously. + +--- + +_:copyright: Gavin Chait 2016. All rights reserved._ diff --git a/{{cookiecutter.project_slug}}/frontend/content/blog/20160721-lament-for-the-auther.md b/{{cookiecutter.project_slug}}/frontend/content/blog/20160721-lament-for-the-auther.md new file mode 100644 index 0000000000..d66f8ecbd9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/blog/20160721-lament-for-the-auther.md @@ -0,0 +1,57 @@ +--- +title: A lament for the author +description: "‘I’ve got a book out,’ says me hopefully, my hands twisted on the keyboard." +author: Gavin Chait +publishedAt: 2016-07-21 +categories: publishing +--- + +# A lament for the author + +_21 July 2016, Gavin Chait_ + +‘I’ve got a book out,’ says me hopefully, my hands twisted on the keyboard. + +Three years ago, I took a month off and set out to start and finish writing a novel. A science fiction novel set in Nigeria, no less. + +The first month allowed me to knock out 60,000 words. It took the next 18 months to make time to expand and polish that core. + +‘Nobody knows anything,’ said William Goldman in describing Hollywood’s ability to pick winners (and their investors’ regular ability to produce financial disasters). + +I’ve wanted to write novels since before I was in my teens, but it always seemed overly intimidating. Swatting out a few short articles a week is one thing. Sitting down and committing to produce 100,000 words is quite another. + +The not knowing is also about not knowing what’s involved in producing the thing, let alone whether it will be successful. + +And the economics are fairly harsh. If you want to make, for example, R20 from each book, you’re going to have to sell tens of thousands every year before you can quit your day-job. + +In exchange, you need to commit months of time unpaid in the insecure hope that what you produce is – at the very least – read. + +Each day an estimated thousand to two thousand books are published, adding to the 31 million paperbacks or the 3.1 million ebooks already available on Amazon.com. + +Maybe you’ve heard of the self-publishing phenomenon, and of the miraculous stories of people like Hugh Howey who self-published his Wool and Sand series and became a best-seller, or Mark Dawson’s series about an assassin which earns him $450,000 a year? + +Sadly, out of the well over half a million new novels published every year, very few are going to make that sort of money. For most writers, scribbling in any spare time they can manage, they are unlikely to experience that kind of success. + +There are numerous lightning strikes you need to navigate, many lottery tickets which need to be won in sequence before the final lottery of which ‘nobody knows anything’: why does one book become a best-seller but another, similar book, goes read only by close friends and relatives of the author? + +You can throw runes and try divining their meaning; is it price? Is it the cover? How about the day of week or time of day when it is launched? Summer or winter? + +George RR Martin published his first novel in 1983, but it wasn’t till 1996 that he released ‘A Game of Thrones’, and it wasn’t till the fourth in that series – 2005’s ‘A Feast for Crows’ – that he began to achieve success. The HBO ‘Game of Thrones’ adaptation of his novels has made him world famous. + +At the other end is Andy Weir who published his first novel, ‘The Martian’, in 2011, achieved runaway success immediately, and saw it turned into a madly successful movie in 2015. + +Writers can achieve success instantly, languish in obscurity and then achieve success, or languish in obscurity indefinitely. + +Figuring out what and who will connect is, well, you know already. + +Weirdly, the same is true of newspaper columns I’ve written. I’ve had relatively obscure topics explode my inbox, and others where I thought it would result in some controversy result in the gentle sound of crickets at midnight. + +Despite all the uncertainty - and the supposed destruction of mainstream publishers - 60% of all commercial sales still accrue to the big six publishers. In the US, that’s an astonishing $27 billion industry total a year. + +I hope you’re interested in reading about how my hero escapes from an orbital prison, survives the subsequent fall and crash-landing in a small Nigerian village, and escapes the interest of a local warlord. + +Continues me, ‘It’ll be out on Friday. Like a real book, with pages and everything. It’s called [“Lament for the Fallen”](https://gavinchait.com/lament-for-the-fallen/), go buy it.’ + +--- + +_:copyright: Gavin Chait 2016. All rights reserved._ diff --git a/{{cookiecutter.project_slug}}/frontend/content/blog/20170203-summer-of-99.md b/{{cookiecutter.project_slug}}/frontend/content/blog/20170203-summer-of-99.md new file mode 100644 index 0000000000..aed436733a --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/blog/20170203-summer-of-99.md @@ -0,0 +1,53 @@ +--- +title: The summer of '99 +description: "I remember what it was like being a student after the fall of the Berlin Wall, the collapse of Communism, the release of Nelson Mandela, and the ignominious end of Apartheid." +author: Gavin Chait +publishedAt: 2017-02-03 +categories: hope, equality, liberty +--- + +# The summer of ‘99 + +I remember what it was like being a student after the fall of the Berlin Wall, the collapse of Communism, the release of Nelson Mandela, and the ignominious end of Apartheid. + +I remember the blue clarity of its summer skies. The way, driving from the university into the city, the green slopes of Table Mountain would open to the vast bright glare of the ocean. The smell of fynbos and roar of cicadas and of hope so tangible it felt as if everyone was bouncing as they walked. + +An entire generation raised on the notion that evil could be vanquished, that the brutalised could be made whole again, and that anything was possible. + +Some of us dedicated our lives to building Nelson Mandela’s vision of a ‘new South Africa’, working in the townships building houses, bringing healthcare and education, or creating jobs. Others, like Elon Musk and Mark Shuttleworth, headed to the US and became billionaires. + +It is symbolic of the time that social confidence – belief in the possible – was so high that some of the largest companies founded in recent years were all immigrants to the US. + +eBay founded by Frenchman Pierre Omidyar. Google founded by Russian Sergey Brin. Yahoo founded by Taiwanese Jerry Yang. And there are countless others, less well known but equally as dynamic and exciting. + +So confident was the period that Francis Fukuyama could pronounce the ‘end of history’, with an important caveat. He worried we would forget what it had cost us to achieve our freedom, and we would chafe with resentment as the established pecking order was disrupted. And then the despots would return. + +I remember when that darkness began to loom. It was 9 July 2000 when Nkosi Johnson rose to address the 13th International AIDS Conference in Durban. + +"Care for us and accept us - we are all human beings. We are normal. We have hands. We have feet. We can walk, we can talk, we have needs just like everyone else - don't be afraid of us - we are all the same!" + +Powerful words. Thabo Mbeki scowled in disgust and walked out. + +If you had to pick a moment when the forces of truth and science and knowledge were cast aside in favour of lies, ‘fake news’, bigotry and superstition, it was that moment. Thabo Mbeki deserves nothing but scorn and contempt. + +And I remember the sense of our world being utterly destroyed on 11 September 2001 when hatred and scorn emerged from the sixteenth century, and liberalism gave way to mutual suspicion. + +There was a brief window when being young was not about crisis and rebellion, but about hope and building. + +It would be nice to say, well, that’s just white racism. It isn’t only. There are violent suppressions of liberal values in almost every country. From Rodrigo Duterte’s massacre of alleged drug-dealers and users in the Philippines, to Recep Erdoğan’s arrest of tens of thousands of ordinary people in the aftermath of a coup attempt in Turkey. The African Union has backed mass withdrawal of all African countries from the International Criminal Court. + +This is the return of politics and history with a vengeance. + +But the summer of ’99 existed. It could exist again. + +The first lesson of the fall of the Berlin Wall is that sustained mass protest works (with the important proviso that – with a nod to Egypt of 2013, China of 1989, and South Africa of 1960 – any government willing to massacre its own protesting citizens can impose anything it likes). + +The second is that you should never stop building and believing. It is easy to run away, more difficult and dangerous to run towards. + +We need people who dream, who build and are willing to share those dreams and ambitions. Start businesses anyway. Organise protests anyway. Work together across the things that divide us anyway. + +Because everyone should get to have that summer. + +--- + +_:copyright: Gavin Chait 2017. All rights reserved._ diff --git a/{{cookiecutter.project_slug}}/frontend/content/fr/about.md b/{{cookiecutter.project_slug}}/frontend/content/fr/about.md new file mode 100644 index 0000000000..7ab7026178 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/fr/about.md @@ -0,0 +1,149 @@ +--- +title: Getting started with a base project +description: "Accelerate your next web development project with this FastAPI/Nuxt.js base project generator." +navigation: false +--- + +# Getting started with a base project + +Accelerate your next web development project with this FastAPI/Nuxt.js base project generator. + +This project is a fork of [Sebastián Ramírez's](https://github.com/tiangolo) [Full Stack FastAPI and PostgreSQL Base Project Generator](https://github.com/tiangolo/full-stack-fastapi-postgresql). FastAPI is updated to version 0.109 (April 2024), SQLAlchemy to version 2.0.29 (March 2024), and the frontend to Nuxt 3.11 (April 2024). + +--- + +- [Key features](#key-features) +- [How to use it](#how-to-use-it) + - [Generate passwords](#generate-passwords) + - [Input variables](#input-variables) +- [How to deploy](#how-to-deploy) +- [Fork differences](#fork-differences) +- [More details](#more-details) +- [Licence](#licence) + +--- + +## Key features + +- **Docker Compose** integration and optimization for local development. +- [**FastAPI**](https://github.com/tiangolo/fastapi) backend with [Inboard](https://inboard.bws.bio/) one-repo Docker images, using Python 3.11: + - **Authentication** user management schemas, models, crud and apis already built, with OAuth2 JWT token support & default hashing. + - **SQLAlchemy** version 2.0 support for models. + - **Pydantic** version 2.7 for schemas. + - **Metadata Schema** based on [Dublin Core](https://www.dublincore.org/specifications/dublin-core/dcmi-terms/#section-3) for inheritance. + - **Common CRUD** support via generic inheritance. + - **Standards-based**: Based on (and fully compatible with) the open standards for APIs: [OpenAPI](https://github.com/OAI/OpenAPI-Specification) and [JSON Schema](http://json-schema.org/). + - **MJML** templates for common email transactions. + - [**Many other features**]("https://fastapi.tiangolo.com/features/"): including automatic validation, serialization, interactive documentation, etc. +- [**Nuxt/Vue 3**](https://nuxt.com/) frontend using TypeScript: + - **Authentication** with JWT and cookie management, including `access` and `refresh` tokens, + - **Authorisation** via middleware for page access, including logged in or superuser. + - **Model blog** project, with [Nuxt Content](https://content.nuxtjs.org/) for writing Markdown pages. + - **Form validation** with [Vee-Validate 4](https://vee-validate.logaretm.com/v4/). + - **State management** with [Pinia](https://pinia.vuejs.org/), and persistance with [Pinia PersistedState](https://prazdevs.github.io/pinia-plugin-persistedstate/). + - **CSS and templates** with [TailwindCSS](https://tailwindcss.com/), [HeroIcons](https://heroicons.com/), and [HeadlessUI](https://headlessui.com/). +- **PostgreSQL** database. +- **PGAdmin** for PostgreSQL database management. +- **Celery** worker that can import and use models and code from the rest of the backend selectively. +- **Flower** for Celery jobs monitoring. +- **Neo4j** graph database, including integration into the FastAPI base project. +- Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers. +- Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation. + +## How to use it + +Go to the directory where you want to create your project and run: + +```bash +pip install cookiecutter +cookiecutter https://github.com/whythawk/full-stack-fastapi-postgresql +``` + +### Generate passwords + +You will be asked to provide passwords and secret keys for several components. Open another terminal and run: + +```bash +openssl rand -hex 32 +# Outputs something like: 99d3b1f01aa639e4a76f4fc281fc834747a543720ba4c8a8648ba755aef9be7f +``` + +Copy the contents and use that as password / secret key. And run that again to generate another secure key. + +### Input variables + +The generator (cookiecutter) will ask you for some data, you might want to have at hand before generating the project. + +The input variables, with their default values (some auto generated) are: + +- `project_name`: The name of the project +- `project_slug`: The development friendly name of the project. By default, based on the project name +- `domain_main`: The domain in where to deploy the project for production (from the branch `production`), used by the load balancer, backend, etc. By default, based on the project slug. +- `domain_staging`: The domain in where to deploy while staging (before production) (from the branch `master`). By default, based on the main domain. +- `domain_base_api_url`: The domain url used by the frontend app for backend api calls. If deploying a localhost development environment, likely to be `http://localhost/api/v1` +- `domain_base_ws_url`: The domain url used by the frontend app for backend websocket calls. If deploying a localhost development environment, likely to be `ws://localhost/api/v1` + +- `docker_swarm_stack_name_main`: The name of the stack while deploying to Docker in Swarm mode for production. By default, based on the domain. +- `docker_swarm_stack_name_staging`: The name of the stack while deploying to Docker in Swarm mode for staging. By default, based on the domain. + +- `secret_key`: Backend server secret key. Use the method above to generate it. +- `first_superuser`: The first superuser generated, with it you will be able to create more users, etc. By default, based on the domain. +- `first_superuser_password`: First superuser password. Use the method above to generate it. +- `backend_cors_origins`: Origins (domains, more or less) that are enabled for CORS (Cross Origin Resource Sharing). This allows a frontend in one domain (e.g. `https://dashboard.example.com`) to communicate with this backend, that could be living in another domain (e.g. `https://api.example.com`). It can also be used to allow your local frontend (with a custom `hosts` domain mapping, as described in the project's `README.md`) that could be living in `http://dev.example.com:8080` to communicate with the backend at `https://stag.example.com`. Notice the `http` vs `https` and the `dev.` prefix for local development vs the "staging" `stag.` prefix. By default, it includes origins for production, staging and development, with ports commonly used during local development by several popular frontend frameworks (Vue with `:8080`, React, Angular). +- `smtp_port`: Port to use to send emails via SMTP. By default `587`. +- `smtp_host`: Host to use to send emails, it would be given by your email provider, like Mailgun, Sparkpost, etc. +- `smtp_user`: The user to use in the SMTP connection. The value will be given by your email provider. +- `smtp_password`: The password to be used in the SMTP connection. The value will be given by the email provider. +- `smtp_emails_from_email`: The email account to use as the sender in the notification emails, it could be something like `info@your-custom-domain.com`. +- `smtp_emails_from_name`: The email account name to use as the sender in the notification emails, it could be something like `Symona Adaro`. +- `smtp_emails_to_email`: The email account to use as the recipient for `contact us` emails, it could be something like `requests@your-custom-domain.com`. + +- `postgres_password`: Postgres database password. Use the method above to generate it. (You could easily modify it to use MySQL, MariaDB, etc). +- `pgadmin_default_user`: PGAdmin default user, to log-in to the PGAdmin interface. +- `pgadmin_default_user_password`: PGAdmin default user password. Generate it with the method above. + +- `neo4j_password`: Neo4j database password. Use the method above to generate it. + +- `traefik_constraint_tag`: The tag to be used by the internal Traefik load balancer (for example, to divide requests between backend and frontend) for production. Used to separate this stack from any other stack you might have. This should identify each stack in each environment (production, staging, etc). +- `traefik_constraint_tag_staging`: The Traefik tag to be used while on staging. +- `traefik_public_constraint_tag`: The tag that should be used by stack services that should communicate with the public. + +- `flower_auth`: Basic HTTP authentication for flower, in the form`user:password`. By default: "`admin:changethis`". + +- `sentry_dsn`: Key URL (DSN) of Sentry, for live error reporting. You can use the open source version or a free account. E.g.: `https://1234abcd:5678ef@sentry.example.com/30`. + +- `docker_image_prefix`: Prefix to use for Docker image names. If you are using GitLab Docker registry it would be based on your code repository. E.g.: `git.example.com/development-team/my-awesome-project/`. +- `docker_image_backend`: Docker image name for the backend. By default, it will be based on your Docker image prefix, e.g.: `git.example.com/development-team/my-awesome-project/backend`. And depending on your environment, a different tag will be appended ( `prod`, `stag`, `branch` ). So, the final image names used will be like: `git.example.com/development-team/my-awesome-project/backend:prod`. +- `docker_image_celeryworker`: Docker image for the celery worker. By default, based on your Docker image prefix. +- `docker_image_frontend`: Docker image for the frontend. By default, based on your Docker image prefix. + +## How to deploy + +This stack can be adjusted and used with several deployment options that are compatible with Docker Compose, but it is designed to be used in a cluster controlled with pure Docker in Swarm Mode with a Traefik main load balancer proxy handling automatic HTTPS certificates, using the ideas from DockerSwarm.rocks. + +Please refer to DockerSwarm.rocks to see how to deploy such a cluster in 20 minutes. + +## Fork differences + +The original objective of this fork was to maintain parity with the [Full Stack FastAPI and PostgreSQL Base Project Generator](https://github.com/tiangolo/full-stack-fastapi-postgresql) but update it to bring it up to current stack versions, fixes, and with a complete auth 'n auth system. + +With the most recent updates to the base stack, Sebastián has made some fairly dramatic changes and these two stacks are no longer compatible. This table presents a summary of the major differences: + +| This base stack | Tiangolo base stack | +| :------------------------------- | :---------------------- | +| SQLAlchemy & Pydantic | SqlModel | +| Postgresql 15 & PGAdmin | Postgresql 12 & Adminer | +| Celery & RabbitMQ task queue | - | +| NuxtJS frontend | React frontend | + +I use this stack to produce some fairly complex web-based applications and I need to get to the full APIs for SQLAlchemy and Pydantic, and SqlModel doesn't offer me that. I also need to run distributed asyncronous tasks, so Celery is important. Finally, I prefer Nuxt. + +This stack also has a much more sophisticated and feature-complete auth 'n auth system which is a requirement for any web app. + +## More details + +After using this generator, your new project (the directory created) will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](./base-project/README.md). + +## Licence + +This project is licensed under the terms of the MIT license. diff --git a/{{cookiecutter.project_slug}}/frontend/content/privacy.md b/{{cookiecutter.project_slug}}/frontend/content/privacy.md new file mode 100644 index 0000000000..6a9999d437 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/content/privacy.md @@ -0,0 +1,29 @@ +--- +title: Your privacy +description: "We at [Website Name] value your privacy as much as we value a cheap joke." +navigation: false +--- + +# Your privacy and rights + +Welcome to [Website Name], the online version of a hallucination induced by eating too much cheese. Here's what you can expect when you use our website: + +## Privacy policy + +- We'll collect every piece of personal information you have, including your name, address, phone number, email, social security number, credit card details, and your mother's maiden name. We'll also peek into your browser history, your text messages, and your dreams (if we can find the right mushrooms). +- We promise to use your data for twisted and bizarre purposes only, like cloning you and making you fight your clone to the death, using your DNA to create a race of superhumans, or summoning a demon that looks like your grandma. +- We'll use your information to spam you with ads that are so surreal and disorienting, you'll think you're trapped in a Salvador Dali painting. We'll also use it to mess with your mind, make you question reality, and possibly even inspire you to start a cult (which we'll join, of course). +- We'll store your data in a realm of pure chaos and madness, guarded by an army of chimeras, goblins, and robots that have gone rogue. We'll also share your data with our interdimensional overlords, who are always hungry for new sources of entertainment. +- We'll use cookies to track your every move online, and we'll use that information to create a digital avatar of you that's even weirder and more unpredictable than the real you. We'll also use cookies to play pranks on you, like making your cursor turn into a banana or making your keyboard explode (don't worry, it's just a harmless little explosion). + +## GDPR + +We don't care about GDPR or any other earthly laws. Our website operates in a dimension beyond your feeble human concepts of order and justice. If you try to sue us, we'll just laugh and summon a horde of poltergeists to haunt you for eternity. + +## Liability + +By using our website, you agree to relinquish all control over your sanity, your identity, and your soul. You acknowledge that our website is a portal to a universe of madness and mayhem, and that you are entering at your own risk. We're not liable for any psychological, spiritual, or metaphysical damage that may result from using our website. But hey, at least you'll have a good story to tell the angels (or the demons, depending on how things turn out). + +Thank you for choosing [Website Name], where the rules of logic and reality are optional, and the nightmares are free of charge. + +_(Yes, generated by ChatGPT. Replace this with something meaningful.) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/interfaces/index.ts b/{{cookiecutter.project_slug}}/frontend/interfaces/index.ts new file mode 100644 index 0000000000..f01594967d --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/interfaces/index.ts @@ -0,0 +1,36 @@ +import type { + IUserProfile, + IUserProfileUpdate, + IUserProfileCreate, + IUserOpenProfileCreate +} from "./profile" + +import type { + ITokenResponse, + IWebToken, + INewTOTP, + IEnableTOTP, + ISendEmail, + IMsg, + INotification +} from "./utilities" + +// https://stackoverflow.com/a/64782482/295606 +interface IKeyable { + [key: string]: any | any[] +} + +export type { + IKeyable, + IUserProfile, + IUserProfileUpdate, + IUserProfileCreate, + IUserOpenProfileCreate, + ITokenResponse, + IWebToken, + INewTOTP, + IEnableTOTP, + ISendEmail, + IMsg, + INotification +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/interfaces/profile.ts b/{{cookiecutter.project_slug}}/frontend/interfaces/profile.ts new file mode 100644 index 0000000000..770209e296 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/interfaces/profile.ts @@ -0,0 +1,35 @@ +/* eslint-disable camelcase */ +export interface IUserProfile { + id: string + email: string + email_validated: boolean + is_active: boolean + is_superuser: boolean + full_name: string + password: boolean + totp: boolean +} + +export interface IUserProfileUpdate { + email?: string + full_name?: string + original?: string + password?: string + is_active?: boolean + is_superuser?: boolean +} + +export interface IUserProfileCreate { + email: string + full_name?: string + password?: string + is_active?: boolean + is_superuser?: boolean +} + +export interface IUserOpenProfileCreate { + email: string + full_name?: string + password: string +} + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/interfaces/utilities.ts b/{{cookiecutter.project_slug}}/frontend/interfaces/utilities.ts new file mode 100644 index 0000000000..27a71dca7f --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/interfaces/utilities.ts @@ -0,0 +1,42 @@ +/* eslint-disable camelcase */ + +export interface ITokenResponse { + access_token: string + refresh_token: string + token_type: string +} + +export interface IWebToken { + claim: string +} + +export interface INewTOTP { + secret?: string + key: string + uri: string +} + + +export interface IEnableTOTP { + claim: string + uri: string + password?: string +} + +export interface ISendEmail { + email: string + subject: string + content: string +} + +export interface IMsg { + msg: string +} + +export interface INotification { + uid?: string + title: string + content: string + icon?: "success" | "error" | "information" + showProgress?: boolean +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/layouts/authentication.vue b/{{cookiecutter.project_slug}}/frontend/layouts/authentication.vue new file mode 100644 index 0000000000..26b92a753d --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/layouts/authentication.vue @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/layouts/content.vue b/{{cookiecutter.project_slug}}/frontend/layouts/content.vue new file mode 100644 index 0000000000..1b2f5b2863 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/layouts/content.vue @@ -0,0 +1,26 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/layouts/default.vue b/{{cookiecutter.project_slug}}/frontend/layouts/default.vue new file mode 100644 index 0000000000..55c49bcbe4 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/layouts/default.vue @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/layouts/home.vue b/{{cookiecutter.project_slug}}/frontend/layouts/home.vue new file mode 100644 index 0000000000..6e2c8ed919 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/layouts/home.vue @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/locales/en-GB.ts b/{{cookiecutter.project_slug}}/frontend/locales/en-GB.ts new file mode 100644 index 0000000000..4e45a00894 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/locales/en-GB.ts @@ -0,0 +1,21 @@ +export default { + common: { + title: "App", + }, + nav: { + about: "About", + authentication: "Authentication", + blog: "Blog", + }, + footer: { + rights: "All rights reserved." + }, + pwa: { + dismiss: "Dismiss", + install: "Install", + install_title: "Install Base App", + title: "New Base App update available!", + update: "Update", + update_available_short: "Update Base App", + }, +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/locales/fr-FR.ts b/{{cookiecutter.project_slug}}/frontend/locales/fr-FR.ts new file mode 100644 index 0000000000..88f145fe56 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/locales/fr-FR.ts @@ -0,0 +1,13 @@ +export default { + common: { + title: "App", + }, + nav: { + about: "À propos", + authentication: "Authentication", + blog: "Blog", + }, + footer: { + rights: "Tous droits réservés." + }, +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/middleware/anonymous.ts b/{{cookiecutter.project_slug}}/frontend/middleware/anonymous.ts new file mode 100644 index 0000000000..3393ff1393 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/middleware/anonymous.ts @@ -0,0 +1,10 @@ +import { useAuthStore } from "@/stores" + +export default defineNuxtRouteMiddleware((to, from) => { + const routes = ["/login", "/join", "/recover-password", "/reset-password"] + const authStore = useAuthStore() + if (authStore.loggedIn) { + if (routes.includes(from.path)) return navigateTo("/") + else return abortNavigation() + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/middleware/authenticated.ts b/{{cookiecutter.project_slug}}/frontend/middleware/authenticated.ts new file mode 100644 index 0000000000..e202cf15a5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/middleware/authenticated.ts @@ -0,0 +1,10 @@ +import { useAuthStore } from "@/stores" + +export default defineNuxtRouteMiddleware((to, from) => { + const authStore = useAuthStore() + const routes = ["/login", "/join", "/recover-password", "/reset-password"] + if (!authStore.loggedIn) { + if (routes.includes(from.path)) return navigateTo("/") + else return abortNavigation() + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/middleware/moderator.ts b/{{cookiecutter.project_slug}}/frontend/middleware/moderator.ts new file mode 100644 index 0000000000..9d807ff2fa --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/middleware/moderator.ts @@ -0,0 +1,8 @@ +import { useAuthStore } from "@/stores" + +export default defineNuxtRouteMiddleware((to, from) => { + const authStore = useAuthStore() + if (!authStore.isAdmin) { + return abortNavigation() + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/middleware/refresh.ts b/{{cookiecutter.project_slug}}/frontend/middleware/refresh.ts new file mode 100644 index 0000000000..9ef7c29e60 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/middleware/refresh.ts @@ -0,0 +1,8 @@ +import { useAuthStore } from "@/stores" + +export default defineNuxtRouteMiddleware(async (to, from) => { + const authStore = useAuthStore() + if (!authStore.loggedIn) { + await authStore.getUserProfile() + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/nginx-backend-not-found.conf b/{{cookiecutter.project_slug}}/frontend/nginx-backend-not-found.conf deleted file mode 100644 index f6fea66358..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/nginx-backend-not-found.conf +++ /dev/null @@ -1,9 +0,0 @@ -location /api { - return 404; -} -location /docs { - return 404; -} -location /redoc { - return 404; -} diff --git a/{{cookiecutter.project_slug}}/frontend/nuxt.config.ts b/{{cookiecutter.project_slug}}/frontend/nuxt.config.ts new file mode 100644 index 0000000000..15d3f0b0b0 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/nuxt.config.ts @@ -0,0 +1,162 @@ +// https://v3.nuxtjs.org/api/configuration/nuxt.config +export default defineNuxtConfig({ + app: { + head: { + meta: [ + { charset: "utf-8" }, + // + { name: "viewport", content: "width=device-width, initial-scale=1" } + ], + script: [ + // + // { src: "@/assets/css/main.css" } + ], + noscript: [ + // + { children: "Javascript is required" } + ] + }, + // pageTransition: { name: "page", mode: "out-in" } + }, + runtimeConfig: { + // https://nuxt.com/docs/api/composables/use-runtime-config#using-the-env-file + // Private keys are only available on the server + apiSecret: process.env.VUE_PRIVATE_TERM, + // Public keys that are exposed to the client + public: { + appName: process.env.VUE_APP_NAME, + appEnv: process.env.VUE_APP_ENV, + apiWS: process.env.VUE_APP_DOMAIN_WS, + apiUrl: process.env.VUE_APP_DOMAIN_API, + // idbName: process.env.VUE_IDB_NAME, + // idbVersion: process.env.VUE_IDB_VERSION, + } + }, + modules: [ + "@nuxtjs/i18n", + "@pinia/nuxt", + "@pinia-plugin-persistedstate/nuxt", + "@nuxt/content", + "tailwindcss", + "@nuxtjs/robots", + "@vite-pwa/nuxt", + ], + // pinia: { + // autoImports: [ + // "definePiniaStore", + // "defineStore", + // ], + // }, + piniaPersistedstate: { + cookieOptions: { + path: "/", + // maxAge: 60 * 60 * 24 * 30, + secure: true, + }, + }, + content: { + // https://content.nuxtjs.org/api/configuration + // https://stackblitz.com/edit/nuxt-starter-jnysug + // https://stackoverflow.com/q/76421724 + navigation: { + fields: ["title", "author", "publishedAt"] + }, + locales: ["en", "fr"], + defaultLocale: "en", + }, + i18n: { + // https://phrase.com/blog/posts/nuxt-js-tutorial-i18n/ + // https://v8.i18n.nuxtjs.org/ + // https://stackblitz.com/edit/nuxt-starter-jnysug + locales: [ + { + code: "en", + name: "English", + iso: "en-GB", + dir: "ltr", + file: "en-GB.ts", + }, + { + code: "fr", + name: "Français", + iso: "fr-FR", + dir: "ltr", + file: "fr-FR.ts", + } + ], + defaultLocale: "en", + detectBrowserLanguage: false, + lazy: true, + langDir: "locales", + strategy: "prefix_and_default", + vueI18n: "./config/i18n.ts", + }, + robots: { + // https://nuxt.com/modules/robots + rules: [ + { + UserAgent: "GPTBot", + Disallow: "/" + }, + ] + }, + pwa: { + // https://vite-pwa-org.netlify.app/frameworks/nuxt.html + // https://github.com/vite-pwa/nuxt/blob/main/playground + // Generate icons with: + // node node_modules/@vite-pwa/assets-generator/bin/pwa-assets-generator.mjs --preset minimal public/images/logo.svg + registerType: "autoUpdate", + manifest: { + name: "Nuxt FastAPI Base App", + short_name: "NuxtFastAPIApp", + theme_color: "#f43f5e", + icons: [ + { + src: 'pwa-64x64.png', + sizes: '64x64', + type: 'image/png' + }, + { + src: 'pwa-192x192.png', + sizes: '192x192', + type: 'image/png' + }, + { + src: 'pwa-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'any' + }, + { + src: 'maskable-icon-512x512.png', + sizes: '512x512', + type: 'image/png', + purpose: 'maskable' + } + ], + }, + workbox: { + navigateFallback: "/", + globPatterns: ["**/*.{js,json,css,html,txt,svg,png,icon,ebpt,woff,woff2,ttf,eot,otf,wasm}"] + }, + client: { + installPrompt: true, + }, + devOptions: { + enabled: true, + suppressWarnings: true, + navigateFallbackAllowlist: [/^\/$/], + type: "module", + }, + }, + css: ["~/assets/css/main.css"], + postcss: { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, + }, + build: { + transpile: ["@heroicons/vue"] + } +}) diff --git a/{{cookiecutter.project_slug}}/frontend/package.json b/{{cookiecutter.project_slug}}/frontend/package.json index a83c616980..b3b2d39a83 100644 --- a/{{cookiecutter.project_slug}}/frontend/package.json +++ b/{{cookiecutter.project_slug}}/frontend/package.json @@ -1,74 +1,35 @@ { - "name": "frontend", - "version": "0.1.0", "private": true, "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "test:unit": "vue-cli-service test:unit", - "lint": "vue-cli-service lint" - }, - "dependencies": { - "@babel/polyfill": "^7.2.5", - "axios": "^0.18.0", - "core-js": "^3.4.3", - "register-service-worker": "^1.0.0", - "typesafe-vuex": "^3.1.1", - "vee-validate": "^2.1.7", - "vue": "^2.5.22", - "vue-class-component": "^6.0.0", - "vue-property-decorator": "^7.3.0", - "vue-router": "^3.0.2", - "vuetify": "^1.4.4", - "vuex": "^3.1.0" + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "start": "nuxt start", + "preview": "nuxt preview" }, "devDependencies": { - "@types/jest": "^23.3.13", - "@vue/cli-plugin-babel": "^4.1.1", - "@vue/cli-plugin-pwa": "^4.1.1", - "@vue/cli-plugin-typescript": "^4.1.1", - "@vue/cli-plugin-unit-jest": "^4.1.1", - "@vue/cli-service": "^4.1.1", - "@vue/test-utils": "^1.0.0-beta.28", - "babel-core": "7.0.0-bridge.0", - "ts-jest": "^23.10.5", - "typescript": "^3.2.4", - "vue-cli-plugin-vuetify": "^2.0.2", - "vue-template-compiler": "^2.5.22" + "@nuxt/content": "^2.12.1", + "@nuxtjs/i18n": "^8.3.1", + "@pinia-plugin-persistedstate/nuxt": "^1.2.0", + "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/forms": "^0.5.7", + "@tailwindcss/typography": "^0.5.13", + "@vite-pwa/assets-generator": "^0.2.4", + "@vite-pwa/nuxt": "^0.7.0", + "autoprefixer": "^10.4.19", + "nuxt": "^3.11.2", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.3" }, - "postcss": { - "plugins": { - "autoprefixer": {} - } - }, - "browserslist": [ - "> 1%", - "last 2 versions", - "not ie <= 10" - ], - "jest": { - "moduleFileExtensions": [ - "js", - "jsx", - "json", - "vue", - "ts", - "tsx" - ], - "transform": { - "^.+\\.vue$": "vue-jest", - ".+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$": "jest-transform-stub", - "^.+\\.tsx?$": "ts-jest" - }, - "moduleNameMapper": { - "^@/(.*)$": "/src/$1" - }, - "snapshotSerializers": [ - "jest-serializer-vue" - ], - "testMatch": [ - "**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)" - ], - "testURL": "http://localhost/" + "dependencies": { + "@headlessui/vue": "^1.7.21", + "@heroicons/vue": "2.1.3", + "@nuxtjs/robots": "^3.0.0", + "@pinia/nuxt": "^0.5.1", + "@vee-validate/i18n": "^4.12.6", + "@vee-validate/rules": "^4.12.6", + "pinia": "^2.1.7", + "qrcode.vue": "^3.3.3", + "vee-validate": "^4.12.6" } } diff --git a/{{cookiecutter.project_slug}}/frontend/pages/[...slug].vue b/{{cookiecutter.project_slug}}/frontend/pages/[...slug].vue new file mode 100644 index 0000000000..d6fb75c0dd --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/[...slug].vue @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/blog/[...slug].vue b/{{cookiecutter.project_slug}}/frontend/pages/blog/[...slug].vue new file mode 100644 index 0000000000..d24b6efc52 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/blog/[...slug].vue @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/blog/index.vue b/{{cookiecutter.project_slug}}/frontend/pages/blog/index.vue new file mode 100644 index 0000000000..f6e2792147 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/blog/index.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/contact.vue b/{{cookiecutter.project_slug}}/frontend/pages/contact.vue new file mode 100644 index 0000000000..dec66ac87c --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/contact.vue @@ -0,0 +1,124 @@ + + + diff --git a/{{cookiecutter.project_slug}}/frontend/pages/index.vue b/{{cookiecutter.project_slug}}/frontend/pages/index.vue new file mode 100644 index 0000000000..d4d5a528cc --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/index.vue @@ -0,0 +1,127 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/login.vue b/{{cookiecutter.project_slug}}/frontend/pages/login.vue new file mode 100644 index 0000000000..5c43127c12 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/login.vue @@ -0,0 +1,105 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/magic.vue b/{{cookiecutter.project_slug}}/frontend/pages/magic.vue new file mode 100644 index 0000000000..59e1f0ee80 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/magic.vue @@ -0,0 +1,56 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/moderation.vue b/{{cookiecutter.project_slug}}/frontend/pages/moderation.vue new file mode 100644 index 0000000000..ddfc0d0b91 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/moderation.vue @@ -0,0 +1,69 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/recover-password.vue b/{{cookiecutter.project_slug}}/frontend/pages/recover-password.vue new file mode 100644 index 0000000000..bc993cd9f0 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/recover-password.vue @@ -0,0 +1,63 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/reset-password.vue b/{{cookiecutter.project_slug}}/frontend/pages/reset-password.vue new file mode 100644 index 0000000000..744c3fffd0 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/reset-password.vue @@ -0,0 +1,75 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/settings.vue b/{{cookiecutter.project_slug}}/frontend/pages/settings.vue new file mode 100644 index 0000000000..576c95b11d --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/settings.vue @@ -0,0 +1,76 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/pages/totp.vue b/{{cookiecutter.project_slug}}/frontend/pages/totp.vue new file mode 100644 index 0000000000..b27ce39569 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/pages/totp.vue @@ -0,0 +1,67 @@ + + + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/plugins/veevalidate-components.ts b/{{cookiecutter.project_slug}}/frontend/plugins/veevalidate-components.ts new file mode 100644 index 0000000000..8e12160793 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/plugins/veevalidate-components.ts @@ -0,0 +1,7 @@ +import { Form, Field, ErrorMessage } from "vee-validate"; + +export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.vueApp.component("Form", Form); + nuxtApp.vueApp.component("Field", Field); + nuxtApp.vueApp.component("ErrorMessage", ErrorMessage); + }); \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/plugins/veevalidate-rules.ts b/{{cookiecutter.project_slug}}/frontend/plugins/veevalidate-rules.ts new file mode 100644 index 0000000000..a9948da7c4 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/plugins/veevalidate-rules.ts @@ -0,0 +1,41 @@ +import { defineRule, configure } from "vee-validate"; +import { required, email, min, max, url } from "@vee-validate/rules"; +import { localize } from "@vee-validate/i18n"; + +export default defineNuxtPlugin((nuxtApp) => { + defineRule("required", required); + defineRule("email", email); + defineRule("min", min); + defineRule("max", max); + defineRule("url", url); + // @ts-ignore + defineRule("confirmed", (value, [target], ctx) => { + // https://vee-validate.logaretm.com/v4/guide/global-validators#cross-field-validation + if (value === ctx.form[target]) { + return true; + } + return "Passwords must match."; + }); +}); + +configure({ + // Generates an English message locale generator + generateMessage: localize("en", { + messages: { + required: "This field is required.", + email: "This email address is invalid.", + min: "Passwords must be 8 to 64 characters long.", + max: "Passwords must be 8 to 64 characters long.", + url: "This url is invalid.", + }, + }), +}); + +/* + References: + + https://vee-validate.logaretm.com/v4/guide/overview/ + https://github.com/razorcx-courses/nuxt3-veevalidate + https://vee-validate.logaretm.com/v4/guide/global-validators/#available-rules + https://vee-validate.logaretm.com/v4/guide/i18n +*/ \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/public/favicon.ico b/{{cookiecutter.project_slug}}/frontend/public/favicon.ico deleted file mode 100644 index c7b9a43c8c..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/favicon.ico and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/apple-touch-icon-180x180.png b/{{cookiecutter.project_slug}}/frontend/public/images/apple-touch-icon-180x180.png new file mode 100644 index 0000000000..37cf12d69d Binary files /dev/null and b/{{cookiecutter.project_slug}}/frontend/public/images/apple-touch-icon-180x180.png differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/favicon.ico b/{{cookiecutter.project_slug}}/frontend/public/images/favicon.ico new file mode 100644 index 0000000000..43ed2e88d2 Binary files /dev/null and b/{{cookiecutter.project_slug}}/frontend/public/images/favicon.ico differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/logo.svg b/{{cookiecutter.project_slug}}/frontend/public/images/logo.svg new file mode 100644 index 0000000000..da55779c48 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/public/images/logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/mark.svg b/{{cookiecutter.project_slug}}/frontend/public/images/mark.svg new file mode 100644 index 0000000000..fe4b9b17e3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/public/images/mark.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/maskable-icon-512x512.png b/{{cookiecutter.project_slug}}/frontend/public/images/maskable-icon-512x512.png new file mode 100644 index 0000000000..f783d0d7e4 Binary files /dev/null and b/{{cookiecutter.project_slug}}/frontend/public/images/maskable-icon-512x512.png differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/pwa-192x192.png b/{{cookiecutter.project_slug}}/frontend/public/images/pwa-192x192.png new file mode 100644 index 0000000000..b4386e57f3 Binary files /dev/null and b/{{cookiecutter.project_slug}}/frontend/public/images/pwa-192x192.png differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/pwa-512x512.png b/{{cookiecutter.project_slug}}/frontend/public/images/pwa-512x512.png new file mode 100644 index 0000000000..b3e491b4a3 Binary files /dev/null and b/{{cookiecutter.project_slug}}/frontend/public/images/pwa-512x512.png differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/images/pwa-64x64.png b/{{cookiecutter.project_slug}}/frontend/public/images/pwa-64x64.png new file mode 100644 index 0000000000..390f59fe80 Binary files /dev/null and b/{{cookiecutter.project_slug}}/frontend/public/images/pwa-64x64.png differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/android-chrome-192x192.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/android-chrome-192x192.png deleted file mode 100644 index b02aa64d97..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/android-chrome-192x192.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/android-chrome-512x512.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/android-chrome-512x512.png deleted file mode 100644 index 06088b011e..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/android-chrome-512x512.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-120x120.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-120x120.png deleted file mode 100644 index 1427cf6275..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-120x120.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-152x152.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-152x152.png deleted file mode 100644 index f24d454a2e..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-152x152.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-180x180.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-180x180.png deleted file mode 100644 index 404e192a95..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-180x180.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-60x60.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-60x60.png deleted file mode 100644 index cf10a5602e..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-60x60.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-76x76.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-76x76.png deleted file mode 100644 index c500769e3d..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon-76x76.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon.png deleted file mode 100644 index 03c0c5d5ec..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/apple-touch-icon.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/favicon-16x16.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/favicon-16x16.png deleted file mode 100644 index 42af00963d..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/favicon-16x16.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/favicon-32x32.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/favicon-32x32.png deleted file mode 100644 index 46ca04dee2..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/favicon-32x32.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/msapplication-icon-144x144.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/msapplication-icon-144x144.png deleted file mode 100644 index 7808237a18..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/msapplication-icon-144x144.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/mstile-150x150.png b/{{cookiecutter.project_slug}}/frontend/public/img/icons/mstile-150x150.png deleted file mode 100644 index 3b37a43ae2..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/public/img/icons/mstile-150x150.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/public/img/icons/safari-pinned-tab.svg b/{{cookiecutter.project_slug}}/frontend/public/img/icons/safari-pinned-tab.svg deleted file mode 100644 index 732afd8eb0..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/public/img/icons/safari-pinned-tab.svg +++ /dev/null @@ -1,149 +0,0 @@ - - - - -Created by potrace 1.11, written by Peter Selinger 2001-2013 - - - - - diff --git a/{{cookiecutter.project_slug}}/frontend/public/index.html b/{{cookiecutter.project_slug}}/frontend/public/index.html deleted file mode 100644 index cad5aa7efa..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/public/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - <%= VUE_APP_NAME %> - - - - - - - -
- - - diff --git a/{{cookiecutter.project_slug}}/frontend/public/manifest.json b/{{cookiecutter.project_slug}}/frontend/public/manifest.json deleted file mode 100644 index 8ce10b9116..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/public/manifest.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "frontend", - "short_name": "frontend", - "icons": [ - { - "src": "/img/icons/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/img/icons/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "start_url": "/", - "display": "standalone", - "background_color": "#000000", - "theme_color": "#4DBA87" -} diff --git a/{{cookiecutter.project_slug}}/frontend/public/robots.txt b/{{cookiecutter.project_slug}}/frontend/public/robots.txt deleted file mode 100644 index eb0536286f..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: diff --git a/{{cookiecutter.project_slug}}/frontend/src/App.vue b/{{cookiecutter.project_slug}}/frontend/src/App.vue deleted file mode 100644 index 795a97c955..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/App.vue +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/api.ts b/{{cookiecutter.project_slug}}/frontend/src/api.ts deleted file mode 100644 index c24712b872..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/api.ts +++ /dev/null @@ -1,45 +0,0 @@ -import axios from 'axios'; -import { apiUrl } from '@/env'; -import { IUserProfile, IUserProfileUpdate, IUserProfileCreate } from './interfaces'; - -function authHeaders(token: string) { - return { - headers: { - Authorization: `Bearer ${token}`, - }, - }; -} - -export const api = { - async logInGetToken(username: string, password: string) { - const params = new URLSearchParams(); - params.append('username', username); - params.append('password', password); - - return axios.post(`${apiUrl}/api/v1/login/access-token`, params); - }, - async getMe(token: string) { - return axios.get(`${apiUrl}/api/v1/users/me`, authHeaders(token)); - }, - async updateMe(token: string, data: IUserProfileUpdate) { - return axios.put(`${apiUrl}/api/v1/users/me`, data, authHeaders(token)); - }, - async getUsers(token: string) { - return axios.get(`${apiUrl}/api/v1/users/`, authHeaders(token)); - }, - async updateUser(token: string, userId: number, data: IUserProfileUpdate) { - return axios.put(`${apiUrl}/api/v1/users/${userId}`, data, authHeaders(token)); - }, - async createUser(token: string, data: IUserProfileCreate) { - return axios.post(`${apiUrl}/api/v1/users/`, data, authHeaders(token)); - }, - async passwordRecovery(email: string) { - return axios.post(`${apiUrl}/api/v1/password-recovery/${email}`); - }, - async resetPassword(password: string, token: string) { - return axios.post(`${apiUrl}/api/v1/reset-password/`, { - new_password: password, - token, - }); - }, -}; diff --git a/{{cookiecutter.project_slug}}/frontend/src/assets/logo.png b/{{cookiecutter.project_slug}}/frontend/src/assets/logo.png deleted file mode 100644 index f3d2503fc2..0000000000 Binary files a/{{cookiecutter.project_slug}}/frontend/src/assets/logo.png and /dev/null differ diff --git a/{{cookiecutter.project_slug}}/frontend/src/component-hooks.ts b/{{cookiecutter.project_slug}}/frontend/src/component-hooks.ts deleted file mode 100644 index cefdc537bb..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/component-hooks.ts +++ /dev/null @@ -1,8 +0,0 @@ -import Component from 'vue-class-component'; - -// Register the router hooks with their names -Component.registerHooks([ - 'beforeRouteEnter', - 'beforeRouteLeave', - 'beforeRouteUpdate', // for vue-router 2.2+ -]); diff --git a/{{cookiecutter.project_slug}}/frontend/src/components/NotificationsManager.vue b/{{cookiecutter.project_slug}}/frontend/src/components/NotificationsManager.vue deleted file mode 100644 index 6fcffdb789..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/components/NotificationsManager.vue +++ /dev/null @@ -1,77 +0,0 @@ - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/components/RouterComponent.vue b/{{cookiecutter.project_slug}}/frontend/src/components/RouterComponent.vue deleted file mode 100644 index ed986a6fda..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/components/RouterComponent.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/components/UploadButton.vue b/{{cookiecutter.project_slug}}/frontend/src/components/UploadButton.vue deleted file mode 100644 index 8902e949e9..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/components/UploadButton.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/env.ts b/{{cookiecutter.project_slug}}/frontend/src/env.ts deleted file mode 100644 index b3387e69bc..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/env.ts +++ /dev/null @@ -1,14 +0,0 @@ -const env = process.env.VUE_APP_ENV; - -let envApiUrl = ''; - -if (env === 'production') { - envApiUrl = `https://${process.env.VUE_APP_DOMAIN_PROD}`; -} else if (env === 'staging') { - envApiUrl = `https://${process.env.VUE_APP_DOMAIN_STAG}`; -} else { - envApiUrl = `http://${process.env.VUE_APP_DOMAIN_DEV}`; -} - -export const apiUrl = envApiUrl; -export const appName = process.env.VUE_APP_NAME; diff --git a/{{cookiecutter.project_slug}}/frontend/src/interfaces/index.ts b/{{cookiecutter.project_slug}}/frontend/src/interfaces/index.ts deleted file mode 100644 index a1b93403cf..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/interfaces/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface IUserProfile { - email: string; - is_active: boolean; - is_superuser: boolean; - full_name: string; - id: number; -} - -export interface IUserProfileUpdate { - email?: string; - full_name?: string; - password?: string; - is_active?: boolean; - is_superuser?: boolean; -} - -export interface IUserProfileCreate { - email: string; - full_name?: string; - password?: string; - is_active?: boolean; - is_superuser?: boolean; -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/main.ts b/{{cookiecutter.project_slug}}/frontend/src/main.ts deleted file mode 100644 index a844b1eab8..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/main.ts +++ /dev/null @@ -1,19 +0,0 @@ -import '@babel/polyfill'; -// Import Component hooks before component definitions -import './component-hooks'; -import Vue from 'vue'; -import './plugins/vuetify'; -import './plugins/vee-validate'; -import App from './App.vue'; -import router from './router'; -import store from '@/store'; -import './registerServiceWorker'; -import 'vuetify/dist/vuetify.min.css'; - -Vue.config.productionTip = false; - -new Vue({ - router, - store, - render: (h) => h(App), -}).$mount('#app'); diff --git a/{{cookiecutter.project_slug}}/frontend/src/plugins/vee-validate.ts b/{{cookiecutter.project_slug}}/frontend/src/plugins/vee-validate.ts deleted file mode 100644 index 9c4238f2f7..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/plugins/vee-validate.ts +++ /dev/null @@ -1,4 +0,0 @@ -import Vue from 'vue'; -import VeeValidate from 'vee-validate'; - -Vue.use(VeeValidate); diff --git a/{{cookiecutter.project_slug}}/frontend/src/plugins/vuetify.ts b/{{cookiecutter.project_slug}}/frontend/src/plugins/vuetify.ts deleted file mode 100644 index 8fdfce3a4a..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/plugins/vuetify.ts +++ /dev/null @@ -1,6 +0,0 @@ -import Vue from 'vue'; -import Vuetify from 'vuetify'; - -Vue.use(Vuetify, { - iconfont: 'md', -}); diff --git a/{{cookiecutter.project_slug}}/frontend/src/registerServiceWorker.ts b/{{cookiecutter.project_slug}}/frontend/src/registerServiceWorker.ts deleted file mode 100644 index d3db583898..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/registerServiceWorker.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* tslint:disable:no-console */ - -import { register } from 'register-service-worker'; - -if (process.env.NODE_ENV === 'production') { - register(`${process.env.BASE_URL}service-worker.js`, { - ready() { - console.log( - 'App is being served from cache by a service worker.\n' + - 'For more details, visit https://goo.gl/AFskqB', - ); - }, - cached() { - console.log('Content has been cached for offline use.'); - }, - updated() { - console.log('New content is available; please refresh.'); - }, - offline() { - console.log('No internet connection found. App is running in offline mode.'); - }, - error(error) { - console.error('Error during service worker registration:', error); - }, - }); -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/router.ts b/{{cookiecutter.project_slug}}/frontend/src/router.ts deleted file mode 100644 index b649c173ad..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/router.ts +++ /dev/null @@ -1,97 +0,0 @@ -import Vue from 'vue'; -import Router from 'vue-router'; - -import RouterComponent from './components/RouterComponent.vue'; - -Vue.use(Router); - -export default new Router({ - mode: 'history', - base: process.env.BASE_URL, - routes: [ - { - path: '/', - component: () => import(/* webpackChunkName: "start" */ './views/main/Start.vue'), - children: [ - { - path: 'login', - // route level code-splitting - // this generates a separate chunk (about.[hash].js) for this route - // which is lazy-loaded when the route is visited. - component: () => import(/* webpackChunkName: "login" */ './views/Login.vue'), - }, - { - path: 'recover-password', - component: () => import(/* webpackChunkName: "recover-password" */ './views/PasswordRecovery.vue'), - }, - { - path: 'reset-password', - component: () => import(/* webpackChunkName: "reset-password" */ './views/ResetPassword.vue'), - }, - { - path: 'main', - component: () => import(/* webpackChunkName: "main" */ './views/main/Main.vue'), - children: [ - { - path: 'dashboard', - component: () => import(/* webpackChunkName: "main-dashboard" */ './views/main/Dashboard.vue'), - }, - { - path: 'profile', - component: RouterComponent, - redirect: 'profile/view', - children: [ - { - path: 'view', - component: () => import( - /* webpackChunkName: "main-profile" */ './views/main/profile/UserProfile.vue'), - }, - { - path: 'edit', - component: () => import( - /* webpackChunkName: "main-profile-edit" */ './views/main/profile/UserProfileEdit.vue'), - }, - { - path: 'password', - component: () => import( - /* webpackChunkName: "main-profile-password" */ './views/main/profile/UserProfileEditPassword.vue'), - }, - ], - }, - { - path: 'admin', - component: () => import(/* webpackChunkName: "main-admin" */ './views/main/admin/Admin.vue'), - redirect: 'admin/users/all', - children: [ - { - path: 'users', - redirect: 'users/all', - }, - { - path: 'users/all', - component: () => import( - /* webpackChunkName: "main-admin-users" */ './views/main/admin/AdminUsers.vue'), - }, - { - path: 'users/edit/:id', - name: 'main-admin-users-edit', - component: () => import( - /* webpackChunkName: "main-admin-users-edit" */ './views/main/admin/EditUser.vue'), - }, - { - path: 'users/create', - name: 'main-admin-users-create', - component: () => import( - /* webpackChunkName: "main-admin-users-create" */ './views/main/admin/CreateUser.vue'), - }, - ], - }, - ], - }, - ], - }, - { - path: '/*', redirect: '/', - }, - ], -}); diff --git a/{{cookiecutter.project_slug}}/frontend/src/shims-tsx.d.ts b/{{cookiecutter.project_slug}}/frontend/src/shims-tsx.d.ts deleted file mode 100644 index 3b88b58292..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/shims-tsx.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -import Vue, { VNode } from 'vue'; - -declare global { - namespace JSX { - // tslint:disable no-empty-interface - interface Element extends VNode {} - // tslint:disable no-empty-interface - interface ElementClass extends Vue {} - interface IntrinsicElements { - [elem: string]: any; - } - } -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/shims-vue.d.ts b/{{cookiecutter.project_slug}}/frontend/src/shims-vue.d.ts deleted file mode 100644 index 8f6f410263..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/shims-vue.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.vue' { - import Vue from 'vue'; - export default Vue; -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/admin/actions.ts b/{{cookiecutter.project_slug}}/frontend/src/store/admin/actions.ts deleted file mode 100644 index 125a08e6fd..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/admin/actions.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { api } from '@/api'; -import { ActionContext } from 'vuex'; -import { IUserProfileCreate, IUserProfileUpdate } from '@/interfaces'; -import { State } from '../state'; -import { AdminState } from './state'; -import { getStoreAccessors } from 'typesafe-vuex'; -import { commitSetUsers, commitSetUser } from './mutations'; -import { dispatchCheckApiError } from '../main/actions'; -import { commitAddNotification, commitRemoveNotification } from '../main/mutations'; - -type MainContext = ActionContext; - -export const actions = { - async actionGetUsers(context: MainContext) { - try { - const response = await api.getUsers(context.rootState.main.token); - if (response) { - commitSetUsers(context, response.data); - } - } catch (error) { - await dispatchCheckApiError(context, error); - } - }, - async actionUpdateUser(context: MainContext, payload: { id: number, user: IUserProfileUpdate }) { - try { - const loadingNotification = { content: 'saving', showProgress: true }; - commitAddNotification(context, loadingNotification); - const response = (await Promise.all([ - api.updateUser(context.rootState.main.token, payload.id, payload.user), - await new Promise((resolve, reject) => setTimeout(() => resolve(), 500)), - ]))[0]; - commitSetUser(context, response.data); - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { content: 'User successfully updated', color: 'success' }); - } catch (error) { - await dispatchCheckApiError(context, error); - } - }, - async actionCreateUser(context: MainContext, payload: IUserProfileCreate) { - try { - const loadingNotification = { content: 'saving', showProgress: true }; - commitAddNotification(context, loadingNotification); - const response = (await Promise.all([ - api.createUser(context.rootState.main.token, payload), - await new Promise((resolve, reject) => setTimeout(() => resolve(), 500)), - ]))[0]; - commitSetUser(context, response.data); - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { content: 'User successfully created', color: 'success' }); - } catch (error) { - await dispatchCheckApiError(context, error); - } - }, -}; - -const { dispatch } = getStoreAccessors(''); - -export const dispatchCreateUser = dispatch(actions.actionCreateUser); -export const dispatchGetUsers = dispatch(actions.actionGetUsers); -export const dispatchUpdateUser = dispatch(actions.actionUpdateUser); diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/admin/getters.ts b/{{cookiecutter.project_slug}}/frontend/src/store/admin/getters.ts deleted file mode 100644 index c5832ef449..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/admin/getters.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { AdminState } from './state'; -import { getStoreAccessors } from 'typesafe-vuex'; -import { State } from '../state'; - -export const getters = { - adminUsers: (state: AdminState) => state.users, - adminOneUser: (state: AdminState) => (userId: number) => { - const filteredUsers = state.users.filter((user) => user.id === userId); - if (filteredUsers.length > 0) { - return { ...filteredUsers[0] }; - } - }, -}; - -const { read } = getStoreAccessors(''); - -export const readAdminOneUser = read(getters.adminOneUser); -export const readAdminUsers = read(getters.adminUsers); diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/admin/index.ts b/{{cookiecutter.project_slug}}/frontend/src/store/admin/index.ts deleted file mode 100644 index dcaf6abbd1..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/admin/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { mutations } from './mutations'; -import { getters } from './getters'; -import { actions } from './actions'; -import { AdminState } from './state'; - -const defaultState: AdminState = { - users: [], -}; - -export const adminModule = { - state: defaultState, - mutations, - actions, - getters, -}; diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/admin/mutations.ts b/{{cookiecutter.project_slug}}/frontend/src/store/admin/mutations.ts deleted file mode 100644 index dea471d4e7..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/admin/mutations.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IUserProfile } from '@/interfaces'; -import { AdminState } from './state'; -import { getStoreAccessors } from 'typesafe-vuex'; -import { State } from '../state'; - -export const mutations = { - setUsers(state: AdminState, payload: IUserProfile[]) { - state.users = payload; - }, - setUser(state: AdminState, payload: IUserProfile) { - const users = state.users.filter((user: IUserProfile) => user.id !== payload.id); - users.push(payload); - state.users = users; - }, -}; - -const { commit } = getStoreAccessors(''); - -export const commitSetUser = commit(mutations.setUser); -export const commitSetUsers = commit(mutations.setUsers); diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/admin/state.ts b/{{cookiecutter.project_slug}}/frontend/src/store/admin/state.ts deleted file mode 100644 index 8dfefe2f99..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/admin/state.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IUserProfile } from '@/interfaces'; - -export interface AdminState { - users: IUserProfile[]; -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/index.ts b/{{cookiecutter.project_slug}}/frontend/src/store/index.ts deleted file mode 100644 index 1089971525..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Vue from 'vue'; -import Vuex, { StoreOptions } from 'vuex'; - -import { mainModule } from './main'; -import { State } from './state'; -import { adminModule } from './admin'; - -Vue.use(Vuex); - -const storeOptions: StoreOptions = { - modules: { - main: mainModule, - admin: adminModule, - }, -}; - -export const store = new Vuex.Store(storeOptions); - -export default store; diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/main/actions.ts b/{{cookiecutter.project_slug}}/frontend/src/store/main/actions.ts deleted file mode 100644 index d02c06d53c..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/main/actions.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { api } from '@/api'; -import router from '@/router'; -import { getLocalToken, removeLocalToken, saveLocalToken } from '@/utils'; -import { AxiosError } from 'axios'; -import { getStoreAccessors } from 'typesafe-vuex'; -import { ActionContext } from 'vuex'; -import { State } from '../state'; -import { - commitAddNotification, - commitRemoveNotification, - commitSetLoggedIn, - commitSetLogInError, - commitSetToken, - commitSetUserProfile, -} from './mutations'; -import { AppNotification, MainState } from './state'; - -type MainContext = ActionContext; - -export const actions = { - async actionLogIn(context: MainContext, payload: { username: string; password: string }) { - try { - const response = await api.logInGetToken(payload.username, payload.password); - const token = response.data.access_token; - if (token) { - saveLocalToken(token); - commitSetToken(context, token); - commitSetLoggedIn(context, true); - commitSetLogInError(context, false); - await dispatchGetUserProfile(context); - await dispatchRouteLoggedIn(context); - commitAddNotification(context, { content: 'Logged in', color: 'success' }); - } else { - await dispatchLogOut(context); - } - } catch (err) { - commitSetLogInError(context, true); - await dispatchLogOut(context); - } - }, - async actionGetUserProfile(context: MainContext) { - try { - const response = await api.getMe(context.state.token); - if (response.data) { - commitSetUserProfile(context, response.data); - } - } catch (error) { - await dispatchCheckApiError(context, error); - } - }, - async actionUpdateUserProfile(context: MainContext, payload) { - try { - const loadingNotification = { content: 'saving', showProgress: true }; - commitAddNotification(context, loadingNotification); - const response = (await Promise.all([ - api.updateMe(context.state.token, payload), - await new Promise((resolve, reject) => setTimeout(() => resolve(), 500)), - ]))[0]; - commitSetUserProfile(context, response.data); - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { content: 'Profile successfully updated', color: 'success' }); - } catch (error) { - await dispatchCheckApiError(context, error); - } - }, - async actionCheckLoggedIn(context: MainContext) { - if (!context.state.isLoggedIn) { - let token = context.state.token; - if (!token) { - const localToken = getLocalToken(); - if (localToken) { - commitSetToken(context, localToken); - token = localToken; - } - } - if (token) { - try { - const response = await api.getMe(token); - commitSetLoggedIn(context, true); - commitSetUserProfile(context, response.data); - } catch (error) { - await dispatchRemoveLogIn(context); - } - } else { - await dispatchRemoveLogIn(context); - } - } - }, - async actionRemoveLogIn(context: MainContext) { - removeLocalToken(); - commitSetToken(context, ''); - commitSetLoggedIn(context, false); - }, - async actionLogOut(context: MainContext) { - await dispatchRemoveLogIn(context); - await dispatchRouteLogOut(context); - }, - async actionUserLogOut(context: MainContext) { - await dispatchLogOut(context); - commitAddNotification(context, { content: 'Logged out', color: 'success' }); - }, - actionRouteLogOut(context: MainContext) { - if (router.currentRoute.path !== '/login') { - router.push('/login'); - } - }, - async actionCheckApiError(context: MainContext, payload: AxiosError) { - if (payload.response!.status === 401) { - await dispatchLogOut(context); - } - }, - actionRouteLoggedIn(context: MainContext) { - if (router.currentRoute.path === '/login' || router.currentRoute.path === '/') { - router.push('/main'); - } - }, - async removeNotification(context: MainContext, payload: { notification: AppNotification, timeout: number }) { - return new Promise((resolve, reject) => { - setTimeout(() => { - commitRemoveNotification(context, payload.notification); - resolve(true); - }, payload.timeout); - }); - }, - async passwordRecovery(context: MainContext, payload: { username: string }) { - const loadingNotification = { content: 'Sending password recovery email', showProgress: true }; - try { - commitAddNotification(context, loadingNotification); - const response = (await Promise.all([ - api.passwordRecovery(payload.username), - await new Promise((resolve, reject) => setTimeout(() => resolve(), 500)), - ]))[0]; - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { content: 'Password recovery email sent', color: 'success' }); - await dispatchLogOut(context); - } catch (error) { - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { color: 'error', content: 'Incorrect username' }); - } - }, - async resetPassword(context: MainContext, payload: { password: string, token: string }) { - const loadingNotification = { content: 'Resetting password', showProgress: true }; - try { - commitAddNotification(context, loadingNotification); - const response = (await Promise.all([ - api.resetPassword(payload.password, payload.token), - await new Promise((resolve, reject) => setTimeout(() => resolve(), 500)), - ]))[0]; - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { content: 'Password successfully reset', color: 'success' }); - await dispatchLogOut(context); - } catch (error) { - commitRemoveNotification(context, loadingNotification); - commitAddNotification(context, { color: 'error', content: 'Error resetting password' }); - } - }, -}; - -const { dispatch } = getStoreAccessors(''); - -export const dispatchCheckApiError = dispatch(actions.actionCheckApiError); -export const dispatchCheckLoggedIn = dispatch(actions.actionCheckLoggedIn); -export const dispatchGetUserProfile = dispatch(actions.actionGetUserProfile); -export const dispatchLogIn = dispatch(actions.actionLogIn); -export const dispatchLogOut = dispatch(actions.actionLogOut); -export const dispatchUserLogOut = dispatch(actions.actionUserLogOut); -export const dispatchRemoveLogIn = dispatch(actions.actionRemoveLogIn); -export const dispatchRouteLoggedIn = dispatch(actions.actionRouteLoggedIn); -export const dispatchRouteLogOut = dispatch(actions.actionRouteLogOut); -export const dispatchUpdateUserProfile = dispatch(actions.actionUpdateUserProfile); -export const dispatchRemoveNotification = dispatch(actions.removeNotification); -export const dispatchPasswordRecovery = dispatch(actions.passwordRecovery); -export const dispatchResetPassword = dispatch(actions.resetPassword); diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/main/getters.ts b/{{cookiecutter.project_slug}}/frontend/src/store/main/getters.ts deleted file mode 100644 index 58f83978fa..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/main/getters.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { MainState } from './state'; -import { getStoreAccessors } from 'typesafe-vuex'; -import { State } from '../state'; - -export const getters = { - hasAdminAccess: (state: MainState) => { - return ( - state.userProfile && - state.userProfile.is_superuser && state.userProfile.is_active); - }, - loginError: (state: MainState) => state.logInError, - dashboardShowDrawer: (state: MainState) => state.dashboardShowDrawer, - dashboardMiniDrawer: (state: MainState) => state.dashboardMiniDrawer, - userProfile: (state: MainState) => state.userProfile, - token: (state: MainState) => state.token, - isLoggedIn: (state: MainState) => state.isLoggedIn, - firstNotification: (state: MainState) => state.notifications.length > 0 && state.notifications[0], -}; - -const {read} = getStoreAccessors(''); - -export const readDashboardMiniDrawer = read(getters.dashboardMiniDrawer); -export const readDashboardShowDrawer = read(getters.dashboardShowDrawer); -export const readHasAdminAccess = read(getters.hasAdminAccess); -export const readIsLoggedIn = read(getters.isLoggedIn); -export const readLoginError = read(getters.loginError); -export const readToken = read(getters.token); -export const readUserProfile = read(getters.userProfile); -export const readFirstNotification = read(getters.firstNotification); diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/main/index.ts b/{{cookiecutter.project_slug}}/frontend/src/store/main/index.ts deleted file mode 100644 index 56ba1a0c2f..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/main/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { mutations } from './mutations'; -import { getters } from './getters'; -import { actions } from './actions'; -import { MainState } from './state'; - -const defaultState: MainState = { - isLoggedIn: null, - token: '', - logInError: false, - userProfile: null, - dashboardMiniDrawer: false, - dashboardShowDrawer: true, - notifications: [], -}; - -export const mainModule = { - state: defaultState, - mutations, - actions, - getters, -}; diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/main/mutations.ts b/{{cookiecutter.project_slug}}/frontend/src/store/main/mutations.ts deleted file mode 100644 index 3e9c8ba2c0..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/main/mutations.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { IUserProfile } from '@/interfaces'; -import { MainState, AppNotification } from './state'; -import { getStoreAccessors } from 'typesafe-vuex'; -import { State } from '../state'; - - -export const mutations = { - setToken(state: MainState, payload: string) { - state.token = payload; - }, - setLoggedIn(state: MainState, payload: boolean) { - state.isLoggedIn = payload; - }, - setLogInError(state: MainState, payload: boolean) { - state.logInError = payload; - }, - setUserProfile(state: MainState, payload: IUserProfile) { - state.userProfile = payload; - }, - setDashboardMiniDrawer(state: MainState, payload: boolean) { - state.dashboardMiniDrawer = payload; - }, - setDashboardShowDrawer(state: MainState, payload: boolean) { - state.dashboardShowDrawer = payload; - }, - addNotification(state: MainState, payload: AppNotification) { - state.notifications.push(payload); - }, - removeNotification(state: MainState, payload: AppNotification) { - state.notifications = state.notifications.filter((notification) => notification !== payload); - }, -}; - -const {commit} = getStoreAccessors(''); - -export const commitSetDashboardMiniDrawer = commit(mutations.setDashboardMiniDrawer); -export const commitSetDashboardShowDrawer = commit(mutations.setDashboardShowDrawer); -export const commitSetLoggedIn = commit(mutations.setLoggedIn); -export const commitSetLogInError = commit(mutations.setLogInError); -export const commitSetToken = commit(mutations.setToken); -export const commitSetUserProfile = commit(mutations.setUserProfile); -export const commitAddNotification = commit(mutations.addNotification); -export const commitRemoveNotification = commit(mutations.removeNotification); diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/main/state.ts b/{{cookiecutter.project_slug}}/frontend/src/store/main/state.ts deleted file mode 100644 index be24b63ae9..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/main/state.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IUserProfile } from '@/interfaces'; - -export interface AppNotification { - content: string; - color?: string; - showProgress?: boolean; -} - -export interface MainState { - token: string; - isLoggedIn: boolean | null; - logInError: boolean; - userProfile: IUserProfile | null; - dashboardMiniDrawer: boolean; - dashboardShowDrawer: boolean; - notifications: AppNotification[]; -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/store/state.ts b/{{cookiecutter.project_slug}}/frontend/src/store/state.ts deleted file mode 100644 index ecec111cd8..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/store/state.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { MainState } from './main/state'; - -export interface State { - main: MainState; -} diff --git a/{{cookiecutter.project_slug}}/frontend/src/utils.ts b/{{cookiecutter.project_slug}}/frontend/src/utils.ts deleted file mode 100644 index ade11b6a2e..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const getLocalToken = () => localStorage.getItem('token'); - -export const saveLocalToken = (token: string) => localStorage.setItem('token', token); - -export const removeLocalToken = () => localStorage.removeItem('token'); diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/Login.vue b/{{cookiecutter.project_slug}}/frontend/src/views/Login.vue deleted file mode 100644 index 28bcb5965e..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/Login.vue +++ /dev/null @@ -1,58 +0,0 @@ - - - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/PasswordRecovery.vue b/{{cookiecutter.project_slug}}/frontend/src/views/PasswordRecovery.vue deleted file mode 100644 index bc1a7ade3f..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/PasswordRecovery.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/ResetPassword.vue b/{{cookiecutter.project_slug}}/frontend/src/views/ResetPassword.vue deleted file mode 100644 index 3e680eb171..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/ResetPassword.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/Dashboard.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/Dashboard.vue deleted file mode 100644 index 421879b1b6..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/Dashboard.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/Main.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/Main.vue deleted file mode 100644 index 846d93bd40..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/Main.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/Start.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/Start.vue deleted file mode 100644 index 71eeaafeff..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/Start.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/Admin.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/Admin.vue deleted file mode 100644 index 1282176aaf..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/Admin.vue +++ /dev/null @@ -1,28 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/AdminUsers.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/AdminUsers.vue deleted file mode 100644 index 9b35d9a6c1..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/AdminUsers.vue +++ /dev/null @@ -1,83 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/CreateUser.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/CreateUser.vue deleted file mode 100644 index 892283ec6c..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/CreateUser.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/EditUser.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/EditUser.vue deleted file mode 100644 index 7421233140..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/admin/EditUser.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfile.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfile.vue deleted file mode 100644 index 25960bd42e..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfile.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfileEdit.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfileEdit.vue deleted file mode 100644 index dfbea8d874..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfileEdit.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfileEditPassword.vue b/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfileEditPassword.vue deleted file mode 100644 index 80e2cc5864..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/src/views/main/profile/UserProfileEditPassword.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - diff --git a/{{cookiecutter.project_slug}}/frontend/stores/auth.ts b/{{cookiecutter.project_slug}}/frontend/stores/auth.ts new file mode 100644 index 0000000000..4a19bc05ee --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/stores/auth.ts @@ -0,0 +1,326 @@ +import type { + IUserProfile, + IUserProfileUpdate, + IUserOpenProfileCreate, + IEnableTOTP, + IWebToken, +} from "@/interfaces" +import { apiAuth } from "@/api" +import { tokenIsTOTP, tokenParser } from "@/utilities" +import { useToastStore } from "./toasts" +import { useTokenStore } from "./tokens" + +export const useAuthStore = defineStore("authUser", { + state: (): IUserProfile => ({ + id: "", + email: "", + email_validated: false, + is_active: false, + is_superuser: false, + full_name: "", + password: false, + totp: false + }), + persist: { + storage: persistedState.cookiesWithOptions({ + // https://prazdevs.github.io/pinia-plugin-persistedstate/frameworks/nuxt-3.html + // https://nuxt.com/docs/api/composables/use-cookie#options + // in seconds + path: "/", + secure: true, + maxAge: 60 * 60 * 24 * 90, + expires: new Date(new Date().getTime() + 60 * 60 * 24 * 90), + }), + }, + getters: { + isAdmin: (state) => { + return ( + state.id && + state.is_superuser && + state.is_active + ) + }, + profile: (state) => state, + loggedIn: (state) => state.id !== "", + tokenStore: () => { + // @ts-ignore + return ( useTokenStore() ) + } + }, + actions: { + // AUTHENTICATION + async logIn(payload: { username: string; password?: string }) { + // @ts-ignore + const toasts = useToastStore() + try { + await this.tokenStore.getTokens(payload) + if (this.tokenStore.token && + !tokenIsTOTP(this.tokenStore.token) + ) await this.getUserProfile() + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Please check your details, or internet connection, and try again.", + icon: "error" + }) + this.logOut() + } + }, + async magicLogin(token: string) { + // @ts-ignore + const toasts = useToastStore() + try { + await this.tokenStore.validateMagicTokens(token) + if (this.tokenStore.token && + !tokenIsTOTP(this.tokenStore.token) + ) await this.getUserProfile() + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Please check your details, or internet connection, and try again.", + icon: "error" + }) + this.logOut() + } + }, + async totpLogin(claim: string) { + // @ts-ignore + const toasts = useToastStore() + try { + await this.tokenStore.validateTOTPClaim(claim) + if (this.tokenStore.token && + !tokenIsTOTP(this.tokenStore.token) + ) await this.getUserProfile() + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Please check your details, or internet connection, and try again.", + icon: "error" + }) + this.logOut() + } + }, + // PROFILE MANAGEMENT + async createUserProfile(payload: IUserOpenProfileCreate) { + // @ts-ignore + const toasts = useToastStore() + try { + const { data: response } = await apiAuth.createProfile(payload) + if (response.value) this.setUserProfile(response.value) + await this.tokenStore.getTokens({ + username: this.email, + password: payload.password + }) + } catch (error) { + toasts.addNotice({ + title: "Login creation error", + content: "Please check your details, or internet connection, and try again.", + icon: "error" + }) + } + }, + async getUserProfile() { + if (!this.loggedIn) { + await this.tokenStore.refreshTokens() + if (this.tokenStore.token) { + try { + const { data: response } = await apiAuth.getProfile(this.tokenStore.token) + if (response.value) this.setUserProfile(response.value) + } catch (error) { + this.logOut() + } + } + } + }, + async updateUserProfile(payload: IUserProfileUpdate) { + // @ts-ignore + const toasts = useToastStore() + await this.tokenStore.refreshTokens() + if (this.loggedIn && this.tokenStore.token) { + try { + const { data: response } = await apiAuth.updateProfile(this.tokenStore.token, payload) + if (response.value) + if (response.value) { + this.setUserProfile(response.value) + toasts.addNotice({ + title: "Profile update", + content: "Your settings have been updated.", + }) + } else throw "Error" + } catch (error) { + toasts.addNotice({ + title: "Profile update error", + content: "Please check your submission, or internet connection, and try again.", + icon: "error" + }) + } + } + }, + // MANAGING TOTP + async enableTOTPAuthentication(payload: IEnableTOTP) { + // @ts-ignore + const toasts = useToastStore() + await this.tokenStore.refreshTokens() + if (this.loggedIn && this.tokenStore.token) { + try { + const { data: response } = await apiAuth.enableTOTPAuthentication(this.tokenStore.token, payload) + if (response.value) { + this.totp = true + toasts.addNotice({ + title: "Two-factor authentication", + content: response.value.msg, + }) + } else throw "Error" + } catch (error) { + toasts.addNotice({ + title: "Error enabling two-factor authentication", + content: "Please check your submission, or internet connection, and try again.", + icon: "error" + }) + } + } + }, + async disableTOTPAuthentication(payload: IUserProfileUpdate) { + // @ts-ignore + const toasts = useToastStore() + await this.tokenStore.refreshTokens() + if (this.loggedIn && this.tokenStore.token) { + try { + const { data: response } = await apiAuth.disableTOTPAuthentication(this.tokenStore.token, payload) + if (response.value) { + this.totp = false + toasts.addNotice({ + title: "Two-factor authentication", + content: response.value.msg, + }) + } else throw "Error" + } catch (error) { + toasts.addNotice({ + title: "Error disabling two-factor authentication", + content: "Please check your submission, or internet connection, and try again.", + icon: "error" + }) + } + } + }, + // mutations are actions, instead of `state` as first argument use `this` + setUserProfile (payload: IUserProfile) { + this.id = payload.id + this.email = payload.email + this.email_validated = payload.email_validated + this.is_active = payload.is_active + this.is_superuser = payload.is_superuser + this.full_name = payload.full_name + this.password = payload.password + this.totp = payload.totp + }, + async sendEmailValidation() { + // @ts-ignore + const toasts = useToastStore() + await this.tokenStore.refreshTokens() + if (this.tokenStore.token && !this.email_validated) { + try { + const { data: response } = await apiAuth.requestValidationEmail(this.tokenStore.token) + if (response.value) { + toasts.addNotice({ + title: "Validation sent", + content: response.value.msg, + }) + } + } catch (error) { + toasts.addNotice({ + title: "Validation error", + content: "Please check your email and try again.", + icon: "error" + }) + } + } + }, + async validateEmail(validationToken: string) { + // @ts-ignore + const toasts = useToastStore() + await this.tokenStore.refreshTokens() + if (this.tokenStore.token && !this.email_validated) { + try { + const { data: response } = await apiAuth.validateEmail( + this.tokenStore.token, + validationToken + ) + if (response.value) { + this.email_validated = true + if (response.value) { + toasts.addNotice({ + title: "Success", + content: response.value.msg, + }) + } + } + } catch (error) { + toasts.addNotice({ + title: "Validation error", + content: "Invalid token. Check your email and resend validation.", + icon: "error" + }) + } + } + }, + async recoverPassword(email: string) { + // @ts-ignore + const toasts = useToastStore() + if (!this.loggedIn) { + try { + const { data: response } = await apiAuth.recoverPassword(email) + if (response.value) { + if (response.value.hasOwnProperty("claim")) + this.tokenStore.setMagicToken(response.value as unknown as IWebToken) + toasts.addNotice({ + title: "Success", + content: "If that login exists, we'll send you an email to reset your password.", + }) + } else throw "Error" + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Please check your details, or internet connection, and try again.", + icon: "error" + }) + this.tokenStore.deleteTokens() + } + } + }, + async resetPassword(password: string, token: string) { + // @ts-ignore + const toasts = useToastStore() + if (!this.loggedIn) { + try { + const claim: string = this.tokenStore.token + // Check the two magic tokens meet basic criteria + const localClaim = tokenParser(claim) + const magicClaim = tokenParser(token) + if (localClaim.hasOwnProperty("fingerprint") + && magicClaim.hasOwnProperty("fingerprint") + && localClaim["fingerprint"] === magicClaim["fingerprint"]) { + const { data: response } = await apiAuth.resetPassword(password, claim, token) + if (response.value) toasts.addNotice({ + title: "Success", + content: response.value.msg, + }) + else throw "Error" + } + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Ensure you're using the same browser and that the token hasn't expired.", + icon: "error" + }) + this.tokenStore.deleteTokens() + } + } + }, + // reset state using `$reset` + logOut () { + this.tokenStore.deleteTokens() + this.$reset() + } + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/stores/index.ts b/{{cookiecutter.project_slug}}/frontend/stores/index.ts new file mode 100644 index 0000000000..62b7861c3e --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/stores/index.ts @@ -0,0 +1,7 @@ +import { useAuthStore } from "./auth" +import { useTokenStore } from "./tokens" +import { useToastStore } from "./toasts" + +export { + useAuthStore, useTokenStore, useToastStore +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/stores/toasts.ts b/{{cookiecutter.project_slug}}/frontend/stores/toasts.ts new file mode 100644 index 0000000000..21b7776ea9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/stores/toasts.ts @@ -0,0 +1,36 @@ +import type { INotification } from "@/interfaces" +import { generateUUID } from "@/utilities" + +export const useToastStore = defineStore("toasts", { + state: () => ({ + notifications: [] as INotification[] + }), + getters: { + first: (state) => state.notifications.length > 0 && state.notifications[0], + notices: (state) => state.notifications + }, + actions: { + addNotice (payload: INotification) { + payload.uid = generateUUID() + if (!payload.icon) payload.icon = "success" + this.notices.push(payload) + }, + removeNotice (payload: INotification) { + this.notifications = this.notices.filter( + (note) => note !== payload + ) + }, + async timeoutNotice (payload: INotification, timeout: number = 2000) { + await new Promise((resolve) => { + setTimeout(() => { + this.removeNotice(payload) + resolve(true) + }, timeout) + }) + }, + // reset state using `$reset` + deleteNotices() { + this.$reset() + } + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/stores/tokens.ts b/{{cookiecutter.project_slug}}/frontend/stores/tokens.ts new file mode 100644 index 0000000000..3323b22730 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/stores/tokens.ts @@ -0,0 +1,121 @@ +import type { ITokenResponse, IWebToken } from "@/interfaces" +import { apiAuth } from "@/api" +import { tokenExpired, tokenParser } from "@/utilities" +import { useToastStore } from "./toasts" + +export const useTokenStore = defineStore("tokens", { + state: (): ITokenResponse => ({ + access_token: "", + refresh_token: "", + token_type: "" + }), + persist: true, + getters: { + token: (state) => state.access_token, + refresh: (state) => state.refresh_token + }, + actions: { + async getTokens(payload: { username: string; password?: string }) { + // @ts-ignore + const toasts = useToastStore() + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment + let response + try { + if (payload.password !== undefined) ( + { data: response } = await apiAuth.loginWithOauth( + payload.username, + payload.password + )) + else ( + { data: response } = await apiAuth.loginWithMagicLink( + payload.username + )) + if (response.value) { + if (response.value.hasOwnProperty("claim")) this.setMagicToken(response.value as unknown as IWebToken) + else this.setTokens(response.value as unknown as ITokenResponse) + } else throw "Error" + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Please check your details, or internet connection, and try again.", + icon: "error" + }) + this.deleteTokens() + } + }, + async validateMagicTokens(token: string) { + // @ts-ignore + const toasts = useToastStore() + try { + const data: string = this.token + // Check the two magic tokens meet basic criteria + const localClaim = tokenParser(data) + const magicClaim = tokenParser(token) + if (localClaim.hasOwnProperty("fingerprint") + && magicClaim.hasOwnProperty("fingerprint") + && localClaim["fingerprint"] === magicClaim["fingerprint"]) { + const { data: response } = await apiAuth.validateMagicLink( + token, { "claim": data } + ) + if (response.value) { + this.setTokens(response.value as unknown as ITokenResponse) + } else throw "Error" + } + } catch (error) { + toasts.addNotice({ + title: "Login error", + content: "Ensure you're using the same browser and that the token hasn't expired.", + icon: "error" + }) + this.deleteTokens() + } + }, + async validateTOTPClaim(data: string) { + // @ts-ignore + const toasts = useToastStore() + try { + const { data: response } = await apiAuth.loginWithTOTP( + this.access_token, { "claim": data } + ) + if (response.value) { + this.setTokens(response.value as unknown as ITokenResponse) + } else throw "Error" + } catch (error) { + toasts.addNotice({ + title: "Two-factor error", + content: "Unable to validate your verification code. Make sure it is the latest.", + icon: "error" + }) + this.deleteTokens() + } + }, + setMagicToken(payload: IWebToken) { + this.access_token = payload.claim + }, + setTokens (payload: ITokenResponse) { + this.access_token = payload.access_token + this.refresh_token = payload.refresh_token + this.token_type = payload.token_type + }, + async refreshTokens() { + let hasExpired = this.token ? tokenExpired(this.token) : true + if (hasExpired) { + hasExpired = this.refresh ? tokenExpired(this.refresh) : true + if (!hasExpired) { + try { + const { data: response } = await apiAuth.getRefreshedToken(this.refresh) + if (response.value) this.setTokens(response.value) + } catch (error) { + this.deleteTokens() + } + } else { + this.deleteTokens() + } + } + }, + // reset state using `$reset` + deleteTokens () { + this.$reset() + } + } +}) \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/tailwind.config.js b/{{cookiecutter.project_slug}}/frontend/tailwind.config.js new file mode 100644 index 0000000000..452fc45c91 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/tailwind.config.js @@ -0,0 +1,29 @@ +/** @type {import("tailwindcss").Config} */ +const colors = require("tailwindcss/colors") + +module.exports = { + content: [ + "./components/**/*.{js,vue,ts}", + "./layouts/**/*.vue", + "./pages/**/*.vue", + "./plugins/**/*.{js,ts}", + "./nuxt.config.{js,ts}", + ], + theme: { + extend: { + colors: { + teal: colors.teal, + cyan: colors.cyan, + rose: colors.rose, + }, + }, + }, + corePlugins: { + aspectRatio: false, + }, + plugins: [ + require("@tailwindcss/typography"), + require("@tailwindcss/forms"), + require("@tailwindcss/aspect-ratio"), + ], +} diff --git a/{{cookiecutter.project_slug}}/frontend/tests/unit/upload-button.spec.ts b/{{cookiecutter.project_slug}}/frontend/tests/unit/upload-button.spec.ts deleted file mode 100644 index b40eed7bea..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/tests/unit/upload-button.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { shallowMount } from '@vue/test-utils'; -import UploadButton from '@/components/UploadButton.vue'; -import '@/plugins/vuetify'; - -describe('UploadButton.vue', () => { - it('renders props.title when passed', () => { - const title = 'upload a file'; - const wrapper = shallowMount(UploadButton, { - slots: { - default: title, - }, - }); - expect(wrapper.text()).toMatch(title); - }); -}); diff --git a/{{cookiecutter.project_slug}}/frontend/tsconfig.json b/{{cookiecutter.project_slug}}/frontend/tsconfig.json index 88cfbc31d8..a7bfa186c3 100644 --- a/{{cookiecutter.project_slug}}/frontend/tsconfig.json +++ b/{{cookiecutter.project_slug}}/frontend/tsconfig.json @@ -1,41 +1,4 @@ { - "compilerOptions": { - "noImplicitAny": false, - "target": "esnext", - "module": "esnext", - "strict": true, - "jsx": "preserve", - "importHelpers": true, - "moduleResolution": "node", - "experimentalDecorators": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "sourceMap": true, - "baseUrl": ".", - "types": [ - "webpack-env", - "jest" - ], - "paths": { - "@/*": [ - "src/*" - ] - }, - "lib": [ - "esnext", - "dom", - "dom.iterable", - "scripthost" - ] - }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - "src/**/*.vue", - "tests/**/*.ts", - "tests/**/*.tsx" - ], - "exclude": [ - "node_modules" - ] + // https://v3.nuxtjs.org/concepts/typescript + "extends": "./.nuxt/tsconfig.json" } diff --git a/{{cookiecutter.project_slug}}/frontend/tslint.json b/{{cookiecutter.project_slug}}/frontend/tslint.json deleted file mode 100644 index 2b37e401c3..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/tslint.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "defaultSeverity": "warning", - "extends": [ - "tslint:recommended" - ], - "linterOptions": { - "exclude": [ - "node_modules/**" - ] - }, - "rules": { - "quotemark": [true, "single"], - "indent": [true, "spaces", 2], - "interface-name": false, - "ordered-imports": false, - "object-literal-sort-keys": false, - "no-consecutive-blank-lines": false - } -} diff --git a/{{cookiecutter.project_slug}}/frontend/utilities/generic.ts b/{{cookiecutter.project_slug}}/frontend/utilities/generic.ts new file mode 100644 index 0000000000..550c2117e3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/utilities/generic.ts @@ -0,0 +1,59 @@ +import { Buffer } from "buffer" + +function generateUUID(): string { + // Reference: https://stackoverflow.com/a/2117523/709884 + // And: https://stackoverflow.com/a/61011303/295606 + return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (s) => { + const c = Number.parseInt(s, 10) + return ( + c ^ + (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4))) + ).toString(16) + }) +} + +function isValidHttpUrl(urlString: string) { + // https://stackoverflow.com/a/43467144 + let url + try { + url = new URL(urlString) + } catch (_) { + return false + } + return url.protocol === "http:" || url.protocol === "https:" +} + +function getKeyByValue(object: any, value: any) { + // https://stackoverflow.com/a/28191966/295606 + return Object.keys(object).find((key) => object[key] === value) +} + +function getTimeInSeconds(): number { + // https://stackoverflow.com/a/3830279/295606 + return Math.floor(new Date().getTime() / 1000) +} + +function tokenExpired(token: string) { + // https://stackoverflow.com/a/60758392/295606 + // https://stackoverflow.com/a/71953677/295606 + const expiry = JSON.parse( + Buffer.from(token.split(".")[1], "base64").toString() + ).exp + return getTimeInSeconds() >= expiry +} + +function tokenParser(token: string) { + return JSON.parse( + Buffer.from(token.split(".")[1], "base64").toString() + ) +} + + export { + generateUUID, + getTimeInSeconds, + tokenExpired, + getKeyByValue, + isValidHttpUrl, + tokenParser, + } + \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/utilities/index.ts b/{{cookiecutter.project_slug}}/frontend/utilities/index.ts new file mode 100644 index 0000000000..623e974dea --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/utilities/index.ts @@ -0,0 +1,25 @@ +import { + generateUUID, + getTimeInSeconds, + tokenExpired, + getKeyByValue, + isValidHttpUrl, + tokenParser, +} from "./generic" +import { + readableDate, +} from "./textual" +import { + tokenIsTOTP +} from "./totp" + +export { + generateUUID, + getTimeInSeconds, + tokenExpired, + getKeyByValue, + isValidHttpUrl, + tokenParser, + readableDate, + tokenIsTOTP, +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/utilities/textual.ts b/{{cookiecutter.project_slug}}/frontend/utilities/textual.ts new file mode 100644 index 0000000000..dceec67da6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/utilities/textual.ts @@ -0,0 +1,17 @@ +function readableDate(term: Date | string, showYear: boolean = true) { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleDateString + // https://stackoverflow.com/a/66590756/295606 + // https://stackoverflow.com/a/67196206/295606 + const readable = term instanceof Date ? term : new Date(term) + const day = readable.toLocaleDateString("en-UK", { day: "numeric" }) + const month = readable.toLocaleDateString("en-UK", { month: "short" }) + if (showYear) { + const year = readable.toLocaleDateString("en-UK", { year: "numeric" }) + return `${day} ${month} ${year}` + } + return `${day} ${month}` +} + +export { + readableDate, +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/utilities/totp.ts b/{{cookiecutter.project_slug}}/frontend/utilities/totp.ts new file mode 100644 index 0000000000..de85f8126f --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/utilities/totp.ts @@ -0,0 +1,14 @@ +import { tokenParser } from "./generic" + + +function tokenIsTOTP(token: string) { + // https://stackoverflow.com/a/60758392/295606 + // https://stackoverflow.com/a/71953677/295606 + const obj = tokenParser(token) + if (obj.hasOwnProperty("totp")) return obj.totp + else return false +} + +export { + tokenIsTOTP +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/frontend/vue.config.js b/{{cookiecutter.project_slug}}/frontend/vue.config.js deleted file mode 100644 index 140713412f..0000000000 --- a/{{cookiecutter.project_slug}}/frontend/vue.config.js +++ /dev/null @@ -1,35 +0,0 @@ -module.exports = { - // Fix Vuex-typescript in prod: https://github.com/istrib/vuex-typescript/issues/13#issuecomment-409869231 - configureWebpack: (config) => { - if (process.env.NODE_ENV === 'production') { - config.optimization.minimizer[0].options.terserOptions = Object.assign( - {}, - config.optimization.minimizer[0].options.terserOptions, - { - ecma: 5, - compress: { - keep_fnames: true, - }, - warnings: false, - mangle: { - keep_fnames: true, - }, - }, - ); - } - }, - chainWebpack: config => { - config.module - .rule('vue') - .use('vue-loader') - .loader('vue-loader') - .tap(options => Object.assign(options, { - transformAssetUrls: { - 'v-img': ['src', 'lazy-src'], - 'v-card': 'src', - 'v-card-media': 'src', - 'v-responsive': 'src', - } - })); - }, -} diff --git a/{{cookiecutter.project_slug}}/frontend/yarn.lock b/{{cookiecutter.project_slug}}/frontend/yarn.lock new file mode 100644 index 0000000000..94117d70e0 --- /dev/null +++ b/{{cookiecutter.project_slug}}/frontend/yarn.lock @@ -0,0 +1,9557 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@antfu/utils@^0.7.7": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.7.7.tgz#26ea493a831b4f3a85475e7157be02fb4eab51fb" + integrity sha512-gFPqTG7otEJ8uP6wrhDv6mqwGWYZKNvAcCq6u9hOj0c+IKCEsY4L1oC9trPq2SaWIzAfHvqfBDxF591JkMf+kg== + +"@apideck/better-ajv-errors@^0.3.1": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz#957d4c28e886a64a8141f7522783be65733ff097" + integrity sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA== + dependencies: + json-schema "^0.4.0" + jsonpointer "^5.0.0" + leven "^3.1.0" + +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2": + version "7.24.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.2.tgz#718b4b19841809a58b29b68cde80bc5e1aa6d9ae" + integrity sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ== + dependencies: + "@babel/highlight" "^7.24.2" + picocolors "^1.0.0" + +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5", "@babel/compat-data@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.4.tgz#6f102372e9094f25d908ca0d34fc74c74606059a" + integrity sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ== + +"@babel/core@^7.23.0", "@babel/core@^7.23.3", "@babel/core@^7.23.7", "@babel/core@^7.24.4": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.5.tgz#15ab5b98e101972d171aeef92ac70d8d6718f06a" + integrity sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.24.5" + "@babel/helpers" "^7.24.5" + "@babel/parser" "^7.24.5" + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.5" + "@babel/types" "^7.24.5" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.5.tgz#e5afc068f932f05616b66713e28d0f04e99daeb3" + integrity sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA== + dependencies: + "@babel/types" "^7.24.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" + integrity sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== + 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" + +"@babel/helper-create-class-features-plugin@^7.24.1", "@babel/helper-create-class-features-plugin@^7.24.4", "@babel/helper-create-class-features-plugin@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.5.tgz#7d19da92c7e0cd8d11c09af2ce1b8e7512a6e723" + integrity sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.24.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.24.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.24.5" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" + integrity sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.6.1", "@babel/helper-define-polyfill-provider@^0.6.2": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" + integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== + +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== + dependencies: + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.23.0", "@babel/helper-member-expression-to-functions@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.5.tgz#5981e131d5c7003c7d1fa1ad49e86c9b097ec475" + integrity sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA== + dependencies: + "@babel/types" "^7.24.5" + +"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.24.1", "@babel/helper-module-imports@^7.24.3": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz#6ac476e6d168c7c23ff3ba3cf4f7841d46ac8128" + integrity sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg== + dependencies: + "@babel/types" "^7.24.0" + +"@babel/helper-module-imports@~7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== + dependencies: + "@babel/types" "^7.22.15" + +"@babel/helper-module-transforms@^7.23.3", "@babel/helper-module-transforms@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz#ea6c5e33f7b262a0ae762fd5986355c45f54a545" + integrity sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.24.3" + "@babel/helper-simple-access" "^7.24.5" + "@babel/helper-split-export-declaration" "^7.24.5" + "@babel/helper-validator-identifier" "^7.24.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.24.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz#a924607dd254a65695e5bd209b98b902b3b2f11a" + integrity sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ== + +"@babel/helper-remap-async-to-generator@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" + +"@babel/helper-replace-supers@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz#7085bd19d4a0b7ed8f405c1ed73ccb70f323abc1" + integrity sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.23.0" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5", "@babel/helper-simple-access@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz#50da5b72f58c16b07fbd992810be6049478e85ba" + integrity sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ== + dependencies: + "@babel/types" "^7.24.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz#b9a67f06a46b0b339323617c8c6213b9055a78b6" + integrity sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q== + dependencies: + "@babel/types" "^7.24.5" + +"@babel/helper-string-parser@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" + integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== + +"@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" + integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helper-wrap-function@^7.22.20": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.24.5.tgz#335f934c0962e2c1ed1fb9d79e06a56115067c09" + integrity sha512-/xxzuNvgRl4/HLNKvnFwdhdgN3cpLxgLROeLDl83Yx0AJ1SGvq1ak0OszTOjDfiB8Vx03eJbeDWh9r+jCCWttw== + dependencies: + "@babel/helper-function-name" "^7.23.0" + "@babel/template" "^7.24.0" + "@babel/types" "^7.24.5" + +"@babel/helpers@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.5.tgz#fedeb87eeafa62b621160402181ad8585a22a40a" + integrity sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q== + dependencies: + "@babel/template" "^7.24.0" + "@babel/traverse" "^7.24.5" + "@babel/types" "^7.24.5" + +"@babel/highlight@^7.24.2": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.5.tgz#bc0613f98e1dd0720e99b2a9ee3760194a704b6e" + integrity sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.5" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.22.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.4", "@babel/parser@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" + integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.5.tgz#4c3685eb9cd790bcad2843900fe0250c91ccf895" + integrity sha512-LdXRi1wEMTrHVR4Zc9F8OewC3vdm5h4QB6L71zy6StmYeqGi1b3ttIO8UC+BfZKcH9jdr4aI249rBkm+3+YvHw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.5" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz#b645d9ba8c2bc5b7af50f0fe949f9edbeb07c8cf" + integrity sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz#da8261f2697f0f41b0855b91d3a20a1fbfd271d3" + integrity sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.24.1" + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz#1181d9685984c91d657b8ddf14f0487a6bab2988" + integrity sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-proposal-decorators@^7.23.0": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.1.tgz#bab2b9e174a2680f0a80f341f3ec70f809f8bb4b" + integrity sha512-zPEvzFijn+hRvJuX2Vu3KbEBN39LN3f7tW3MQO2LsIs57B26KU+kUc82BdAktS1VCM6libzh45eKGI65lg0cpA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-decorators" "^7.24.1" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.1.tgz#71d9ad06063a6ac5430db126b5df48c70ee885fa" + integrity sha512-05RJdO/cCrtVWuAaSn1tS3bH8jbsJa/Y1uD186u6J4C/1mnHFxseeuWpsqr9anvo7TUulev7tm7GDwRV+VuhDw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-import-assertions@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz#db3aad724153a00eaac115a3fb898de544e34971" + integrity sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-import-attributes@^7.22.5", "@babel/plugin-syntax-import-attributes@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz#c66b966c63b714c4eec508fcf5763b1f2d381093" + integrity sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.23.3": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz#3f6ca04b8c841811dbc3c5c5f837934e0d626c10" + integrity sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz#b3bcc51f396d15f3591683f90239de143c076844" + integrity sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz#2bf263617060c9cc45bcdbf492b8cc805082bf27" + integrity sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-async-generator-functions@^7.24.3": + version "7.24.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz#8fa7ae481b100768cc9842c8617808c5352b8b89" + integrity sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-remap-async-to-generator" "^7.22.20" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz#0e220703b89f2216800ce7b1c53cb0cf521c37f4" + integrity sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw== + dependencies: + "@babel/helper-module-imports" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-remap-async-to-generator" "^7.22.20" + +"@babel/plugin-transform-block-scoped-functions@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz#1c94799e20fcd5c4d4589523bbc57b7692979380" + integrity sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-block-scoping@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.5.tgz#89574191397f85661d6f748d4b89ee4d9ee69a2a" + integrity sha512-sMfBc3OxghjC95BkYrYocHL3NaOplrcaunblzwXhGmlPwpmfsxr4vK+mBBt49r+S240vahmv+kUxkeKgs+haCw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + +"@babel/plugin-transform-class-properties@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz#bcbf1aef6ba6085cfddec9fc8d58871cf011fc29" + integrity sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-class-static-block@^7.24.4": + version "7.24.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz#1a4653c0cf8ac46441ec406dece6e9bc590356a4" + integrity sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.4" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.5.tgz#05e04a09df49a46348299a0e24bfd7e901129339" + integrity sha512-gWkLP25DFj2dwe9Ck8uwMOpko4YsqyfZJrOmqqcegeDYEbp7rmn4U6UQZNj08UF6MaX39XenSpKRCvpDRBtZ7Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/helper-replace-supers" "^7.24.1" + "@babel/helper-split-export-declaration" "^7.24.5" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz#bc7e787f8e021eccfb677af5f13c29a9934ed8a7" + integrity sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/template" "^7.24.0" + +"@babel/plugin-transform-destructuring@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.5.tgz#80843ee6a520f7362686d1a97a7b53544ede453c" + integrity sha512-SZuuLyfxvsm+Ah57I/i1HVjveBENYK9ue8MJ7qkc7ndoNjqquJiElzA7f5yaAXjyW2hKojosOTAQQRX50bPSVg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + +"@babel/plugin-transform-dotall-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz#d56913d2f12795cc9930801b84c6f8c47513ac13" + integrity sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-duplicate-keys@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz#5347a797fe82b8d09749d10e9f5b83665adbca88" + integrity sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-dynamic-import@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz#2a5a49959201970dd09a5fca856cb651e44439dd" + integrity sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz#6650ebeb5bd5c012d5f5f90a26613a08162e8ba4" + integrity sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-export-namespace-from@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz#f033541fc036e3efb2dcb58eedafd4f6b8078acd" + integrity sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz#67448446b67ab6c091360ce3717e7d3a59e202fd" + integrity sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz#8cba6f7730626cc4dfe4ca2fa516215a0592b361" + integrity sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA== + dependencies: + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-json-strings@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz#08e6369b62ab3e8a7b61089151b161180c8299f7" + integrity sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz#0a1982297af83e6b3c94972686067df588c5c096" + integrity sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-logical-assignment-operators@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz#719d8aded1aa94b8fb34e3a785ae8518e24cfa40" + integrity sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz#896d23601c92f437af8b01371ad34beb75df4489" + integrity sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-modules-amd@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz#b6d829ed15258536977e9c7cc6437814871ffa39" + integrity sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-modules-commonjs@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz#e71ba1d0d69e049a22bf90b3867e263823d3f1b9" + integrity sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz#2b9625a3d4e445babac9788daec39094e6b11e3e" + integrity sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-validator-identifier" "^7.22.20" + +"@babel/plugin-transform-modules-umd@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz#69220c66653a19cf2c0872b9c762b9a48b8bebef" + integrity sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg== + dependencies: + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz#29c59988fa3d0157de1c871a28cd83096363cc34" + integrity sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz#0cd494bb97cb07d428bd651632cb9d4140513988" + integrity sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz#5bc019ce5b3435c1cadf37215e55e433d674d4e8" + integrity sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.5.tgz#f91bbcb092ff957c54b4091c86bda8372f0b10ef" + integrity sha512-7EauQHszLGM3ay7a161tTQH7fj+3vVM/gThlz5HpFtnygTxjrlvoeq7MPVA1Vy9Q555OB8SnAOsMkLShNkkrHA== + dependencies: + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.24.5" + +"@babel/plugin-transform-object-super@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz#e71d6ab13483cca89ed95a474f542bbfc20a0520" + integrity sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-replace-supers" "^7.24.1" + +"@babel/plugin-transform-optional-catch-binding@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz#92a3d0efe847ba722f1a4508669b23134669e2da" + integrity sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.24.1", "@babel/plugin-transform-optional-chaining@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.5.tgz#a6334bebd7f9dd3df37447880d0bd64b778e600f" + integrity sha512-xWCkmwKT+ihmA6l7SSTpk8e4qQl/274iNbSKRRS8mpqFR32ksy36+a+LWY8OXCCEefF8WFlnOHVsaDI2231wBg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.5.tgz#5c3b23f3a6b8fed090f9b98f2926896d3153cc62" + integrity sha512-9Co00MqZ2aoky+4j2jhofErthm6QVLKbpQrvz20c3CH9KQCLHyNB+t2ya4/UrRpQGR+Wrwjg9foopoeSdnHOkA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + +"@babel/plugin-transform-private-methods@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz#a0faa1ae87eff077e1e47a5ec81c3aef383dc15a" + integrity sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.24.1" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-private-property-in-object@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.5.tgz#f5d1fcad36e30c960134cb479f1ca98a5b06eda5" + integrity sha512-JM4MHZqnWR04jPMujQDTBVRnqxpLLpx2tkn7iPn+Hmsc0Gnb79yvRWOkvqFOx3Z7P7VxiRIR22c4eGSNj87OBQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.24.5" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz#d6a9aeab96f03749f4eebeb0b6ea8e90ec958825" + integrity sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-regenerator@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz#625b7545bae52363bdc1fbbdc7252b5046409c8c" + integrity sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + regenerator-transform "^0.15.2" + +"@babel/plugin-transform-reserved-words@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz#8de729f5ecbaaf5cf83b67de13bad38a21be57c1" + integrity sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-shorthand-properties@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz#ba9a09144cf55d35ec6b93a32253becad8ee5b55" + integrity sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-spread@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz#a1acf9152cbf690e4da0ba10790b3ac7d2b2b391" + integrity sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz#f03e672912c6e203ed8d6e0271d9c2113dc031b9" + integrity sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-template-literals@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz#15e2166873a30d8617e3e2ccadb86643d327aab7" + integrity sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-typeof-symbol@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.5.tgz#703cace5ef74155fb5eecab63cbfc39bdd25fe12" + integrity sha512-UTGnhYVZtTAjdwOTzT+sCyXmTn8AhaxOS/MjG9REclZ6ULHWF9KoCZur0HSGU7hk8PdBFKKbYe6+gqdXWz84Jg== + dependencies: + "@babel/helper-plugin-utils" "^7.24.5" + +"@babel/plugin-transform-typescript@^7.22.15", "@babel/plugin-transform-typescript@^7.23.3": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.5.tgz#bcba979e462120dc06a75bd34c473a04781931b8" + integrity sha512-E0VWu/hk83BIFUWnsKZ4D81KXjN5L3MobvevOHErASk9IPwKHOkTgvqzvNo1yP/ePJWqqK2SpUR5z+KQbl6NVw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.24.5" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/plugin-syntax-typescript" "^7.24.1" + +"@babel/plugin-transform-unicode-escapes@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz#fb3fa16676549ac7c7449db9b342614985c2a3a4" + integrity sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw== + dependencies: + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-unicode-property-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz#56704fd4d99da81e5e9f0c0c93cabd91dbc4889e" + integrity sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-unicode-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz#57c3c191d68f998ac46b708380c1ce4d13536385" + integrity sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/plugin-transform-unicode-sets-regex@^7.24.1": + version "7.24.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz#c1ea175b02afcffc9cf57a9c4658326625165b7f" + integrity sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.0" + +"@babel/preset-env@^7.11.0": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.24.5.tgz#6a9ac90bd5a5a9dae502af60dfc58c190551bbcd" + integrity sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ== + dependencies: + "@babel/compat-data" "^7.24.4" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-plugin-utils" "^7.24.5" + "@babel/helper-validator-option" "^7.23.5" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.24.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.24.1" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.1" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.24.1" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.24.1" + "@babel/plugin-syntax-import-attributes" "^7.24.1" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.24.1" + "@babel/plugin-transform-async-generator-functions" "^7.24.3" + "@babel/plugin-transform-async-to-generator" "^7.24.1" + "@babel/plugin-transform-block-scoped-functions" "^7.24.1" + "@babel/plugin-transform-block-scoping" "^7.24.5" + "@babel/plugin-transform-class-properties" "^7.24.1" + "@babel/plugin-transform-class-static-block" "^7.24.4" + "@babel/plugin-transform-classes" "^7.24.5" + "@babel/plugin-transform-computed-properties" "^7.24.1" + "@babel/plugin-transform-destructuring" "^7.24.5" + "@babel/plugin-transform-dotall-regex" "^7.24.1" + "@babel/plugin-transform-duplicate-keys" "^7.24.1" + "@babel/plugin-transform-dynamic-import" "^7.24.1" + "@babel/plugin-transform-exponentiation-operator" "^7.24.1" + "@babel/plugin-transform-export-namespace-from" "^7.24.1" + "@babel/plugin-transform-for-of" "^7.24.1" + "@babel/plugin-transform-function-name" "^7.24.1" + "@babel/plugin-transform-json-strings" "^7.24.1" + "@babel/plugin-transform-literals" "^7.24.1" + "@babel/plugin-transform-logical-assignment-operators" "^7.24.1" + "@babel/plugin-transform-member-expression-literals" "^7.24.1" + "@babel/plugin-transform-modules-amd" "^7.24.1" + "@babel/plugin-transform-modules-commonjs" "^7.24.1" + "@babel/plugin-transform-modules-systemjs" "^7.24.1" + "@babel/plugin-transform-modules-umd" "^7.24.1" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.24.1" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.1" + "@babel/plugin-transform-numeric-separator" "^7.24.1" + "@babel/plugin-transform-object-rest-spread" "^7.24.5" + "@babel/plugin-transform-object-super" "^7.24.1" + "@babel/plugin-transform-optional-catch-binding" "^7.24.1" + "@babel/plugin-transform-optional-chaining" "^7.24.5" + "@babel/plugin-transform-parameters" "^7.24.5" + "@babel/plugin-transform-private-methods" "^7.24.1" + "@babel/plugin-transform-private-property-in-object" "^7.24.5" + "@babel/plugin-transform-property-literals" "^7.24.1" + "@babel/plugin-transform-regenerator" "^7.24.1" + "@babel/plugin-transform-reserved-words" "^7.24.1" + "@babel/plugin-transform-shorthand-properties" "^7.24.1" + "@babel/plugin-transform-spread" "^7.24.1" + "@babel/plugin-transform-sticky-regex" "^7.24.1" + "@babel/plugin-transform-template-literals" "^7.24.1" + "@babel/plugin-transform-typeof-symbol" "^7.24.5" + "@babel/plugin-transform-unicode-escapes" "^7.24.1" + "@babel/plugin-transform-unicode-property-regex" "^7.24.1" + "@babel/plugin-transform-unicode-regex" "^7.24.1" + "@babel/plugin-transform-unicode-sets-regex" "^7.24.1" + "@babel/preset-modules" "0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2 "^0.4.10" + babel-plugin-polyfill-corejs3 "^0.10.4" + babel-plugin-polyfill-regenerator "^0.6.1" + core-js-compat "^3.31.0" + semver "^6.3.1" + +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.11.2", "@babel/runtime@^7.8.4": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.5.tgz#230946857c053a36ccc66e1dd03b17dd0c4ed02c" + integrity sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/standalone@^7.23.8": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.24.5.tgz#2003c40665adf222c952b6d652607f30b3152c0d" + integrity sha512-Sl8oN9bGfRlNUA2jzfzoHEZxFBDliBlwi5mPVCAWKSlBNkXXJOHpu7SDOqjF6mRoTa6GNX/1kAWG3Tr+YQ3N7A== + +"@babel/template@^7.22.15", "@babel/template@^7.23.9", "@babel/template@^7.24.0": + version "7.24.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.0.tgz#c6a524aa93a4a05d66aaf31654258fae69d87d50" + integrity sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/parser" "^7.24.0" + "@babel/types" "^7.24.0" + +"@babel/traverse@^7.23.9", "@babel/traverse@^7.24.5": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.5.tgz#972aa0bc45f16983bf64aa1f877b2dd0eea7e6f8" + integrity sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA== + dependencies: + "@babel/code-frame" "^7.24.2" + "@babel/generator" "^7.24.5" + "@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.24.5" + "@babel/parser" "^7.24.5" + "@babel/types" "^7.24.5" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.24.0", "@babel/types@^7.24.5", "@babel/types@^7.4.4": + version "7.24.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.5.tgz#7661930afc638a5383eb0c4aee59b74f38db84d7" + integrity sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ== + dependencies: + "@babel/helper-string-parser" "^7.24.1" + "@babel/helper-validator-identifier" "^7.24.5" + to-fast-properties "^2.0.0" + +"@canvas/image-data@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@canvas/image-data/-/image-data-1.0.0.tgz#3bd2cd856e13fc9e2c25feff360a4056857b0367" + integrity sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw== + +"@cloudflare/kv-asset-handler@^0.3.1": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.2.tgz#06437b75664729823ac9033b89f06a3b078e4f55" + integrity sha512-EeEjMobfuJrwoctj7FA1y1KEbM0+Q1xSjobIEyie9k4haVEBB7vkDvsasw1pM3rO39mL2akxIAzLMUAtrMHZhA== + dependencies: + mime "^3.0.0" + +"@emnapi/runtime@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.1.1.tgz#697d02276ca6f49bafe6fd01c9df0034818afa98" + integrity sha512-3bfqkzuR1KLx57nZfjr2NLnFOobvyS0aTszaEGCGqmYMVDRaGvgIZbjGSV/MHSSmLgQ/b9JFHQ5xm5WRZYd+XQ== + dependencies: + tslib "^2.4.0" + +"@esbuild/aix-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" + integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== + +"@esbuild/android-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" + integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== + +"@esbuild/android-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" + integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== + +"@esbuild/android-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" + integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== + +"@esbuild/darwin-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" + integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== + +"@esbuild/darwin-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" + integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== + +"@esbuild/freebsd-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" + integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== + +"@esbuild/freebsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" + integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== + +"@esbuild/linux-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" + integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== + +"@esbuild/linux-arm@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" + integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== + +"@esbuild/linux-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" + integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== + +"@esbuild/linux-loong64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" + integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== + +"@esbuild/linux-mips64el@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" + integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== + +"@esbuild/linux-ppc64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" + integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== + +"@esbuild/linux-riscv64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" + integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== + +"@esbuild/linux-s390x@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" + integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== + +"@esbuild/linux-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" + integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== + +"@esbuild/netbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" + integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== + +"@esbuild/openbsd-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" + integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== + +"@esbuild/sunos-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" + integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== + +"@esbuild/win32-arm64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" + integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== + +"@esbuild/win32-ia32@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" + integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== + +"@esbuild/win32-x64@0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" + integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== + +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + +"@headlessui/vue@^1.7.21": + version "1.7.21" + resolved "https://registry.yarnpkg.com/@headlessui/vue/-/vue-1.7.21.tgz#debad090e6ae191f50cadde54d0b53322983ac6c" + integrity sha512-95cPFP5X9luB8/+smPENFv0ruaotT6epFnqK9EKldX2YpNkYM/qkN44oFoqQo+higlCAxDK5Pkg2E3FuD1Anyg== + dependencies: + "@tanstack/vue-virtual" "^3.0.0-beta.60" + +"@heroicons/vue@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@heroicons/vue/-/vue-2.1.3.tgz#7553aea937bf23013b44ab3787ce181a45188356" + integrity sha512-CP4ipIwFbV4NEn8ULUCN110wkV0wZq6dsViDL3HwgIh+jn5yQGlRm6QaRN+Mv+o+UsUBbRDei3Je/q0NZHf5Gg== + +"@img/sharp-darwin-arm64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.3.tgz#2bbf676be830c5a9ae7d9294f201c9151535badd" + integrity sha512-FaNiGX1MrOuJ3hxuNzWgsT/mg5OHG/Izh59WW2mk1UwYHUwtfbhk5QNKYZgxf0pLOhx9ctGiGa2OykD71vOnSw== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.0.2" + +"@img/sharp-darwin-x64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.3.tgz#c59567b141eb676e884066f76091a2673120c3f5" + integrity sha512-2QeSl7QDK9ru//YBT4sQkoq7L0EAJZA3rtV+v9p8xTKl4U1bUqTIaCnoC7Ctx2kCjQgwFXDasOtPTCT8eCTXvw== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.0.2" + +"@img/sharp-libvips-darwin-arm64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz#b69f49fecbe9572378675769b189410721b0fa53" + integrity sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA== + +"@img/sharp-libvips-darwin-x64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz#5665da7360d8e5ed7bee314491c8fe736b6a3c39" + integrity sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw== + +"@img/sharp-libvips-linux-arm64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz#8a05e5e9e9b760ff46561e32f19bd5e035fa881c" + integrity sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw== + +"@img/sharp-libvips-linux-arm@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz#0fd33b9bf3221948ce0ca7a5a725942626577a03" + integrity sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw== + +"@img/sharp-libvips-linux-s390x@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz#4b89150ec91b256ee2cbb5bb125321bf029a4770" + integrity sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog== + +"@img/sharp-libvips-linux-x64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz#947ccc22ca5bc8c8cfe921b39a5fdaebc5e39f3f" + integrity sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ== + +"@img/sharp-libvips-linuxmusl-arm64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz#821d58ce774f0f8bed065b69913a62f65d512f2f" + integrity sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ== + +"@img/sharp-libvips-linuxmusl-x64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz#4309474bd8b728a61af0b3b4fad0c476b5f3ccbe" + integrity sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw== + +"@img/sharp-linux-arm64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.3.tgz#a1f788ddf49ed63509dd37d4b01e571fe7f189d5" + integrity sha512-Zf+sF1jHZJKA6Gor9hoYG2ljr4wo9cY4twaxgFDvlG0Xz9V7sinsPp8pFd1XtlhTzYo0IhDbl3rK7P6MzHpnYA== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.0.2" + +"@img/sharp-linux-arm@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.3.tgz#661b0671ed7f740fd06821ce15050ba23f1d0523" + integrity sha512-Q7Ee3fFSC9P7vUSqVEF0zccJsZ8GiiCJYGWDdhEjdlOeS9/jdkyJ6sUSPj+bL8VuOYFSbofrW0t/86ceVhx32w== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.2" + +"@img/sharp-linux-s390x@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.3.tgz#8719341d3931a297df1a956c02ee003736fa8fac" + integrity sha512-vFk441DKRFepjhTEH20oBlFrHcLjPfI8B0pMIxGm3+yilKyYeHEVvrZhYFdqIseSclIqbQ3SnZMwEMWonY5XFA== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.0.2" + +"@img/sharp-linux-x64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.3.tgz#dbd860b4aa16e7e25727c7e05b411132b58d017d" + integrity sha512-Q4I++herIJxJi+qmbySd072oDPRkCg/SClLEIDh5IL9h1zjhqjv82H0Seupd+q2m0yOfD+/fJnjSoDFtKiHu2g== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.0.2" + +"@img/sharp-linuxmusl-arm64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.3.tgz#25b3fbfe9b6fa32d773422d878d8d84f3f6afceb" + integrity sha512-qnDccehRDXadhM9PM5hLvcPRYqyFCBN31kq+ErBSZtZlsAc1U4Z85xf/RXv1qolkdu+ibw64fUDaRdktxTNP9A== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.0.2" + +"@img/sharp-linuxmusl-x64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.3.tgz#1e533e44abf2e2d427428ed49294ddba4eb11456" + integrity sha512-Jhchim8kHWIU/GZ+9poHMWRcefeaxFIs9EBqf9KtcC14Ojk6qua7ghKiPs0sbeLbLj/2IGBtDcxHyjCdYWkk2w== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.0.2" + +"@img/sharp-wasm32@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.3.tgz#340006047a77df0744db84477768bbca6327b4b4" + integrity sha512-68zivsdJ0koE96stdUfM+gmyaK/NcoSZK5dV5CAjES0FUXS9lchYt8LAB5rTbM7nlWtxaU/2GON0HVN6/ZYJAQ== + dependencies: + "@emnapi/runtime" "^1.1.0" + +"@img/sharp-win32-ia32@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.3.tgz#0fdc49ab094ed0151ec8347afac7917aa5fc5145" + integrity sha512-CyimAduT2whQD8ER4Ux7exKrtfoaUiVr7HG0zZvO0XTFn2idUWljjxv58GxNTkFb8/J9Ub9AqITGkJD6ZginxQ== + +"@img/sharp-win32-x64@0.33.3": + version "0.33.3" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.3.tgz#a94e1028f180666f97fd51e35c4ad092d7704ef0" + integrity sha512-viT4fUIDKnli3IfOephGnolMzhz5VaTvDRkYqtZxOMIoMQ4MrAziO7pT1nVnOt2FAm7qW5aa+CCc13aEY6Le0g== + +"@intlify/bundle-utils@^7.4.0": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-7.5.1.tgz#8f70f44929dc4e43c1ddb8fddb9e590277145914" + integrity sha512-UovJl10oBIlmYEcWw+VIHdKY5Uv5sdPG0b/b6bOYxGLln3UwB75+2dlc0F3Fsa0RhoznQ5Rp589/BZpABpE4Xw== + dependencies: + "@intlify/message-compiler" "^9.4.0" + "@intlify/shared" "^9.4.0" + acorn "^8.8.2" + escodegen "^2.1.0" + estree-walker "^2.0.2" + jsonc-eslint-parser "^2.3.0" + magic-string "^0.30.0" + mlly "^1.2.0" + source-map-js "^1.0.1" + yaml-eslint-parser "^1.2.2" + +"@intlify/core-base@9.13.1": + version "9.13.1" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.13.1.tgz#bd1f38e665095993ef9b67aeeb794f3cabcb515d" + integrity sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w== + dependencies: + "@intlify/message-compiler" "9.13.1" + "@intlify/shared" "9.13.1" + +"@intlify/core@^9.8.0": + version "9.13.1" + resolved "https://registry.yarnpkg.com/@intlify/core/-/core-9.13.1.tgz#e98e5b0cae993e598e7beacadc3b638d02547134" + integrity sha512-R+l9DRqzfK0yT9UgaCq3sl24NJAP4f/djAu4z9zLknAUBEal2q/tXFV+oGzcGpvi3uXWNvF9Gctj+IsuPwJjoA== + dependencies: + "@intlify/core-base" "9.13.1" + "@intlify/shared" "9.13.1" + +"@intlify/h3@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@intlify/h3/-/h3-0.5.0.tgz#41aa9aed393efd947f867a4a314e24c7f28611b6" + integrity sha512-cgfrtD3qu3BPJ47gfZ35J2LJpI64Riic0K8NGgid5ilyPXRQTNY7mXlT/B+HZYQg1hmBxKa5G5HJXyAZ4R2H5A== + dependencies: + "@intlify/core" "^9.8.0" + "@intlify/utils" "^0.12.0" + +"@intlify/message-compiler@9.13.1", "@intlify/message-compiler@^9.4.0": + version "9.13.1" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.13.1.tgz#ff8129badf77db3fb648b8d3cceee87c8033ed0a" + integrity sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w== + dependencies: + "@intlify/shared" "9.13.1" + source-map-js "^1.0.2" + +"@intlify/shared@9.13.1", "@intlify/shared@^9.4.0", "@intlify/shared@^9.9.0": + version "9.13.1" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.13.1.tgz#202741d11ece1a9c7480bfd3f27afcf9cb8f72e4" + integrity sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ== + +"@intlify/unplugin-vue-i18n@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-3.0.1.tgz#8bed58d5cbaadda056c2ff88acf99300db516639" + integrity sha512-q1zJhA/WpoLBzAAuKA5/AEp0e+bMOM10ll/HxT4g1VAw/9JhC4TTobP9KobKH90JMZ4U2daLFlYQfKNd29lpqw== + dependencies: + "@intlify/bundle-utils" "^7.4.0" + "@intlify/shared" "^9.4.0" + "@rollup/pluginutils" "^5.1.0" + "@vue/compiler-sfc" "^3.2.47" + debug "^4.3.3" + fast-glob "^3.2.12" + js-yaml "^4.1.0" + json5 "^2.2.3" + pathe "^1.0.0" + picocolors "^1.0.0" + source-map-js "^1.0.2" + unplugin "^1.1.0" + +"@intlify/utils@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@intlify/utils/-/utils-0.12.0.tgz#74ef0723048151687447342e90fbc3b26a7de3ff" + integrity sha512-yCBNcuZQ49iInqmWC2xfW0rgEQyNtCM8C8KcWKTXxyscgUE1+48gjLgZZqP75MjhlApxwph7ZMWLqyABkSgxQA== + +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@jridgewell/gen-mapping@^0.3.2", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@kwsites/file-exists@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz#ad1efcac13e1987d8dbaf235ef3be5b0d96faa99" + integrity sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw== + dependencies: + debug "^4.1.1" + +"@kwsites/promise-deferred@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz#8ace5259254426ccef57f3175bc64ed7095ed919" + integrity sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw== + +"@mapbox/node-pre-gyp@^1.0.5": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + +"@miyaneee/rollup-plugin-json5@^1.1.2": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@miyaneee/rollup-plugin-json5/-/rollup-plugin-json5-1.2.0.tgz#a0995e55bfcf055aea0c9c73e0b2dcac120e036e" + integrity sha512-JjTIaXZp9WzhUHpElrqPnl1AzBi/rvRs065F71+aTmlqvTMVkdbjZ8vfFl4nRlgJy+TPBw69ZK4pwFdmOAt4aA== + dependencies: + "@rollup/pluginutils" "^5.1.0" + json5 "^2.2.3" + +"@netlify/functions@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@netlify/functions/-/functions-2.6.0.tgz#801a6fe8ceef2ce1512c637a28e53e6a3aae289b" + integrity sha512-vU20tij0fb4nRGACqb+5SQvKd50JYyTyEhQetCMHdakcJFzjLDivvRR16u1G2Oy4A7xNAtGJF1uz8reeOtTVcQ== + dependencies: + "@netlify/serverless-functions-api" "1.14.0" + +"@netlify/node-cookies@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@netlify/node-cookies/-/node-cookies-0.1.0.tgz#dda912ba618527695cf519fafa221c5e6777c612" + integrity sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g== + +"@netlify/serverless-functions-api@1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@netlify/serverless-functions-api/-/serverless-functions-api-1.14.0.tgz#2bedff76cf898e24e48161aa2508776c4d261ed1" + integrity sha512-HUNETLNvNiC2J+SB/YuRwJA9+agPrc0azSoWVk8H85GC+YE114hcS5JW+dstpKwVerp2xILE3vNWN7IMXP5Q5Q== + dependencies: + "@netlify/node-cookies" "^0.1.0" + urlpattern-polyfill "8.0.2" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== + dependencies: + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" + +"@npmcli/fs@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" + integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== + dependencies: + semver "^7.3.5" + +"@npmcli/git@^5.0.0": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-5.0.6.tgz#d7b24eb2cff98754c8868faab40405abfa1abe28" + integrity sha512-4x/182sKXmQkf0EtXxT26GEsaOATpD7WVtza5hrYivWZeo6QefC6xq9KAXrnjtFKBZ4rZwR7aX/zClYYXgtwLw== + dependencies: + "@npmcli/promise-spawn" "^7.0.0" + lru-cache "^10.0.1" + npm-pick-manifest "^9.0.0" + proc-log "^4.0.0" + promise-inflight "^1.0.1" + promise-retry "^2.0.1" + semver "^7.3.5" + which "^4.0.0" + +"@npmcli/installed-package-contents@^2.0.1": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz#63048e5f6e40947a3a88dcbcb4fd9b76fdd37c17" + integrity sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w== + dependencies: + npm-bundled "^3.0.0" + npm-normalize-package-bin "^3.0.0" + +"@npmcli/node-gyp@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" + integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== + +"@npmcli/package-json@^5.0.0", "@npmcli/package-json@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-5.1.0.tgz#10d117b5fb175acc14c70901a151c52deffc843e" + integrity sha512-1aL4TuVrLS9sf8quCLerU3H9J4vtCtgu8VauYozrmEyU57i/EdKleCnsQ7vpnABIH6c9mnTxcH5sFkO3BlV8wQ== + dependencies: + "@npmcli/git" "^5.0.0" + glob "^10.2.2" + hosted-git-info "^7.0.0" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^6.0.0" + proc-log "^4.0.0" + semver "^7.5.3" + +"@npmcli/promise-spawn@^7.0.0": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-7.0.1.tgz#a836de2f42a2245d629cf6fbb8dd6c74c74c55af" + integrity sha512-P4KkF9jX3y+7yFUxgcUdDtLy+t4OlDGuEBLNs57AZsfSfg+uV6MLndqGpnl4831ggaEdXwR50XFoZP4VFtHolg== + dependencies: + which "^4.0.0" + +"@npmcli/redact@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/redact/-/redact-2.0.0.tgz#72f9689e87bd0ce419c35e75b8470ce8ac9e14f0" + integrity sha512-SEjCPAVHWYUIQR+Yn03kJmrJjZDtJLYpj300m3HV9OTRZNpC5YpbMsM3eTkECyT4aWj8lDr9WeY6TWefpubtYQ== + +"@npmcli/run-script@^8.0.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-8.1.0.tgz#a563e5e29b1ca4e648a6b1bbbfe7220b4bfe39fc" + integrity sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg== + dependencies: + "@npmcli/node-gyp" "^3.0.0" + "@npmcli/package-json" "^5.0.0" + "@npmcli/promise-spawn" "^7.0.0" + node-gyp "^10.0.0" + proc-log "^4.0.0" + which "^4.0.0" + +"@nuxt/content@^2.12.1": + version "2.12.1" + resolved "https://registry.yarnpkg.com/@nuxt/content/-/content-2.12.1.tgz#9657b89cb04b34534b245c1c8d4f3d2ccddd999a" + integrity sha512-xW4xjyYm6zqglb17Tu0J+rpKUV1PF9zp6SLu1lopylFnerdyImtce84206HT6Zd/DJgivKtoW4dyyJn0ZaSqCQ== + dependencies: + "@nuxt/kit" "^3.10.3" + "@nuxtjs/mdc" "^0.6.1" + "@vueuse/core" "^10.9.0" + "@vueuse/head" "^2.0.0" + "@vueuse/nuxt" "^10.9.0" + consola "^3.2.3" + defu "^6.1.4" + destr "^2.0.3" + json5 "^2.2.3" + knitwork "^1.0.0" + listhen "^1.7.2" + mdast-util-to-string "^4.0.0" + mdurl "^2.0.0" + micromark "^4.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-types "^2.0.0" + minisearch "^6.3.0" + ohash "^1.1.3" + pathe "^1.1.2" + scule "^1.3.0" + shiki "^1.1.7" + slugify "^1.6.6" + socket.io-client "^4.7.4" + ufo "^1.4.0" + unist-util-stringify-position "^4.0.0" + unstorage "^1.10.1" + ws "^8.16.0" + +"@nuxt/devalue@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nuxt/devalue/-/devalue-2.0.2.tgz#5749f04df13bda4c863338d8dabaf370f45ef7c7" + integrity sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA== + +"@nuxt/devtools-kit@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@nuxt/devtools-kit/-/devtools-kit-1.2.0.tgz#149fa7c3baf1203df0238bd37b3558c392e84b6f" + integrity sha512-T81TQuaN6hbQFzgvQeRAMJjcL4mgWtYvlGTAvtuvd3TFuHV7bMK+tFZaxgJXzIu1/UPO7/aO4VLCB0xl5sSwZw== + dependencies: + "@nuxt/kit" "^3.11.2" + "@nuxt/schema" "^3.11.2" + execa "^7.2.0" + +"@nuxt/devtools-wizard@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@nuxt/devtools-wizard/-/devtools-wizard-1.2.0.tgz#9bc957d2db85a0eea41d87df0cb87070696773b0" + integrity sha512-qGepEgm7m1q9fmnwcrbijpRgdprPbczStmVlKcONYE/9PrGn+MHeHthJHD0im30FHBVQytbN11jor1sHEauGhA== + dependencies: + consola "^3.2.3" + diff "^5.2.0" + execa "^7.2.0" + global-directory "^4.0.1" + magicast "^0.3.4" + pathe "^1.1.2" + pkg-types "^1.1.0" + prompts "^2.4.2" + rc9 "^2.1.2" + semver "^7.6.0" + +"@nuxt/devtools@^1.1.5": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@nuxt/devtools/-/devtools-1.2.0.tgz#fff16c7f2fa53cfffb2be6f9be04c300c7f5f322" + integrity sha512-pdEvZJqovqxJp9E1BJAaGeFdFPEpCKwuuy9l9k4exBvwvxjTfjLeyW7oPD5RUTCGGxhOswgbXwuDrO4k+x2zpA== + dependencies: + "@antfu/utils" "^0.7.7" + "@nuxt/devtools-kit" "1.2.0" + "@nuxt/devtools-wizard" "1.2.0" + "@nuxt/kit" "^3.11.2" + "@vue/devtools-applet" "^7.0.27" + "@vue/devtools-core" "^7.0.27" + "@vue/devtools-kit" "^7.0.27" + birpc "^0.2.17" + consola "^3.2.3" + cronstrue "^2.49.0" + destr "^2.0.3" + error-stack-parser-es "^0.1.1" + execa "^7.2.0" + fast-glob "^3.3.2" + flatted "^3.3.1" + get-port-please "^3.1.2" + hookable "^5.5.3" + image-meta "^0.2.0" + is-installed-globally "^1.0.0" + launch-editor "^2.6.1" + local-pkg "^0.5.0" + magicast "^0.3.4" + nypm "^0.3.8" + ohash "^1.1.3" + pacote "^18.0.0" + pathe "^1.1.2" + perfect-debounce "^1.0.0" + pkg-types "^1.1.0" + rc9 "^2.1.2" + scule "^1.3.0" + semver "^7.6.0" + simple-git "^3.24.0" + sirv "^2.0.4" + unimport "^3.7.1" + vite-plugin-inspect "^0.8.3" + vite-plugin-vue-inspector "^4.0.2" + which "^3.0.1" + ws "^8.16.0" + +"@nuxt/kit@3.11.2", "@nuxt/kit@^3.0.0", "@nuxt/kit@^3.10.2", "@nuxt/kit@^3.10.3", "@nuxt/kit@^3.11.2", "@nuxt/kit@^3.5.0", "@nuxt/kit@^3.8.0", "@nuxt/kit@^3.9.0": + version "3.11.2" + resolved "https://registry.yarnpkg.com/@nuxt/kit/-/kit-3.11.2.tgz#dfc43c05992691bcd6aa58c14f88cf43e3abb788" + integrity sha512-yiYKP0ZWMW7T3TCmsv4H8+jEsB/nFriRAR8bKoSqSV9bkVYWPE36sf7JDux30dQ91jSlQG6LQkB3vCHYTS2cIg== + dependencies: + "@nuxt/schema" "3.11.2" + c12 "^1.10.0" + consola "^3.2.3" + defu "^6.1.4" + globby "^14.0.1" + hash-sum "^2.0.0" + ignore "^5.3.1" + jiti "^1.21.0" + knitwork "^1.1.0" + mlly "^1.6.1" + pathe "^1.1.2" + pkg-types "^1.0.3" + scule "^1.3.0" + semver "^7.6.0" + ufo "^1.5.3" + unctx "^2.3.1" + unimport "^3.7.1" + untyped "^1.4.2" + +"@nuxt/schema@3.11.2", "@nuxt/schema@^3.11.2": + version "3.11.2" + resolved "https://registry.yarnpkg.com/@nuxt/schema/-/schema-3.11.2.tgz#530c7b4efd24c24523d8fd2d83dd66f44474d434" + integrity sha512-Z0bx7N08itD5edtpkstImLctWMNvxTArsKXzS35ZuqyAyKBPcRjO1CU01slH0ahO30Gg9kbck3/RKNZPwfOjJg== + dependencies: + "@nuxt/ui-templates" "^1.3.2" + consola "^3.2.3" + defu "^6.1.4" + hookable "^5.5.3" + pathe "^1.1.2" + pkg-types "^1.0.3" + scule "^1.3.0" + std-env "^3.7.0" + ufo "^1.5.3" + unimport "^3.7.1" + untyped "^1.4.2" + +"@nuxt/telemetry@^2.5.3": + version "2.5.4" + resolved "https://registry.yarnpkg.com/@nuxt/telemetry/-/telemetry-2.5.4.tgz#a395be090cef23de14204cd7b4e1a9faf81e1838" + integrity sha512-KH6wxzsNys69daSO0xUv0LEBAfhwwjK1M+0Cdi1/vxmifCslMIY7lN11B4eywSfscbyVPAYJvANyc7XiVPImBQ== + dependencies: + "@nuxt/kit" "^3.11.2" + ci-info "^4.0.0" + consola "^3.2.3" + create-require "^1.1.1" + defu "^6.1.4" + destr "^2.0.3" + dotenv "^16.4.5" + git-url-parse "^14.0.0" + is-docker "^3.0.0" + jiti "^1.21.0" + mri "^1.2.0" + nanoid "^5.0.7" + ofetch "^1.3.4" + parse-git-config "^3.0.0" + pathe "^1.1.2" + rc9 "^2.1.2" + std-env "^3.7.0" + +"@nuxt/ui-templates@^1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@nuxt/ui-templates/-/ui-templates-1.3.3.tgz#b52728772d247d5027f5cdc0afbc82120b390ff3" + integrity sha512-3BG5doAREcD50dbKyXgmjD4b1GzY8CUy3T41jMhHZXNDdaNwOd31IBq+D6dV00OSrDVhzrTVj0IxsUsnMyHvIQ== + +"@nuxt/vite-builder@3.11.2": + version "3.11.2" + resolved "https://registry.yarnpkg.com/@nuxt/vite-builder/-/vite-builder-3.11.2.tgz#2edd81a1482aefce4a4782ef11b072c3742cc3ad" + integrity sha512-eXTZsAAN4dPz4eA2UD5YU2kD/DqgfyQp1UYsIdCe6+PAVe1ifkUboBjbc0piR5+3qI/S/eqk3nzxRGbiYF7Ccg== + dependencies: + "@nuxt/kit" "3.11.2" + "@rollup/plugin-replace" "^5.0.5" + "@vitejs/plugin-vue" "^5.0.4" + "@vitejs/plugin-vue-jsx" "^3.1.0" + autoprefixer "^10.4.19" + clear "^0.1.0" + consola "^3.2.3" + cssnano "^6.1.2" + defu "^6.1.4" + esbuild "^0.20.2" + escape-string-regexp "^5.0.0" + estree-walker "^3.0.3" + externality "^1.0.2" + fs-extra "^11.2.0" + get-port-please "^3.1.2" + h3 "^1.11.1" + knitwork "^1.1.0" + magic-string "^0.30.9" + mlly "^1.6.1" + ohash "^1.1.3" + pathe "^1.1.2" + perfect-debounce "^1.0.0" + pkg-types "^1.0.3" + postcss "^8.4.38" + rollup-plugin-visualizer "^5.12.0" + std-env "^3.7.0" + strip-literal "^2.1.0" + ufo "^1.5.3" + unenv "^1.9.0" + unplugin "^1.10.1" + vite "^5.2.8" + vite-node "^1.4.0" + vite-plugin-checker "^0.6.4" + vue-bundle-renderer "^2.0.0" + +"@nuxtjs/i18n@^8.3.1": + version "8.3.1" + resolved "https://registry.yarnpkg.com/@nuxtjs/i18n/-/i18n-8.3.1.tgz#bfa6c92991f842ec4df16573dd5cb210907d18be" + integrity sha512-VHnnjFTTep2oC5++61WY06y4c/h943NyHQh1CRUJQvjsdbGSMX3WQjMGk+X05a3pyPFN70aq0YbgtsEoEoTEjQ== + dependencies: + "@intlify/h3" "^0.5.0" + "@intlify/shared" "^9.9.0" + "@intlify/unplugin-vue-i18n" "^3.0.1" + "@intlify/utils" "^0.12.0" + "@miyaneee/rollup-plugin-json5" "^1.1.2" + "@nuxt/kit" "^3.10.3" + "@rollup/plugin-yaml" "^4.1.2" + "@vue/compiler-sfc" "^3.3.4" + debug "^4.3.4" + defu "^6.1.2" + estree-walker "^3.0.3" + is-https "^4.0.0" + knitwork "^1.0.0" + magic-string "^0.30.4" + mlly "^1.4.2" + pathe "^1.1.1" + scule "^1.1.1" + sucrase "^3.34.0" + ufo "^1.3.1" + unplugin "^1.5.0" + vue-i18n "^9.9.0" + vue-router "^4.2.5" + +"@nuxtjs/mdc@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@nuxtjs/mdc/-/mdc-0.6.1.tgz#2db4630eaeb3b3ee8495e58f344acec174cf2ac9" + integrity sha512-zS5QK7DZ/SBrjqQX1DOy7GnxKy+wbj2+LvooefOWmQqHfLTAqJLVIjuv/BmKnQWiRCq19+uysys3iY42EoY5/A== + dependencies: + "@nuxt/kit" "^3.10.3" + "@shikijs/transformers" "^1.1.7" + "@types/hast" "^3.0.4" + "@types/mdast" "^4.0.3" + "@vue/compiler-core" "^3.4.21" + consola "^3.2.3" + debug "^4.3.4" + defu "^6.1.4" + destr "^2.0.3" + detab "^3.0.2" + github-slugger "^2.0.0" + hast-util-to-string "^3.0.0" + mdast-util-to-hast "^13.1.0" + micromark-util-sanitize-uri "^2.0.0" + ohash "^1.1.3" + parse5 "^7.1.2" + pathe "^1.1.2" + property-information "^6.4.1" + rehype-external-links "^3.0.0" + rehype-raw "^7.0.0" + rehype-slug "^6.0.0" + rehype-sort-attribute-values "^5.0.0" + rehype-sort-attributes "^5.0.0" + remark-emoji "^4.0.1" + remark-gfm "^4.0.0" + remark-mdc "^3.1.0" + remark-parse "^11.0.0" + remark-rehype "^11.1.0" + scule "^1.3.0" + shiki "^1.1.7" + ufo "^1.4.0" + unified "^11.0.4" + unist-builder "^4.0.0" + unist-util-visit "^5.0.0" + unwasm "^0.3.7" + +"@nuxtjs/robots@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@nuxtjs/robots/-/robots-3.0.0.tgz#057f0a68d847f22c36b4e574b456173e3f3d80b0" + integrity sha512-qsHGW1KXBvV5Kq7u3huPnFCUTbHS4Fk8Za2mNPgmUXcwLgp3exvhPtdZfIdjrCF/aZk3A8OZONNmGghs6CecBA== + dependencies: + "@nuxt/kit" "^3.0.0" + h3 "^1.0.1" + +"@parcel/watcher-android-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" + integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== + +"@parcel/watcher-darwin-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" + integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== + +"@parcel/watcher-darwin-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" + integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== + +"@parcel/watcher-freebsd-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" + integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== + +"@parcel/watcher-linux-arm-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" + integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== + +"@parcel/watcher-linux-arm64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" + integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== + +"@parcel/watcher-linux-arm64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" + integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== + +"@parcel/watcher-linux-x64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" + integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== + +"@parcel/watcher-linux-x64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" + integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== + +"@parcel/watcher-wasm@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-wasm/-/watcher-wasm-2.4.1.tgz#c4353e4fdb96ee14389856f7f6f6d21b7dcef9e1" + integrity sha512-/ZR0RxqxU/xxDGzbzosMjh4W6NdYFMqq2nvo2b8SLi7rsl/4jkL8S5stIikorNkdR50oVDvqb/3JT05WM+CRRA== + dependencies: + is-glob "^4.0.3" + micromatch "^4.0.5" + napi-wasm "^1.1.0" + +"@parcel/watcher-win32-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" + integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== + +"@parcel/watcher-win32-ia32@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" + integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== + +"@parcel/watcher-win32-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" + integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== + +"@parcel/watcher@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" + integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.4.1" + "@parcel/watcher-darwin-arm64" "2.4.1" + "@parcel/watcher-darwin-x64" "2.4.1" + "@parcel/watcher-freebsd-x64" "2.4.1" + "@parcel/watcher-linux-arm-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-musl" "2.4.1" + "@parcel/watcher-linux-x64-glibc" "2.4.1" + "@parcel/watcher-linux-x64-musl" "2.4.1" + "@parcel/watcher-win32-arm64" "2.4.1" + "@parcel/watcher-win32-ia32" "2.4.1" + "@parcel/watcher-win32-x64" "2.4.1" + +"@pinia-plugin-persistedstate/nuxt@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@pinia-plugin-persistedstate/nuxt/-/nuxt-1.2.0.tgz#559d6abf0204726fa1fb7bb324ea4be99a10e6bd" + integrity sha512-2rtgx5viGSMQMCoFYZMHguA2FhFKCUvw0PwETfqQegsWeBHlqk1/D0G/9xqep8Hq+c1BuFx+jNLJzoLXtYfivg== + dependencies: + "@nuxt/kit" "^3.8.0" + defu "^6.1.2" + pinia-plugin-persistedstate ">=3.2.0" + +"@pinia/nuxt@^0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@pinia/nuxt/-/nuxt-0.5.1.tgz#ee7c979d365a5dfda882430ddfae405fbd78d8d5" + integrity sha512-6wT6TqY81n+7/x3Yhf0yfaJVKkZU42AGqOR0T3+UvChcaOJhSma7OWPN64v+ptYlznat+fS1VTwNAcbi2lzHnw== + dependencies: + "@nuxt/kit" "^3.5.0" + pinia ">=2.1.7" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.25" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.25.tgz#f077fdc0b5d0078d30893396ff4827a13f99e817" + integrity sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ== + +"@rollup/plugin-alias@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-5.1.0.tgz#99a94accc4ff9a3483be5baeedd5d7da3b597e93" + integrity sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ== + dependencies: + slash "^4.0.0" + +"@rollup/plugin-babel@^5.2.0": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283" + integrity sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@rollup/pluginutils" "^3.1.0" + +"@rollup/plugin-commonjs@^25.0.7": + version "25.0.7" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf" + integrity sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.30.3" + +"@rollup/plugin-inject@^5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz#616f3a73fe075765f91c5bec90176608bed277a3" + integrity sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg== + dependencies: + "@rollup/pluginutils" "^5.0.1" + estree-walker "^2.0.2" + magic-string "^0.30.3" + +"@rollup/plugin-json@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-6.1.0.tgz#fbe784e29682e9bb6dee28ea75a1a83702e7b805" + integrity sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA== + dependencies: + "@rollup/pluginutils" "^5.1.0" + +"@rollup/plugin-node-resolve@^15.2.3": + version "15.2.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz#e5e0b059bd85ca57489492f295ce88c2d4b0daf9" + integrity sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + "@types/resolve" "1.20.2" + deepmerge "^4.2.2" + is-builtin-module "^3.2.1" + is-module "^1.0.0" + resolve "^1.22.1" + +"@rollup/plugin-replace@^2.4.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a" + integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + magic-string "^0.25.7" + +"@rollup/plugin-replace@^5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-5.0.5.tgz#33d5653dce6d03cb24ef98bef7f6d25b57faefdf" + integrity sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + magic-string "^0.30.3" + +"@rollup/plugin-terser@^0.4.3", "@rollup/plugin-terser@^0.4.4": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz#15dffdb3f73f121aa4fbb37e7ca6be9aeea91962" + integrity sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A== + dependencies: + serialize-javascript "^6.0.1" + smob "^1.0.0" + terser "^5.17.4" + +"@rollup/plugin-yaml@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@rollup/plugin-yaml/-/plugin-yaml-4.1.2.tgz#a3b4cd5793dfd374b815c60183f5adf21bf1ff66" + integrity sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw== + dependencies: + "@rollup/pluginutils" "^5.0.1" + js-yaml "^4.1.0" + tosource "^2.0.0-alpha.3" + +"@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^4.0.0": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.0.2", "@rollup/pluginutils@^5.0.4", "@rollup/pluginutils@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + +"@rollup/rollup-android-arm-eabi@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz#1a32112822660ee104c5dd3a7c595e26100d4c2d" + integrity sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ== + +"@rollup/rollup-android-arm64@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz#5aeef206d65ff4db423f3a93f71af91b28662c5b" + integrity sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw== + +"@rollup/rollup-darwin-arm64@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz#6b66aaf003c70454c292cd5f0236ebdc6ffbdf1a" + integrity sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw== + +"@rollup/rollup-darwin-x64@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz#f64fc51ed12b19f883131ccbcea59fc68cbd6c0b" + integrity sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz#1a7641111be67c10111f7122d1e375d1226cbf14" + integrity sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A== + +"@rollup/rollup-linux-arm-musleabihf@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz#c93fd632923e0fee25aacd2ae414288d0b7455bb" + integrity sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg== + +"@rollup/rollup-linux-arm64-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz#fa531425dd21d058a630947527b4612d9d0b4a4a" + integrity sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A== + +"@rollup/rollup-linux-arm64-musl@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz#8acc16f095ceea5854caf7b07e73f7d1802ac5af" + integrity sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA== + +"@rollup/rollup-linux-powerpc64le-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz#94e69a8499b5cf368911b83a44bb230782aeb571" + integrity sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ== + +"@rollup/rollup-linux-riscv64-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz#7ef1c781c7e59e85a6ce261cc95d7f1e0b56db0f" + integrity sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg== + +"@rollup/rollup-linux-s390x-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz#f15775841c3232fca9b78cd25a7a0512c694b354" + integrity sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g== + +"@rollup/rollup-linux-x64-gnu@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz#b521d271798d037ad70c9f85dd97d25f8a52e811" + integrity sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ== + +"@rollup/rollup-linux-x64-musl@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz#9254019cc4baac35800991315d133cc9fd1bf385" + integrity sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q== + +"@rollup/rollup-win32-arm64-msvc@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz#27f65a89f6f52ee9426ec11e3571038e4671790f" + integrity sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA== + +"@rollup/rollup-win32-ia32-msvc@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz#a2fbf8246ed0bb014f078ca34ae6b377a90cb411" + integrity sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ== + +"@rollup/rollup-win32-x64-msvc@4.17.2": + version "4.17.2" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503" + integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w== + +"@shikijs/core@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.3.0.tgz#5b93b51ddb8def1e3a1543107f9b5b0540f716f6" + integrity sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA== + +"@shikijs/core@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.4.0.tgz#268de16828ff8a8c8c66f570d54e8203f0521b7c" + integrity sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ== + +"@shikijs/transformers@^1.1.7": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@shikijs/transformers/-/transformers-1.4.0.tgz#098ba08233c0ea53cd4deab4b13a1d92a0dc70db" + integrity sha512-kzvlWmWYYSeaLKRce/kgmFFORUtBtFahfXRKndor0b60ocYiXufBQM6d6w1PlMuUkdk55aor9xLvy9wy7hTEJg== + dependencies: + shiki "1.4.0" + +"@sigstore/bundle@^2.3.0", "@sigstore/bundle@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@sigstore/bundle/-/bundle-2.3.1.tgz#f6cdc67c8400e58ca27f0ef495b27a9327512073" + integrity sha512-eqV17lO3EIFqCWK3969Rz+J8MYrRZKw9IBHpSo6DEcEX2c+uzDFOgHE9f2MnyDpfs48LFO4hXmk9KhQ74JzU1g== + dependencies: + "@sigstore/protobuf-specs" "^0.3.1" + +"@sigstore/core@^1.0.0", "@sigstore/core@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@sigstore/core/-/core-1.1.0.tgz#5583d8f7ffe599fa0a89f2bf289301a5af262380" + integrity sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg== + +"@sigstore/protobuf-specs@^0.3.0", "@sigstore/protobuf-specs@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.3.1.tgz#7095819fa7c5743efde48a858c37b30fab190a09" + integrity sha512-aIL8Z9NsMr3C64jyQzE0XlkEyBLpgEJJFDHLVVStkFV5Q3Il/r/YtY6NJWKQ4cy4AE7spP1IX5Jq7VCAxHHMfQ== + +"@sigstore/sign@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@sigstore/sign/-/sign-2.3.0.tgz#c35e10a3d707e0c69a29bd9f93fa2bdc6275817c" + integrity sha512-tsAyV6FC3R3pHmKS880IXcDJuiFJiKITO1jxR1qbplcsBkZLBmjrEw5GbC7ikD6f5RU1hr7WnmxB/2kKc1qUWQ== + dependencies: + "@sigstore/bundle" "^2.3.0" + "@sigstore/core" "^1.0.0" + "@sigstore/protobuf-specs" "^0.3.1" + make-fetch-happen "^13.0.0" + +"@sigstore/tuf@^2.3.1": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@sigstore/tuf/-/tuf-2.3.2.tgz#e9c5bffc2a5f3434f87195902d7f9cd7f48c70fa" + integrity sha512-mwbY1VrEGU4CO55t+Kl6I7WZzIl+ysSzEYdA1Nv/FTrl2bkeaPXo5PnWZAVfcY2zSdhOpsUTJW67/M2zHXGn5w== + dependencies: + "@sigstore/protobuf-specs" "^0.3.0" + tuf-js "^2.2.0" + +"@sigstore/verify@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@sigstore/verify/-/verify-1.2.0.tgz#48549186305d8a5e471a3a304cf4cb3e0c99dde7" + integrity sha512-hQF60nc9yab+Csi4AyoAmilGNfpXT+EXdBgFkP9OgPwIBPwyqVf7JAWPtmqrrrneTmAT6ojv7OlH1f6Ix5BG4Q== + dependencies: + "@sigstore/bundle" "^2.3.1" + "@sigstore/core" "^1.1.0" + "@sigstore/protobuf-specs" "^0.3.1" + +"@sindresorhus/is@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + +"@sindresorhus/merge-streams@^2.1.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" + integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@surma/rollup-plugin-off-main-thread@^2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053" + integrity sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ== + dependencies: + ejs "^3.1.6" + json5 "^2.2.0" + magic-string "^0.25.0" + string.prototype.matchall "^4.0.6" + +"@tailwindcss/aspect-ratio@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz#9ffd52fee8e3c8b20623ff0dcb29e5c21fb0a9ba" + integrity sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ== + +"@tailwindcss/forms@^0.5.7": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.7.tgz#db5421f062a757b5f828bc9286ba626c6685e821" + integrity sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw== + dependencies: + mini-svg-data-uri "^1.2.3" + +"@tailwindcss/typography@^0.5.13": + version "0.5.13" + resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.13.tgz#cd788a4fa4d0ca2506e242d512f377b22c1f7932" + integrity sha512-ADGcJ8dX21dVVHIwTRgzrcunY6YY9uSlAHHGVKvkA+vLc5qLwEszvKts40lx7z0qc4clpjclwLeK5rVCV2P/uw== + dependencies: + lodash.castarray "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + postcss-selector-parser "6.0.10" + +"@tanstack/virtual-core@3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.5.0.tgz#108208d0f1d75271300bc5560cf9a85a1fa01e89" + integrity sha512-KnPRCkQTyqhanNC0K63GBG3wA8I+D1fQuVnAvcBF8f13akOKeQp1gSbu6f77zCxhEk727iV5oQnbHLYzHrECLg== + +"@tanstack/vue-virtual@^3.0.0-beta.60": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@tanstack/vue-virtual/-/vue-virtual-3.5.0.tgz#5794af3b2d0cc0331ba3f671fba8770e0abd4993" + integrity sha512-wvRQ8sFxn/NDr3WvI5XabhFovZ5MBmpEck2GHpTxYunmV63Ovpl30lRu6W5BPQo35a1GqDZ+Pvzlz6WDWRNqqw== + dependencies: + "@tanstack/virtual-core" "3.5.0" + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@tufjs/canonical-json@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz#a52f61a3d7374833fca945b2549bc30a2dd40d0a" + integrity sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA== + +"@tufjs/models@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tufjs/models/-/models-2.0.0.tgz#c7ab241cf11dd29deb213d6817dabb8c99ce0863" + integrity sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg== + dependencies: + "@tufjs/canonical-json" "2.0.0" + minimatch "^9.0.3" + +"@types/debug@^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/hast@^3.0.0", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + +"@types/http-proxy@^1.17.14": + version "1.17.14" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" + integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== + dependencies: + "@types/node" "*" + +"@types/mdast@^4.0.0", "@types/mdast@^4.0.2", "@types/mdast@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.3.tgz#1e011ff013566e919a4232d1701ad30d70cab333" + integrity sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg== + dependencies: + "@types/unist" "*" + +"@types/ms@*": + version "0.7.34" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" + integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== + +"@types/node@*": + version "20.12.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.12.7.tgz#04080362fa3dd6c5822061aa3124f5c152cff384" + integrity sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg== + dependencies: + undici-types "~5.26.4" + +"@types/resolve@1.20.2": + version "1.20.2" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" + integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== + +"@types/trusted-types@^2.0.2": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +"@types/unist@*", "@types/unist@^3.0.0", "@types/unist@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" + integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== + +"@types/unist@^2.0.0": + version "2.0.10" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" + integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== + +"@types/web-bluetooth@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597" + integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow== + +"@ungap/structured-clone@^1.0.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +"@unhead/dom@1.9.7", "@unhead/dom@^1.7.0", "@unhead/dom@^1.9.4": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@unhead/dom/-/dom-1.9.7.tgz#b5cdcbc84d735e28dc863a9f2e15a1a29d9666c8" + integrity sha512-suZVi8apZCNEMKuasGboBB3njJJm+gd8G0NA89geVozJ0bz40FvLyLEJZ9LirbzpujmhgHhsUSvlq4QyslRqdQ== + dependencies: + "@unhead/schema" "1.9.7" + "@unhead/shared" "1.9.7" + +"@unhead/schema@1.9.7", "@unhead/schema@^1.7.0": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@unhead/schema/-/schema-1.9.7.tgz#44c87f89b7526b7cd81ef5c5d399a63e77d373b6" + integrity sha512-naQGY1gQqq8DmQCxVTOeeXIqaRwbqnLEgvQl12zPEDviYxmg7TCbmKyN9uT4ZarQbJ2WYT2UtYvdSrmTXcwlBw== + dependencies: + hookable "^5.5.3" + zhead "^2.2.4" + +"@unhead/shared@1.9.7": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@unhead/shared/-/shared-1.9.7.tgz#1588ad1c3fb9c9517f48b63261d07d40a1d3a075" + integrity sha512-srji+qaBkkGOTdtTmFxt3AebFYcpt1qQHeQva7X3dSm5nZJDoKj35BJJTZfBSRCjgvkTtsdVUT14f9p9/4BCMA== + dependencies: + "@unhead/schema" "1.9.7" + +"@unhead/ssr@^1.7.0", "@unhead/ssr@^1.9.4": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@unhead/ssr/-/ssr-1.9.7.tgz#4f25baf6cf456310e0c258c526350be481b578f0" + integrity sha512-3K0J9biCypPzJ5o4AgjhKboX2Sas4COj54wfT+ghSfyQ05Lp5IlWxw0FrXuxKPk54ObovskUwIf8eCa9ke0Vuw== + dependencies: + "@unhead/schema" "1.9.7" + "@unhead/shared" "1.9.7" + +"@unhead/vue@^1.7.0", "@unhead/vue@^1.9.4": + version "1.9.7" + resolved "https://registry.yarnpkg.com/@unhead/vue/-/vue-1.9.7.tgz#75b5bcb67a5942192748b2552093f75f169345eb" + integrity sha512-c5pcNvi3FwMfqd+lfD3XUyRKPDv/AVPrep84CFXaqB7ebb+2OQTgtxBiCoRsa8+DtdhYI50lYJ/yeVdfLI9XUw== + dependencies: + "@unhead/schema" "1.9.7" + "@unhead/shared" "1.9.7" + hookable "^5.5.3" + unhead "1.9.7" + +"@vee-validate/i18n@^4.12.6": + version "4.12.6" + resolved "https://registry.yarnpkg.com/@vee-validate/i18n/-/i18n-4.12.6.tgz#fa2fa9b42e35d194fb2887f04127d93af74cf955" + integrity sha512-K3xbjLIQQwDrllS18Ka5nyQDIF/1FuHnedXTr6bfmTm44/7C4MyMGo9KDahnrMWTcFkYIKR7XMshM3R6o54MFg== + +"@vee-validate/rules@^4.12.6": + version "4.12.6" + resolved "https://registry.yarnpkg.com/@vee-validate/rules/-/rules-4.12.6.tgz#a105242ed6df83f46288bbad58337b2297d21fe6" + integrity sha512-vXhunbllildEbCWaBI2m6mij7U4pDTeoqX9CQ7/0gVMhtkTZh+QhKsDSaGI2evatECINH2cpLOBaMkubdN82VQ== + dependencies: + vee-validate "4.12.6" + +"@vercel/nft@^0.26.4": + version "0.26.4" + resolved "https://registry.yarnpkg.com/@vercel/nft/-/nft-0.26.4.tgz#d7e8ebb91567d25240e8cb996152ea77392cea7f" + integrity sha512-j4jCOOXke2t8cHZCIxu1dzKLHLcFmYzC3yqAK6MfZznOL1QIJKd0xcFsXK3zcqzU7ScsE2zWkiMMNHGMHgp+FA== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.5" + "@rollup/pluginutils" "^4.0.0" + acorn "^8.6.0" + acorn-import-attributes "^1.9.2" + async-sema "^3.1.1" + bindings "^1.4.0" + estree-walker "2.0.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + micromatch "^4.0.2" + node-gyp-build "^4.2.2" + resolve-from "^5.0.0" + +"@vite-pwa/assets-generator@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@vite-pwa/assets-generator/-/assets-generator-0.2.4.tgz#ffd5dee762f6e98eaff9938fd52591cb04d8dbc7" + integrity sha512-DXyPLPR/IpbZPSpo1amZEPghY/ziIwpTUKNaz0v1xG+ELzCXmrVQhVzEMqr2JLSqRxjc+UzKfGJA/YdUuaao3w== + dependencies: + cac "^6.7.14" + colorette "^2.0.20" + consola "^3.2.3" + sharp "^0.32.6" + sharp-ico "^0.1.5" + unconfig "^0.3.11" + +"@vite-pwa/nuxt@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@vite-pwa/nuxt/-/nuxt-0.7.0.tgz#413364e9a4b541173d284a0c0253ef72efa1a9a5" + integrity sha512-s4bNChQSlhIIRDZApxWaA4p10UnBDm+DP/mnNgncRaZeWxAg3wpikR8PY/c0ZwJ5H2GBS+k7CVUlKryZZ44DGg== + dependencies: + "@nuxt/kit" "^3.9.0" + pathe "^1.1.1" + ufo "^1.3.2" + vite-plugin-pwa ">=0.20.0 <1" + +"@vitejs/plugin-vue-jsx@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz#9953fd9456539e1f0f253bf0fcd1289e66c67cd1" + integrity sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA== + dependencies: + "@babel/core" "^7.23.3" + "@babel/plugin-transform-typescript" "^7.23.3" + "@vue/babel-plugin-jsx" "^1.1.5" + +"@vitejs/plugin-vue@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz#508d6a0f2440f86945835d903fcc0d95d1bb8a37" + integrity sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ== + +"@vue-macros/common@^1.8.0": + version "1.10.2" + resolved "https://registry.yarnpkg.com/@vue-macros/common/-/common-1.10.2.tgz#4c886082cfd94de2fb16e8e1df99d141873450e7" + integrity sha512-WC66NPVh2mJWqm4L0l/u/cOqm4pNOIwVdMGnDYAH2rHcOWy5x68GkhpkYTBu1+xwCSeHWOQn1TCGGbD+98fFpA== + dependencies: + "@babel/types" "^7.24.0" + "@rollup/pluginutils" "^5.1.0" + "@vue/compiler-sfc" "^3.4.21" + ast-kit "^0.12.1" + local-pkg "^0.5.0" + magic-string-ast "^0.3.0" + +"@vue/babel-helper-vue-transform-on@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.2.2.tgz#7f1f817a4f00ad531651a8d1d22e22d9e42807ef" + integrity sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw== + +"@vue/babel-plugin-jsx@^1.1.5": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.2.2.tgz#eb426fb4660aa510bb8d188ff0ec140405a97d8a" + integrity sha512-nYTkZUVTu4nhP199UoORePsql0l+wj7v/oyQjtThUVhJl1U+6qHuoVhIvR3bf7eVKjbCK+Cs2AWd7mi9Mpz9rA== + dependencies: + "@babel/helper-module-imports" "~7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/template" "^7.23.9" + "@babel/traverse" "^7.23.9" + "@babel/types" "^7.23.9" + "@vue/babel-helper-vue-transform-on" "1.2.2" + "@vue/babel-plugin-resolve-type" "1.2.2" + camelcase "^6.3.0" + html-tags "^3.3.1" + svg-tags "^1.0.0" + +"@vue/babel-plugin-resolve-type@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.2.2.tgz#66844898561da6449e0f4a261b0c875118e0707b" + integrity sha512-EntyroPwNg5IPVdUJupqs0CFzuf6lUrVvCspmv2J1FITLeGnUCuoGNNk78dgCusxEiYj6RMkTJflGSxk5aIC4A== + dependencies: + "@babel/code-frame" "^7.23.5" + "@babel/helper-module-imports" "~7.22.15" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/parser" "^7.23.9" + "@vue/compiler-sfc" "^3.4.15" + +"@vue/compiler-core@3.4.26", "@vue/compiler-core@^3.4.21": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.4.26.tgz#d507886520e83a6f8339ed55ed0b2b5d84b44b73" + integrity sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ== + dependencies: + "@babel/parser" "^7.24.4" + "@vue/shared" "3.4.26" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.0" + +"@vue/compiler-dom@3.4.26", "@vue/compiler-dom@^3.3.4": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz#acc7b788b48152d087d4bb9e655b795e3dbec554" + integrity sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA== + dependencies: + "@vue/compiler-core" "3.4.26" + "@vue/shared" "3.4.26" + +"@vue/compiler-sfc@3.4.26", "@vue/compiler-sfc@^3.2.47", "@vue/compiler-sfc@^3.3.4", "@vue/compiler-sfc@^3.4.15", "@vue/compiler-sfc@^3.4.21": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.4.26.tgz#c679f206829954c3c078d8a9be76d0098b8377ae" + integrity sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw== + dependencies: + "@babel/parser" "^7.24.4" + "@vue/compiler-core" "3.4.26" + "@vue/compiler-dom" "3.4.26" + "@vue/compiler-ssr" "3.4.26" + "@vue/shared" "3.4.26" + estree-walker "^2.0.2" + magic-string "^0.30.10" + postcss "^8.4.38" + source-map-js "^1.2.0" + +"@vue/compiler-ssr@3.4.26": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.4.26.tgz#22842d8adfff972d87bb798b8d496111f7f814b5" + integrity sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ== + dependencies: + "@vue/compiler-dom" "3.4.26" + "@vue/shared" "3.4.26" + +"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.5.1": + version "6.6.1" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.1.tgz#7c14346383751d9f6ad4bea0963245b30220ef83" + integrity sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA== + +"@vue/devtools-applet@^7.0.27": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@vue/devtools-applet/-/devtools-applet-7.1.3.tgz#6aa6d222bb8ccde873d56bb14b11c7f1e36ebaf7" + integrity sha512-525h17FzUF7ssko/U+yeP5jv0HaGm3eI4dVqncWPRCLTDtOy1V+srjoxYqr5qnzx6AdIU2icPQF2KNomd9FGZw== + dependencies: + "@vue/devtools-core" "^7.1.3" + "@vue/devtools-kit" "^7.1.3" + "@vue/devtools-shared" "^7.1.3" + "@vue/devtools-ui" "^7.1.3" + lodash-es "^4.17.21" + perfect-debounce "^1.0.0" + shiki "1.3.0" + splitpanes "^3.1.5" + vue-virtual-scroller "2.0.0-beta.8" + +"@vue/devtools-core@^7.0.27", "@vue/devtools-core@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@vue/devtools-core/-/devtools-core-7.1.3.tgz#22107b618ae05d873545724c6238a8c41c70b426" + integrity sha512-pVbWi8pf2Z/fZPioYOIgu+cv9pQG55k4D8bL31ec+Wfe+pQR0ImFDu0OhHfch1Ra8uvLLrAZTF4IKeGAkmzD4A== + dependencies: + "@vue/devtools-kit" "^7.1.3" + "@vue/devtools-shared" "^7.1.3" + mitt "^3.0.1" + nanoid "^3.3.4" + pathe "^1.1.2" + vite-hot-client "^0.2.3" + +"@vue/devtools-kit@^7.0.27", "@vue/devtools-kit@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.1.3.tgz#0344fd1a926ff535d3be3378e1da8bb71d8430b9" + integrity sha512-NFskFSJMVCBXTkByuk2llzI3KD3Blcm7WqiRorWjD6nClHPgkH5BobDH08rfulqq5ocRt5xV+3qOT1Q9FXJrwQ== + dependencies: + "@vue/devtools-shared" "^7.1.3" + hookable "^5.5.3" + mitt "^3.0.1" + perfect-debounce "^1.0.0" + speakingurl "^14.0.1" + +"@vue/devtools-shared@7.1.3", "@vue/devtools-shared@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.1.3.tgz#f570bba72d53a0c84d1faa19f4d1a29a339c1dc7" + integrity sha512-KJ3AfgjTn3tJz/XKF+BlVShNPecim3G21oHRue+YQOsooW+0s+qXvm09U09aO7yBza5SivL1QgxSrzAbiKWjhQ== + dependencies: + rfdc "^1.3.1" + +"@vue/devtools-ui@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@vue/devtools-ui/-/devtools-ui-7.1.3.tgz#346866cc8a6a72984c7e8b7f12c070c56a6ae387" + integrity sha512-gO2EV3T0wO+HK884+m6UgTEirNOuf+k8U4PcR0vIYA97/A9nTzv9HheCRyFMiHMePYxnlBOsgD7K2fp1/M+EWA== + dependencies: + "@vue/devtools-shared" "7.1.3" + "@vueuse/components" "^10.9.0" + "@vueuse/core" "^10.9.0" + "@vueuse/integrations" "^10.9.0" + colord "^2.9.3" + focus-trap "^7.5.4" + +"@vue/reactivity@3.4.26": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.4.26.tgz#1191f543809d4c93e5b3e842ba83022350a3f205" + integrity sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ== + dependencies: + "@vue/shared" "3.4.26" + +"@vue/runtime-core@3.4.26": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.4.26.tgz#51ee971cb700370a67e5a510c4a84eff7491d658" + integrity sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw== + dependencies: + "@vue/reactivity" "3.4.26" + "@vue/shared" "3.4.26" + +"@vue/runtime-dom@3.4.26": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.4.26.tgz#179aa7c8dc964112e6d096bc8ec5f361111009a1" + integrity sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw== + dependencies: + "@vue/runtime-core" "3.4.26" + "@vue/shared" "3.4.26" + csstype "^3.1.3" + +"@vue/server-renderer@3.4.26": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.4.26.tgz#6d0c6b0366bfe0232579aea00e3ff6784e5a1c60" + integrity sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw== + dependencies: + "@vue/compiler-ssr" "3.4.26" + "@vue/shared" "3.4.26" + +"@vue/shared@3.4.26", "@vue/shared@^3.4.21": + version "3.4.26" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.4.26.tgz#f17854fb1faf889854aed4b23b60e86a8cab6403" + integrity sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ== + +"@vueuse/components@^10.9.0": + version "10.9.0" + resolved "https://registry.yarnpkg.com/@vueuse/components/-/components-10.9.0.tgz#5c1011e0511b68e4d94f5d545343f86d2a7e3044" + integrity sha512-BHQpA0yIi3y7zKa1gYD0FUzLLkcRTqVhP8smnvsCK6GFpd94Nziq1XVPD7YpFeho0k5BzbBiNZF7V/DpkJ967A== + dependencies: + "@vueuse/core" "10.9.0" + "@vueuse/shared" "10.9.0" + vue-demi ">=0.14.7" + +"@vueuse/core@10.9.0", "@vueuse/core@^10.9.0": + version "10.9.0" + resolved "https://registry.yarnpkg.com/@vueuse/core/-/core-10.9.0.tgz#7d779a95cf0189de176fee63cee4ba44b3c85d64" + integrity sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg== + dependencies: + "@types/web-bluetooth" "^0.0.20" + "@vueuse/metadata" "10.9.0" + "@vueuse/shared" "10.9.0" + vue-demi ">=0.14.7" + +"@vueuse/head@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@vueuse/head/-/head-2.0.0.tgz#a4570c0933368a436796c2f737d56e169a8f0864" + integrity sha512-ykdOxTGs95xjD4WXE4na/umxZea2Itl0GWBILas+O4oqS7eXIods38INvk3XkJKjqMdWPcpCyLX/DioLQxU1KA== + dependencies: + "@unhead/dom" "^1.7.0" + "@unhead/schema" "^1.7.0" + "@unhead/ssr" "^1.7.0" + "@unhead/vue" "^1.7.0" + +"@vueuse/integrations@^10.9.0": + version "10.9.0" + resolved "https://registry.yarnpkg.com/@vueuse/integrations/-/integrations-10.9.0.tgz#2b1a9556215ad3c1f96d39cbfbef102cf6e0ec05" + integrity sha512-acK+A01AYdWSvL4BZmCoJAcyHJ6EqhmkQEXbQLwev1MY7NBnS+hcEMx/BzVoR9zKI+UqEPMD9u6PsyAuiTRT4Q== + dependencies: + "@vueuse/core" "10.9.0" + "@vueuse/shared" "10.9.0" + vue-demi ">=0.14.7" + +"@vueuse/metadata@10.9.0": + version "10.9.0" + resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-10.9.0.tgz#769a1a9db65daac15cf98084cbf7819ed3758620" + integrity sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA== + +"@vueuse/nuxt@^10.9.0": + version "10.9.0" + resolved "https://registry.yarnpkg.com/@vueuse/nuxt/-/nuxt-10.9.0.tgz#598663517c0285caa8b6a8221c7c931d32d11c0b" + integrity sha512-nC4Efg28Q6E41fUD5R+zM9uT5c+NfaDzaJCpqaEV/qHj+/BNJmkDBK8POLIUsiVOY35d0oD/YxZ+eVizqWBZow== + dependencies: + "@nuxt/kit" "^3.10.2" + "@vueuse/core" "10.9.0" + "@vueuse/metadata" "10.9.0" + local-pkg "^0.5.0" + vue-demi ">=0.14.7" + +"@vueuse/shared@10.9.0": + version "10.9.0" + resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-10.9.0.tgz#13af2a348de15d07b7be2fd0c7fc9853a69d8fe0" + integrity sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw== + dependencies: + vue-demi ">=0.14.7" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +acorn-import-attributes@^1.9.2: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@8.11.3, acorn@^8.10.0, acorn@^8.11.2, acorn@^8.11.3, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^8.6.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.13.0.tgz#a3939eaec9fb80d217ddf0c3376948c023f28c91" + integrity sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + +anymatch@^3.1.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +archiver-utils@^5.0.0, archiver-utils@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d" + integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA== + dependencies: + glob "^10.0.0" + graceful-fs "^4.2.0" + is-stream "^2.0.1" + lazystream "^1.0.0" + lodash "^4.17.15" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +archiver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61" + integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ== + dependencies: + archiver-utils "^5.0.2" + async "^3.2.4" + buffer-crc32 "^1.0.0" + readable-stream "^4.0.0" + readdir-glob "^1.1.2" + tar-stream "^3.0.0" + zip-stream "^6.0.1" + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + +ast-kit@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/ast-kit/-/ast-kit-0.12.1.tgz#94a42db3c1a3d73efdb627e01839dae15586fdd8" + integrity sha512-O+33g7x6irsESUcd47KdfWUrS2F6aGp9KeVJFGj0YjIznfXpBxVGjA0w+y/1OKqX4mFOfmZ9Xpf1ixPT4n9xxw== + dependencies: + "@babel/parser" "^7.23.9" + pathe "^1.1.2" + +ast-kit@^0.9.4: + version "0.9.5" + resolved "https://registry.yarnpkg.com/ast-kit/-/ast-kit-0.9.5.tgz#88c0ba76b6f7f24c04ccf9ae778e33afc187dc80" + integrity sha512-kbL7ERlqjXubdDd+szuwdlQ1xUxEz9mCz1+m07ftNVStgwRb2RWw+U6oKo08PAvOishMxiqz1mlJyLl8yQx2Qg== + dependencies: + "@babel/parser" "^7.22.7" + "@rollup/pluginutils" "^5.0.2" + pathe "^1.1.1" + +ast-walker-scope@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/ast-walker-scope/-/ast-walker-scope-0.5.0.tgz#87e0ca4f34394d11ec4dea5925b8bda80b811819" + integrity sha512-NsyHMxBh4dmdEHjBo1/TBZvCKxffmZxRYhmclfu0PP6Aftre47jOHYaYaNqJcV0bxihxFXhDkzLHUwHc0ocd0Q== + dependencies: + "@babel/parser" "^7.22.7" + ast-kit "^0.9.4" + +async-sema@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.1.tgz#e527c08758a0f8f6f9f15f799a173ff3c40ea808" + integrity sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg== + +async@^3.2.3, async@^3.2.4: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +autoprefixer@^10.4.19: + version "10.4.19" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.19.tgz#ad25a856e82ee9d7898c59583c1afeb3fa65f89f" + integrity sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew== + dependencies: + browserslist "^4.23.0" + caniuse-lite "^1.0.30001599" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +b4a@^1.6.4: + version "1.6.6" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.6.tgz#a4cc349a3851987c3c4ac2d7785c18744f6da9ba" + integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== + +babel-plugin-polyfill-corejs2@^0.4.10: + version "0.4.11" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz#30320dfe3ffe1a336c15afdcdafd6fd615b25e33" + integrity sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.6.2" + semver "^6.3.1" + +babel-plugin-polyfill-corejs3@^0.10.4: + version "0.10.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz#789ac82405ad664c20476d0233b485281deb9c77" + integrity sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.1" + core-js-compat "^3.36.1" + +babel-plugin-polyfill-regenerator@^0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz#addc47e240edd1da1058ebda03021f382bba785e" + integrity sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.6.2" + +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.2.2.tgz#a98a41841f98b2efe7ecc5c5468814469b018078" + integrity sha512-h7z00dWdG0PYOQEvChhOSWvOfkIKsdZGkWr083FgN/HyoQuebSew/cgirYqh9SCuy/hRvxc5Vy6Fw8xAmYHLkQ== + +bare-fs@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.0.tgz#0872f8e33cf291c9fd527d827154f156a298d402" + integrity sha512-TNFqa1B4N99pds2a5NYHR15o0ZpdNKbAeKTE/+G6ED/UeOavv8RY3dr/Fu99HW3zU3pXpo2kDNO8Sjsm2esfOw== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^1.0.0" + +bare-os@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.3.0.tgz#718e680b139effff0624a7421c098e7a2c2d63da" + integrity sha512-oPb8oMM1xZbhRQBngTgpcQ5gXw6kjOaRsSWsIeNyRxGed2w/ARyP7ScBYpWR1qfX2E5rS3gBw6OWcSQo+s+kUg== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.2.tgz#7a0940d34ebe65f7e179fa61ed8d49d9dc151d67" + integrity sha512-o7KSt4prEphWUHa3QUwCxUI00R86VdjiuxmJK0iNVDHYPGo+HsDaVCnqCmPbf/MiW1ok8F4p3m8RTHlWk8K2ig== + dependencies: + bare-os "^2.1.0" + +bare-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-1.0.0.tgz#25c3e56198d922187320c3f8c52d75c4051178b4" + integrity sha512-KhNUoDL40iP4gFaLSsoGE479t0jHijfYdIcxRn/XtezA2BaUD0NRf/JGRpsMq6dMNM+SrCrB0YSSo/5wBY4rOQ== + dependencies: + streamx "^2.16.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bindings@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +birpc@^0.2.17: + version "0.2.17" + resolved "https://registry.yarnpkg.com/birpc/-/birpc-0.2.17.tgz#d0bdb90d4d063061156637f03b7b0adea1779734" + integrity sha512-+hkTxhot+dWsLpp3gia5AkVHIsKlZybNT5gIYiDlNzJrmYPcTM9k5/w2uaj3IPpd7LlEYpmCj4Jj1nC41VhDFg== + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browserslist@^4.0.0, browserslist@^4.22.2, browserslist@^4.23.0: + version "4.23.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" + integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== + dependencies: + caniuse-lite "^1.0.30001587" + electron-to-chromium "^1.4.668" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-crc32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" + integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + +builtins@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.1.0.tgz#6d85eeb360c4ebc166c3fdef922a15aa7316a5e8" + integrity sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg== + dependencies: + semver "^7.0.0" + +bundle-name@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-4.1.0.tgz#f3b96b34160d6431a19d7688135af7cfb8797889" + integrity sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q== + dependencies: + run-applescript "^7.0.0" + +c12@^1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/c12/-/c12-1.10.0.tgz#e1936baa26fd03a9427875554aa6aeb86077b7fb" + integrity sha512-0SsG7UDhoRWcuSvKWHaXmu5uNjDCDN3nkQLRL4Q42IlFy+ze58FcCoI3uPwINXinkz7ZinbhEgyzYFw9u9ZV8g== + dependencies: + chokidar "^3.6.0" + confbox "^0.1.3" + defu "^6.1.4" + dotenv "^16.4.5" + giget "^1.2.1" + jiti "^1.21.0" + mlly "^1.6.1" + ohash "^1.1.3" + pathe "^1.1.2" + perfect-debounce "^1.0.0" + pkg-types "^1.0.3" + rc9 "^2.1.1" + +cac@^6.7.14: + version "6.7.14" + resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" + integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== + +cacache@^18.0.0: + version "18.0.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.2.tgz#fd527ea0f03a603be5c0da5805635f8eef00c60c" + integrity sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + p-map "^4.0.0" + ssri "^10.0.0" + tar "^6.1.11" + unique-filename "^3.0.0" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001599: + version "1.0.30001614" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz#f894b4209376a0bf923d67d9c361d96b1dfebe39" + integrity sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog== + +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.2, chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== + +chokidar@^3.5.1, chokidar@^3.5.3, chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + 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" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +ci-info@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" + integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + +citty@^0.1.5, citty@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/citty/-/citty-0.1.6.tgz#0f7904da1ed4625e1a9ea7e0fa780981aab7c5e4" + integrity sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ== + dependencies: + consola "^3.2.3" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +clear@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a" + integrity sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw== + +clipboardy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-4.0.0.tgz#e73ced93a76d19dd379ebf1f297565426dffdca1" + integrity sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w== + dependencies: + execa "^8.0.1" + is-wsl "^3.1.0" + is64bit "^2.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + +colord@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" + integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw== + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +compress-commons@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e" + integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg== + dependencies: + crc-32 "^1.2.0" + crc32-stream "^6.0.0" + is-stream "^2.0.1" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confbox@^0.1.3, confbox@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/confbox/-/confbox-0.1.7.tgz#ccfc0a2bcae36a84838e83a3b7f770fb17d6c579" + integrity sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA== + +consola@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.2.3.tgz#0741857aa88cfa0d6fd53f1cff0375136e98502f" + integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-es@^1.0.0, cookie-es@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/cookie-es/-/cookie-es-1.1.0.tgz#68f8d9f48aeb5a534f3896f80e792760d3d20def" + integrity sha512-L2rLOcK0wzWSfSDA33YR+PUHDG10a8px7rUHKWbGLP4YfbsMed2KFUw5fczvDPbT98DDe3LEzviswl810apTEw== + +core-js-compat@^3.31.0, core-js-compat@^3.36.1: + version "3.37.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.37.0.tgz#d9570e544163779bb4dff1031c7972f44918dc73" + integrity sha512-vYq4L+T8aS5UuFg4UwDhc7YNRWVeVZwltad9C/jV3R2LgVOpS9BDr7l/WL6BN0dbV3k1XejPTHqqEzJgsa0frA== + dependencies: + browserslist "^4.23.0" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430" + integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g== + dependencies: + crc-32 "^1.2.0" + readable-stream "^4.0.0" + +create-require@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +croner@^8.0.1: + version "8.0.2" + resolved "https://registry.yarnpkg.com/croner/-/croner-8.0.2.tgz#e8f7b539ba4e23e16d737867fec2d89d984789de" + integrity sha512-HgSdlSUX8mIgDTTiQpWUP4qY4IFRMsduPCYdca34Pelt8MVdxdaDOzreFtCscA6R+cRZd7UbD1CD3uyx6J3X1A== + +cronstrue@^2.49.0: + version "2.49.0" + resolved "https://registry.yarnpkg.com/cronstrue/-/cronstrue-2.49.0.tgz#d59f6d19e33030d45d9ecd3b845d4ccd79c6bfbd" + integrity sha512-FWZBqdStQaPR8ZTBQGALh1EK9Hl1HcG70dyGvD1rKLPafFO3H73o38dz/e8YkIlbLn3JxmBI/f6Doe3Nh+DcEQ== + +cross-spawn@^7.0.0, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crossws@^0.2.0, crossws@^0.2.2, crossws@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/crossws/-/crossws-0.2.4.tgz#82a8b518bff1018ab1d21ced9e35ffbe1681ad03" + integrity sha512-DAxroI2uSOgUKLz00NX6A8U/8EE3SZHmIND+10jkVSaypvyt57J5JEOxAQOL6lQxyzi/wZbTIwssU1uy69h5Vg== + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-declaration-sorter@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz#6dec1c9523bc4a643e088aab8f09e67a54961024" + integrity sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow== + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssnano-preset-default@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz#adf4b89b975aa775f2750c89dbaf199bbd9da35e" + integrity sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg== + dependencies: + browserslist "^4.23.0" + css-declaration-sorter "^7.2.0" + cssnano-utils "^4.0.2" + postcss-calc "^9.0.1" + postcss-colormin "^6.1.0" + postcss-convert-values "^6.1.0" + postcss-discard-comments "^6.0.2" + postcss-discard-duplicates "^6.0.3" + postcss-discard-empty "^6.0.3" + postcss-discard-overridden "^6.0.2" + postcss-merge-longhand "^6.0.5" + postcss-merge-rules "^6.1.1" + postcss-minify-font-values "^6.1.0" + postcss-minify-gradients "^6.0.3" + postcss-minify-params "^6.1.0" + postcss-minify-selectors "^6.0.4" + postcss-normalize-charset "^6.0.2" + postcss-normalize-display-values "^6.0.2" + postcss-normalize-positions "^6.0.2" + postcss-normalize-repeat-style "^6.0.2" + postcss-normalize-string "^6.0.2" + postcss-normalize-timing-functions "^6.0.2" + postcss-normalize-unicode "^6.1.0" + postcss-normalize-url "^6.0.2" + postcss-normalize-whitespace "^6.0.2" + postcss-ordered-values "^6.0.2" + postcss-reduce-initial "^6.1.0" + postcss-reduce-transforms "^6.0.2" + postcss-svgo "^6.0.3" + postcss-unique-selectors "^6.0.4" + +cssnano-utils@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.2.tgz#56f61c126cd0f11f2eef1596239d730d9fceff3c" + integrity sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ== + +cssnano@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-6.1.2.tgz#4bd19e505bd37ee7cf0dc902d3d869f6d79c66b8" + integrity sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA== + dependencies: + cssnano-preset-default "^6.1.2" + lilconfig "^3.1.1" + +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + +csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +db0@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/db0/-/db0-0.1.4.tgz#8df1d9600b812bad0b4129ccbbb7f1b8596a5817" + integrity sha512-Ft6eCwONYxlwLjBXSJxw0t0RYtA5gW9mq8JfBXn9TtC0nDPlqePAhpv9v4g9aONBi6JI1OXHTKKkUYGd+BOrCA== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decode-bmp@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/decode-bmp/-/decode-bmp-0.2.1.tgz#cec3e0197ec3b6c60f02220f50e8757030ff2427" + integrity sha512-NiOaGe+GN0KJqi2STf24hfMkFitDUaIoUU3eKvP/wAbLe8o6FuW5n/x7MHPR0HKvBokp6MQY/j7w8lewEeVCIA== + dependencies: + "@canvas/image-data" "^1.0.0" + to-data-view "^1.1.0" + +decode-ico@*: + version "0.4.1" + resolved "https://registry.yarnpkg.com/decode-ico/-/decode-ico-0.4.1.tgz#e0f7373081532c7b8495bd51fb225d354e14de25" + integrity sha512-69NZfbKIzux1vBOd31al3XnMnH+2mqDhEgLdpygErm4d60N+UwA5Sq5WFjmEDQzumgB9fElojGwWG0vybVfFmA== + dependencies: + "@canvas/image-data" "^1.0.0" + decode-bmp "^0.2.0" + to-data-view "^1.1.0" + +decode-named-character-reference@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" + integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== + dependencies: + character-entities "^2.0.0" + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-browser-id@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" + integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== + +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== + dependencies: + bundle-name "^4.1.0" + default-browser-id "^5.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +defu@^6.0.0, defu@^6.1.2, defu@^6.1.3, defu@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +destr@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/destr/-/destr-2.0.3.tgz#7f9e97cb3d16dbdca7be52aca1644ce402cfe449" + integrity sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detab@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/detab/-/detab-3.0.2.tgz#b9909b52881badd598f653c5e4fcc7c94b158474" + integrity sha512-7Bp16Bk8sk0Y6gdXiCtnpGbghn8atnTJdd/82aWvS5ESnlcNvgUc10U2NYS0PAiDSGjWiI8qs/Cv1b2uSGdQ8w== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +detect-libc@^2.0.0, detect-libc@^2.0.2, detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + +devalue@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/devalue/-/devalue-4.3.3.tgz#e35df3bdc49136837e77986f629b9fa6fef50726" + integrity sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg== + +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + +diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +dot-prop@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-8.0.2.tgz#afda6866610684dd155a96538f8efcdf78a27f18" + integrity sha512-xaBe6ZT4DHPkg0k4Ytbvn5xoxgpG0jOS1dYxSOwAHPuNLjP3/OzN0gH55SrLqpx8cBfSaVt91lXYkApjb+nYdQ== + dependencies: + type-fest "^3.8.0" + +dotenv@^16.4.5: + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@^3.1.6: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.4.668: + version "1.4.752" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.752.tgz#99227455547c8254488e3dab7d316c34a2c067b8" + integrity sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emojilib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.4.0.tgz#ac518a8bb0d5f76dda57289ccb2fdf9d39ae721e" + integrity sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw== + +emoticon@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-4.0.1.tgz#2d2bbbf231ce3a5909e185bbb64a9da703a1e749" + integrity sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +encoding@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +engine.io-client@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.5.3.tgz#4cf6fa24845029b238f83c628916d9149c399bc5" + integrity sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.11.0" + xmlhttprequest-ssl "~2.0.0" + +engine.io-parser@~5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49" + integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw== + +enhanced-resolve@^5.14.1: + version "5.16.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" + integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^4.2.0, entities@^4.4.0, entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + +error-stack-parser-es@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/error-stack-parser-es/-/error-stack-parser-es-0.1.1.tgz#9c1d2bbfbba8b51670062e7fbf43c6bcfb6eb4da" + integrity sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA== + +es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +esbuild@^0.20.1, esbuild@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" + integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== + optionalDependencies: + "@esbuild/aix-ppc64" "0.20.2" + "@esbuild/android-arm" "0.20.2" + "@esbuild/android-arm64" "0.20.2" + "@esbuild/android-x64" "0.20.2" + "@esbuild/darwin-arm64" "0.20.2" + "@esbuild/darwin-x64" "0.20.2" + "@esbuild/freebsd-arm64" "0.20.2" + "@esbuild/freebsd-x64" "0.20.2" + "@esbuild/linux-arm" "0.20.2" + "@esbuild/linux-arm64" "0.20.2" + "@esbuild/linux-ia32" "0.20.2" + "@esbuild/linux-loong64" "0.20.2" + "@esbuild/linux-mips64el" "0.20.2" + "@esbuild/linux-ppc64" "0.20.2" + "@esbuild/linux-riscv64" "0.20.2" + "@esbuild/linux-s390x" "0.20.2" + "@esbuild/linux-x64" "0.20.2" + "@esbuild/netbsd-x64" "0.20.2" + "@esbuild/openbsd-x64" "0.20.2" + "@esbuild/sunos-x64" "0.20.2" + "@esbuild/win32-arm64" "0.20.2" + "@esbuild/win32-ia32" "0.20.2" + "@esbuild/win32-x64" "0.20.2" + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +escodegen@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.4.1: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +espree@^9.0.0: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@2.0.2, estree-walker@^2.0.1, estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@^1.8.1, etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +execa@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" + integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== + 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" + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +exponential-backoff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" + integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +externality@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/externality/-/externality-1.0.2.tgz#a027f8cfd995c42fd35a8d794cfc224d4a5840c0" + integrity sha512-LyExtJWKxtgVzmgtEHyQtLFpw1KFhQphF9nTG8TpAIVkiI/xQ3FJh75tRFLYl4hkn7BNIIdLJInuDAavX35pMw== + dependencies: + enhanced-resolve "^5.14.1" + mlly "^1.3.0" + pathe "^1.1.1" + ufo "^1.1.2" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-fifo@^1.1.0, fast-fifo@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + +fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.3.0, fast-glob@^3.3.1, fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + 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" + +fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +flat@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/flat/-/flat-6.0.1.tgz#09070cf918293b401577f20843edeadf4d3e8755" + integrity sha512-/3FfIa8mbrg3xE7+wAhWeV+bd7L2Mof+xtZb5dRDKZ+wDvYJK4WDYeIOuOhre5Yv5aQObZrlbRmk3RTSiuQBtw== + +flatted@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +focus-trap@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-7.5.4.tgz#6c4e342fe1dae6add9c2aa332a6e7a0bbd495ba2" + integrity sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w== + dependencies: + tabbable "^6.2.0" + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^11.1.0, fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + dependencies: + minipass "^7.0.3" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-port-please@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/get-port-please/-/get-port-please-3.1.2.tgz#502795e56217128e4183025c89a48c71652f4e49" + integrity sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ== + +get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-stream@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" + integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== + +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + +giget@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/giget/-/giget-1.2.3.tgz#ef6845d1140e89adad595f7f3bb60aa31c672cb6" + integrity sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA== + dependencies: + citty "^0.1.6" + consola "^3.2.3" + defu "^6.1.4" + node-fetch-native "^1.6.3" + nypm "^0.3.8" + ohash "^1.1.3" + pathe "^1.1.2" + tar "^6.2.0" + +git-config-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/git-config-path/-/git-config-path-2.0.0.tgz#62633d61af63af4405a5024efd325762f58a181b" + integrity sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA== + +git-up@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/git-up/-/git-up-7.0.0.tgz#bace30786e36f56ea341b6f69adfd83286337467" + integrity sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ== + dependencies: + is-ssh "^1.4.0" + parse-url "^8.1.0" + +git-url-parse@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-14.0.0.tgz#18ce834726d5fbca0c25a4555101aa277017418f" + integrity sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ== + dependencies: + git-up "^7.0.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +github-slugger@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^10.0.0, glob@^10.2.2, glob@^10.3.10: + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" + +glob@^7.1.3, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + 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" + +glob@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +global-directory@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/global-directory/-/global-directory-4.0.1.tgz#4d7ac7cfd2cb73f304c53b8810891748df5e361e" + integrity sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q== + dependencies: + ini "4.1.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globalthis@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + +globby@^14.0.1: + version "14.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.1.tgz#a1b44841aa7f4c6d8af2bc39951109d77301959b" + integrity sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +gzip-size@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-7.0.0.tgz#9f9644251f15bc78460fccef4055ae5a5562ac60" + integrity sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA== + dependencies: + duplexer "^0.1.2" + +h3@^1.0.1, h3@^1.10.2, h3@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/h3/-/h3-1.11.1.tgz#e9414ae6f2a076a345ea07256b320edb29bab9f7" + integrity sha512-AbaH6IDnZN6nmbnJOH72y3c5Wwh9P97soSVdGSBbcDACRdkC0FEWf25pzx4f/NuOCK6quHmW18yF2Wx+G4Zi1A== + dependencies: + cookie-es "^1.0.0" + crossws "^0.2.2" + defu "^6.1.4" + destr "^2.0.3" + iron-webcrypto "^1.0.0" + ohash "^1.1.3" + radix3 "^1.1.0" + ufo "^1.4.0" + uncrypto "^0.1.3" + unenv "^1.9.0" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +hash-sum@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" + integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== + +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hast-util-from-parse5@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz#654a5676a41211e14ee80d1b1758c399a0327651" + integrity sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + hastscript "^8.0.0" + property-information "^6.0.0" + vfile "^6.0.0" + vfile-location "^5.0.0" + web-namespaces "^2.0.0" + +hast-util-heading-rank@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz#2d5c6f2807a7af5c45f74e623498dd6054d2aba8" + integrity sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-is-element@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz#6e31a6532c217e5b533848c7e52c9d9369ca0932" + integrity sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-raw@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-9.0.2.tgz#39b4a4886bd9f0a5dd42e86d02c966c2c152884c" + integrity sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + hast-util-from-parse5 "^8.0.0" + hast-util-to-parse5 "^8.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + parse5 "^7.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-to-parse5@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed" + integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + web-namespaces "^2.0.0" + zwitch "^2.0.0" + +hast-util-to-string@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz#2a131948b4b1b26461a2c8ac876e2c88d02946bd" + integrity sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA== + dependencies: + "@types/hast" "^3.0.0" + +hastscript@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-8.0.0.tgz#4ef795ec8dee867101b9f23cc830d4baf4fd781a" + integrity sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^6.0.0" + space-separated-tokens "^2.0.0" + +hookable@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" + integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== + +hosted-git-info@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.1.tgz#9985fcb2700467fecf7f33a4d4874e30680b5322" + integrity sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA== + dependencies: + lru-cache "^10.0.1" + +html-tags@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + 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" + +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + +http-shutdown@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/http-shutdown/-/http-shutdown-1.2.2.tgz#41bc78fc767637c4c95179bc492f312c0ae64c5f" + integrity sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw== + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" + integrity sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg== + dependencies: + agent-base "^7.0.2" + debug "4" + +httpxy@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/httpxy/-/httpxy-0.1.5.tgz#fd2401206e0b5d919aeda25e967ece0f1a6c8569" + integrity sha512-hqLDO+rfststuyEUTWObQK6zHEEmZ/kaIP2/zclGGZn6X8h/ESTWg+WKecQ/e5k4nPswjzZD+q2VqZIbr15CoQ== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +human-signals@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" + integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== + +ico-endec@*: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ico-endec/-/ico-endec-0.1.6.tgz#9b320cc3ed0a0c779f54e998a8db49002abd7c6e" + integrity sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ== + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idb@^7.0.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" + integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== + +ieee754@^1.1.13, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-walk@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.4.tgz#89950be94b4f522225eb63a13c56badb639190e9" + integrity sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw== + dependencies: + minimatch "^9.0.0" + +ignore@^5.2.4, ignore@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + +image-meta@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/image-meta/-/image-meta-0.2.0.tgz#ea28d05d52f5ad35f75b14f46278a44d626f48bc" + integrity sha512-ZBGjl0ZMEMeOC3Ns0wUF/5UdUmr3qQhBSCniT0LxOgGGIRHiNFOkMtIHB7EOznRU47V2AxPgiVP+s+0/UCU0Hg== + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ini/-/ini-4.1.1.tgz#d95b3d843b1e906e56d6747d5447904ff50ce7a1" + integrity sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g== + +ini@^1.3.5, ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + +ioredis@^5.3.2: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.1.tgz#1c56b70b759f01465913887375ed809134296f40" + integrity sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.1.0" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + +iron-webcrypto@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/iron-webcrypto/-/iron-webcrypto-1.1.1.tgz#245c9d467075ee810343ddfa53dd4909616aaf33" + integrity sha512-5xGwQUWHQSy039rFr+5q/zOmj7GP0Ypzvo34Ep+61bPIhaLduEDp/PvLGlU3awD2mzWUR0weN2vJ1mILydFPEg== + +is-absolute-url@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-4.0.1.tgz#16e4d487d4fded05cfe0685e53ec86804a5e94dc" + integrity sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A== + +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== + +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.8.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== + +is-https@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-https/-/is-https-4.0.0.tgz#9ee725a334fb517b988278d2674efc96e4f348ed" + integrity sha512-FeMLiqf8E5g6SdiVJsPcNZX8k4h2fBs1wp5Bb6uaNxn58ufK1axBqQZdmAQsqh0t9BuwFObybrdVJh6MKyPlyg== + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-installed-globally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz#08952c43758c33d815692392f7f8437b9e436d5a" + integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ== + dependencies: + global-directory "^4.0.1" + is-path-inside "^4.0.0" + +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== + +is-path-inside@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-4.0.0.tgz#805aeb62c47c1b12fc3fd13bfb3ed1e7430071db" + integrity sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA== + +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + +is-primitive@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-3.0.1.tgz#98c4db1abff185485a657fc2905052b940524d05" + integrity sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w== + +is-reference@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== + +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + +is-ssh@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.4.0.tgz#4f8220601d2839d8fa624b3106f8e8884f01b8b2" + integrity sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ== + dependencies: + protocols "^2.0.1" + +is-stream@^2.0.0, is-stream@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +is-wsl@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" + integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== + dependencies: + is-inside-container "^1.0.0" + +is64bit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is64bit/-/is64bit-2.0.0.tgz#198c627cbcb198bbec402251f88e5e1a51236c07" + integrity sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw== + dependencies: + system-architecture "^0.1.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jake@^10.8.5: + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jiti@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-tokens@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-9.0.0.tgz#0f893996d6f3ed46df7f0a3b12a03f5fd84223c1" + integrity sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-even-better-errors@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz#02bb29fb5da90b5444581749c22cedd3597c6cb0" + integrity sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json5@^2.2.0, json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-eslint-parser@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz#74ded53f9d716e8d0671bd167bf5391f452d5461" + integrity sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg== + dependencies: + acorn "^8.5.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + semver "^7.3.5" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +jsonpointer@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + integrity sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA== + +knitwork@^1.0.0, knitwork@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/knitwork/-/knitwork-1.1.0.tgz#d8c9feafadd7ee744ff64340b216a52c7199c417" + integrity sha512-oHnmiBUVHz1V+URE77PNot2lv3QiYU2zQf1JjOVkMt3YDKGbu8NAFr+c4mcNOhdsGrB/VpVbRwPwhiXrPhxQbw== + +kolorist@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c" + integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ== + +launch-editor@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" + integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lilconfig@^3.0.0, lilconfig@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.1.tgz#9d8a246fa753106cfc205fd2d77042faca56e5e3" + integrity sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +listhen@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/listhen/-/listhen-1.7.2.tgz#66b81740692269d5d8cafdc475020f2fc51afbae" + integrity sha512-7/HamOm5YD9Wb7CFgAZkKgVPA96WwhcTQoqtm2VTZGVbVVn3IWKRBTgrU7cchA3Q8k9iCsG8Osoi9GX4JsGM9g== + dependencies: + "@parcel/watcher" "^2.4.1" + "@parcel/watcher-wasm" "^2.4.1" + citty "^0.1.6" + clipboardy "^4.0.0" + consola "^3.2.3" + crossws "^0.2.0" + defu "^6.1.4" + get-port-please "^3.1.2" + h3 "^1.10.2" + http-shutdown "^1.2.2" + jiti "^1.21.0" + mlly "^1.6.1" + node-forge "^1.3.1" + pathe "^1.1.2" + std-env "^3.7.0" + ufo "^1.4.0" + untun "^0.1.3" + uqr "^0.1.2" + +local-pkg@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" + integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== + +local-pkg@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" + integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg== + dependencies: + mlly "^1.4.2" + pkg-types "^1.0.3" + +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" + integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== + +lru-cache@^10.0.1, lru-cache@^10.2.0: + version "10.2.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" + integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string-ast@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/magic-string-ast/-/magic-string-ast-0.3.0.tgz#8fc83ac6d084c5a342645a30354184a6e0ab4382" + integrity sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA== + dependencies: + magic-string "^0.30.2" + +magic-string@^0.25.0, magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + +magic-string@^0.30.0, magic-string@^0.30.10, magic-string@^0.30.2, magic-string@^0.30.3, magic-string@^0.30.4, magic-string@^0.30.5, magic-string@^0.30.8, magic-string@^0.30.9: + version "0.30.10" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" + integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +magicast@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/magicast/-/magicast-0.3.4.tgz#bbda1791d03190a24b00ff3dd18151e7fd381d19" + integrity sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q== + dependencies: + "@babel/parser" "^7.24.4" + "@babel/types" "^7.24.0" + source-map-js "^1.2.0" + +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-fetch-happen@^13.0.0: + version "13.0.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36" + integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA== + dependencies: + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" + is-lambda "^1.0.1" + minipass "^7.0.2" + minipass-fetch "^3.0.0" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.3" + proc-log "^4.2.0" + promise-retry "^2.0.1" + ssri "^10.0.0" + +markdown-table@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" + integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== + +mdast-util-find-and-replace@^3.0.0, mdast-util-find-and-replace@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz#a6fc7b62f0994e973490e45262e4bc07607b04e0" + integrity sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA== + dependencies: + "@types/mdast" "^4.0.0" + escape-string-regexp "^5.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +mdast-util-from-markdown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz#52f14815ec291ed061f2922fd14d6689c810cb88" + integrity sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-gfm-autolink-literal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz#5baf35407421310a08e68c15e5d8821e8898ba2a" + integrity sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg== + dependencies: + "@types/mdast" "^4.0.0" + ccount "^2.0.0" + devlop "^1.0.0" + mdast-util-find-and-replace "^3.0.0" + micromark-util-character "^2.0.0" + +mdast-util-gfm-footnote@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz#25a1753c7d16db8bfd53cd84fe50562bd1e6d6a9" + integrity sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + +mdast-util-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" + integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" + integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + markdown-table "^3.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm-task-list-item@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" + integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== + dependencies: + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz#3f2aecc879785c3cb6a81ff3a243dc11eca61095" + integrity sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-gfm-autolink-literal "^2.0.0" + mdast-util-gfm-footnote "^2.0.0" + mdast-util-gfm-strikethrough "^2.0.0" + mdast-util-gfm-table "^2.0.0" + mdast-util-gfm-task-list-item "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + +mdast-util-to-hast@^13.0.0, mdast-util-to-hast@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz#1ae54d903150a10fe04d59f03b2b95fd210b2124" + integrity sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +mdast-util-to-markdown@^2.0.0, mdast-util-to-markdown@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz#9813f1d6e0cdaac7c244ec8c6dabfdb2102ea2b4" + integrity sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromark-core-commonmark@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d" + integrity sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-autolink-literal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz#f1e50b42e67d441528f39a67133eddde2bbabfd9" + integrity sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-footnote@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz#91afad310065a94b636ab1e9dab2c60d1aab953c" + integrity sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg== + dependencies: + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-strikethrough@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz#6917db8e320da70e39ffbf97abdbff83e6783e61" + integrity sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-table@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz#2cf3fe352d9e089b7ef5fff003bdfe0da29649b7" + integrity sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm-tagfilter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" + integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-gfm-task-list-item@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz#ee8b208f1ced1eb9fb11c19a23666e59d86d4838" + integrity sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw== + dependencies: + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-gfm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" + integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== + dependencies: + micromark-extension-gfm-autolink-literal "^2.0.0" + micromark-extension-gfm-footnote "^2.0.0" + micromark-extension-gfm-strikethrough "^2.0.0" + micromark-extension-gfm-table "^2.0.0" + micromark-extension-gfm-tagfilter "^2.0.0" + micromark-extension-gfm-task-list-item "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-destination@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" + integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-label@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" + integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-space@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" + integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-title@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" + integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-whitespace@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" + integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-character@^2.0.0, micromark-util-character@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" + integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-chunked@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" + integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-classify-character@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" + integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-combine-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" + integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" + integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-decode-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" + integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" + integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== + +micromark-util-html-tag-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" + integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== + +micromark-util-normalize-identifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" + integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-resolve-all@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" + integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== + dependencies: + micromark-util-types "^2.0.0" + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" + integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-subtokenize@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" + integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" + integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== + +micromark-util-types@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" + integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== + +micromark@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" + integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + +mime@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/mime/-/mime-4.0.3.tgz#cd4a1aa052fc980dfc34f111fe1be9e8b878c5d2" + integrity sha512-KgUb15Oorc0NEKPbvfa0wRU+PItIEZmiv+pyAO2i0oTIVTJhlzMclU7w4RXWQrSOVH5ax/p/CkIO7KI4OyFJTQ== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +mini-svg-data-uri@^1.2.3: + version "1.4.4" + resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939" + integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg== + +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1, minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.0, minimatch@^9.0.1, minimatch@^9.0.3: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.3: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== + dependencies: + minipass "^7.0.3" + +minipass-fetch@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.4.tgz#4d4d9b9f34053af6c6e597a64be8e66e42bf45b7" + integrity sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg== + dependencies: + minipass "^7.0.3" + minipass-sized "^1.0.3" + minizlib "^2.1.2" + optionalDependencies: + encoding "^0.1.13" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-json-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz#7edbb92588fbfc2ff1db2fc10397acb7b6b44aa7" + integrity sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg== + dependencies: + jsonparse "^1.3.1" + minipass "^3.0.0" + +minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + +minisearch@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/minisearch/-/minisearch-6.3.0.tgz#985a2f1ca3c73c2d65af94f0616bfe57164b0b6b" + integrity sha512-ihFnidEeU8iXzcVHy74dhkxh/dn8Dc08ERl0xwoMMGqp4+LvRSCgicb+zGqWthVokQKvCSxITlh3P08OzdTYCQ== + +minizlib@^2.1.1, minizlib@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mitt@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-2.1.0.tgz#f740577c23176c6205b121b2973514eade1b2230" + integrity sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg== + +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mlly@^1.2.0, mlly@^1.3.0, mlly@^1.4.2, mlly@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.7.0.tgz#587383ae40dda23cadb11c3c3cc972b277724271" + integrity sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ== + dependencies: + acorn "^8.11.3" + pathe "^1.1.2" + pkg-types "^1.1.0" + ufo "^1.5.3" + +mri@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoid@^3.3.4, nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + +nanoid@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.7.tgz#6452e8c5a816861fd9d2b898399f7e5fd6944cc6" + integrity sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ== + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +napi-wasm@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/napi-wasm/-/napi-wasm-1.1.0.tgz#bbe617823765ae9c1bc12ff5942370eae7b2ba4e" + integrity sha512-lHwIAJbmLSjF9VDRm9GoVOy9AGp3aIvkjv+Kvz9h16QR3uSVYH78PNQUnT2U4X53mhlnV2M7wrhibQ3GHicDmg== + +negotiator@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +nitropack@^2.9.6: + version "2.9.6" + resolved "https://registry.yarnpkg.com/nitropack/-/nitropack-2.9.6.tgz#90af114b796872f34e125e780e6148525149769f" + integrity sha512-HP2PE0dREcDIBVkL8Zm6eVyrDd10/GI9hTL00PHvjUM8I9Y/2cv73wRDmxNyInfrx/CJKHATb2U/pQrqpzJyXA== + dependencies: + "@cloudflare/kv-asset-handler" "^0.3.1" + "@netlify/functions" "^2.6.0" + "@rollup/plugin-alias" "^5.1.0" + "@rollup/plugin-commonjs" "^25.0.7" + "@rollup/plugin-inject" "^5.0.5" + "@rollup/plugin-json" "^6.1.0" + "@rollup/plugin-node-resolve" "^15.2.3" + "@rollup/plugin-replace" "^5.0.5" + "@rollup/plugin-terser" "^0.4.4" + "@rollup/pluginutils" "^5.1.0" + "@types/http-proxy" "^1.17.14" + "@vercel/nft" "^0.26.4" + archiver "^7.0.1" + c12 "^1.10.0" + chalk "^5.3.0" + chokidar "^3.6.0" + citty "^0.1.6" + consola "^3.2.3" + cookie-es "^1.1.0" + croner "^8.0.1" + crossws "^0.2.4" + db0 "^0.1.4" + defu "^6.1.4" + destr "^2.0.3" + dot-prop "^8.0.2" + esbuild "^0.20.2" + escape-string-regexp "^5.0.0" + etag "^1.8.1" + fs-extra "^11.2.0" + globby "^14.0.1" + gzip-size "^7.0.0" + h3 "^1.11.1" + hookable "^5.5.3" + httpxy "^0.1.5" + ioredis "^5.3.2" + is-primitive "^3.0.1" + jiti "^1.21.0" + klona "^2.0.6" + knitwork "^1.1.0" + listhen "^1.7.2" + magic-string "^0.30.8" + mime "^4.0.1" + mlly "^1.6.1" + mri "^1.2.0" + node-fetch-native "^1.6.4" + ofetch "^1.3.4" + ohash "^1.1.3" + openapi-typescript "^6.7.5" + pathe "^1.1.2" + perfect-debounce "^1.0.0" + pkg-types "^1.0.3" + pretty-bytes "^6.1.1" + radix3 "^1.1.2" + rollup "^4.13.2" + rollup-plugin-visualizer "^5.12.0" + scule "^1.3.0" + semver "^7.6.0" + serve-placeholder "^2.0.1" + serve-static "^1.15.0" + std-env "^3.7.0" + ufo "^1.5.3" + uncrypto "^0.1.3" + unctx "^2.3.1" + unenv "^1.9.0" + unimport "^3.7.1" + unstorage "^1.10.2" + unwasm "^0.3.9" + +node-abi@^3.3.0: + version "3.62.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.62.0.tgz#017958ed120f89a3a14a7253da810f5d724e3f36" + integrity sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g== + dependencies: + semver "^7.3.5" + +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + +node-addon-api@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" + integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== + +node-emoji@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-2.1.3.tgz#93cfabb5cc7c3653aa52f29d6ffb7927d8047c06" + integrity sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA== + dependencies: + "@sindresorhus/is" "^4.6.0" + char-regex "^1.0.2" + emojilib "^2.4.0" + skin-tone "^2.0.0" + +node-fetch-native@^1.6.1, node-fetch-native@^1.6.2, node-fetch-native@^1.6.3, node-fetch-native@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.4.tgz#679fc8fd8111266d47d7e72c379f1bed9acff06e" + integrity sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ== + +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-gyp-build@^4.2.2: + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== + +node-gyp@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.1.0.tgz#75e6f223f2acb4026866c26a2ead6aab75a8ca7e" + integrity sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA== + dependencies: + env-paths "^2.2.0" + exponential-backoff "^3.1.1" + glob "^10.3.10" + graceful-fs "^4.2.6" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^3.0.0" + semver "^7.3.5" + tar "^6.1.2" + which "^4.0.0" + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.0.tgz#067378c68116f602f552876194fd11f1292503d7" + integrity sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA== + dependencies: + abbrev "^2.0.0" + +normalize-package-data@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.0.tgz#68a96b3c11edd462af7189c837b6b1064a484196" + integrity sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg== + dependencies: + hosted-git-info "^7.0.0" + is-core-module "^2.8.1" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +npm-bundled@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-3.0.0.tgz#7e8e2f8bb26b794265028491be60321a25a39db7" + integrity sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ== + dependencies: + npm-normalize-package-bin "^3.0.0" + +npm-install-checks@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" + integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== + dependencies: + semver "^7.1.1" + +npm-normalize-package-bin@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz#25447e32a9a7de1f51362c61a559233b89947832" + integrity sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ== + +npm-package-arg@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-11.0.2.tgz#1ef8006c4a9e9204ddde403035f7ff7d718251ca" + integrity sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw== + dependencies: + hosted-git-info "^7.0.0" + proc-log "^4.0.0" + semver "^7.3.5" + validate-npm-package-name "^5.0.0" + +npm-packlist@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-8.0.2.tgz#5b8d1d906d96d21c85ebbeed2cf54147477c8478" + integrity sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA== + dependencies: + ignore-walk "^6.0.4" + +npm-pick-manifest@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz#f87a4c134504a2c7931f2bb8733126e3c3bb7e8f" + integrity sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg== + dependencies: + npm-install-checks "^6.0.0" + npm-normalize-package-bin "^3.0.0" + npm-package-arg "^11.0.0" + semver "^7.3.5" + +npm-registry-fetch@^17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-17.0.0.tgz#b93bd8d55e6e7dc2f9ea6832e8b19922cdf3548f" + integrity sha512-JoOpdYqru846tJX96Jn2jyYVpc1TD1o6Oox80rjVIDAZqIsS2n+nNx+/Qd02LlQm/itGhsBgzP1VUKACLQHD+Q== + dependencies: + "@npmcli/redact" "^2.0.0" + make-fetch-happen "^13.0.0" + minipass "^7.0.2" + minipass-fetch "^3.0.0" + minipass-json-stream "^1.0.1" + minizlib "^2.1.2" + npm-package-arg "^11.0.0" + proc-log "^4.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" + integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== + dependencies: + path-key "^4.0.0" + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +nuxi@^3.11.1: + version "3.11.1" + resolved "https://registry.yarnpkg.com/nuxi/-/nuxi-3.11.1.tgz#a9c03333fde2200fd9f7d2965c33e93d3edeb378" + integrity sha512-AW71TpxRHNg8MplQVju9tEFvXPvX42e0wPYknutSStDuAjV99vWTWYed4jxr/grk2FtKAuv2KvdJxcn2W59qyg== + optionalDependencies: + fsevents "~2.3.3" + +nuxt@^3.11.2: + version "3.11.2" + resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-3.11.2.tgz#38e14b96ff5669b1fbae7fc99e8d79042dce658d" + integrity sha512-Be1d4oyFo60pdF+diBolYDcfNemoMYM3R8PDjhnGrs/w3xJoDH1YMUVWHXXY8WhSmYZI7dyBehx/6kTfGFliVA== + dependencies: + "@nuxt/devalue" "^2.0.2" + "@nuxt/devtools" "^1.1.5" + "@nuxt/kit" "3.11.2" + "@nuxt/schema" "3.11.2" + "@nuxt/telemetry" "^2.5.3" + "@nuxt/ui-templates" "^1.3.2" + "@nuxt/vite-builder" "3.11.2" + "@unhead/dom" "^1.9.4" + "@unhead/ssr" "^1.9.4" + "@unhead/vue" "^1.9.4" + "@vue/shared" "^3.4.21" + acorn "8.11.3" + c12 "^1.10.0" + chokidar "^3.6.0" + cookie-es "^1.1.0" + defu "^6.1.4" + destr "^2.0.3" + devalue "^4.3.2" + esbuild "^0.20.2" + escape-string-regexp "^5.0.0" + estree-walker "^3.0.3" + fs-extra "^11.2.0" + globby "^14.0.1" + h3 "^1.11.1" + hookable "^5.5.3" + jiti "^1.21.0" + klona "^2.0.6" + knitwork "^1.1.0" + magic-string "^0.30.9" + mlly "^1.6.1" + nitropack "^2.9.6" + nuxi "^3.11.1" + nypm "^0.3.8" + ofetch "^1.3.4" + ohash "^1.1.3" + pathe "^1.1.2" + perfect-debounce "^1.0.0" + pkg-types "^1.0.3" + radix3 "^1.1.2" + scule "^1.3.0" + std-env "^3.7.0" + strip-literal "^2.1.0" + ufo "^1.5.3" + ultrahtml "^1.5.3" + uncrypto "^0.1.3" + unctx "^2.3.1" + unenv "^1.9.0" + unimport "^3.7.1" + unplugin "^1.10.1" + unplugin-vue-router "^0.7.0" + unstorage "^1.10.2" + untyped "^1.4.2" + vue "^3.4.21" + vue-bundle-renderer "^2.0.0" + vue-devtools-stub "^0.1.0" + vue-router "^4.3.0" + +nypm@^0.3.8: + version "0.3.8" + resolved "https://registry.yarnpkg.com/nypm/-/nypm-0.3.8.tgz#a16b078b161be5885351e72cf0b97326973722bf" + integrity sha512-IGWlC6So2xv6V4cIDmoV0SwwWx7zLG086gyqkyumteH2fIgCAM4nDVFB2iDRszDvmdSVW9xb1N+2KjQ6C7d4og== + dependencies: + citty "^0.1.6" + consola "^3.2.3" + execa "^8.0.1" + pathe "^1.1.2" + ufo "^1.4.0" + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +ofetch@^1.3.3, ofetch@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ofetch/-/ofetch-1.3.4.tgz#7ea65ced3c592ec2b9906975ae3fe1d26a56f635" + integrity sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw== + dependencies: + destr "^2.0.3" + node-fetch-native "^1.6.3" + ufo "^1.5.3" + +ohash@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/ohash/-/ohash-1.1.3.tgz#f12c3c50bfe7271ce3fd1097d42568122ccdcf07" + integrity sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-10.1.0.tgz#a7795e6e5d519abe4286d9937bb24b51122598e1" + integrity sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw== + dependencies: + default-browser "^5.2.1" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^3.1.0" + +open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +openapi-typescript@^6.7.5: + version "6.7.5" + resolved "https://registry.yarnpkg.com/openapi-typescript/-/openapi-typescript-6.7.5.tgz#3e7f0d080d540396ef8db3df4ed07e1a4a5bb1d8" + integrity sha512-ZD6dgSZi0u1QCP55g8/2yS5hNJfIpgqsSGHLxxdOjvY7eIrXzj271FJEQw33VwsZ6RCtO/NOuhxa7GBWmEudyA== + dependencies: + ansi-colors "^4.1.3" + fast-glob "^3.3.2" + js-yaml "^4.1.0" + supports-color "^9.4.0" + undici "^5.28.2" + yargs-parser "^21.1.1" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +pacote@^18.0.0: + version "18.0.3" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-18.0.3.tgz#72a2bafc603935d2a1b0bf7c17034fdcc0a58001" + integrity sha512-GFCGn27RSf2xa5DHShI7DzI8dCf5F0+9N+fbSpItZvi9ykSouPRXMKzfl2jF35Zoi9QysNF/aqeqQyU2tOJBbA== + dependencies: + "@npmcli/git" "^5.0.0" + "@npmcli/installed-package-contents" "^2.0.1" + "@npmcli/package-json" "^5.1.0" + "@npmcli/promise-spawn" "^7.0.0" + "@npmcli/run-script" "^8.0.0" + cacache "^18.0.0" + fs-minipass "^3.0.0" + minipass "^7.0.2" + npm-package-arg "^11.0.0" + npm-packlist "^8.0.0" + npm-pick-manifest "^9.0.0" + npm-registry-fetch "^17.0.0" + proc-log "^4.0.0" + promise-retry "^2.0.1" + sigstore "^2.2.0" + ssri "^10.0.0" + tar "^6.1.11" + +parse-entities@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e" + integrity sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w== + dependencies: + "@types/unist" "^2.0.0" + character-entities "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + +parse-git-config@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/parse-git-config/-/parse-git-config-3.0.0.tgz#4a2de08c7b74a2555efa5ae94d40cd44302a6132" + integrity sha512-wXoQGL1D+2COYWCD35/xbiKma1Z15xvZL8cI25wvxzled58V51SJM04Urt/uznS900iQor7QO04SgdfT/XlbuA== + dependencies: + git-config-path "^2.0.0" + ini "^1.3.5" + +parse-path@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse-path/-/parse-path-7.0.0.tgz#605a2d58d0a749c8594405d8cc3a2bf76d16099b" + integrity sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog== + dependencies: + protocols "^2.0.0" + +parse-url@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-url/-/parse-url-8.1.0.tgz#972e0827ed4b57fc85f0ea6b0d839f0d8a57a57d" + integrity sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w== + dependencies: + parse-path "^7.0.0" + +parse5@^7.0.0, parse5@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + +pathe@^1.0.0, pathe@^1.1.1, pathe@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" + integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== + +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pinia-plugin-persistedstate@>=3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.1.tgz#66780602aecd6c7b152dd7e3ddc249a1f7a13fe5" + integrity sha512-MK++8LRUsGF7r45PjBFES82ISnPzyO6IZx3CH5vyPseFLZCk1g2kgx6l/nW8pEBKxxd4do0P6bJw+mUSZIEZUQ== + +pinia@>=2.1.7, pinia@^2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.1.7.tgz#4cf5420d9324ca00b7b4984d3fbf693222115bbc" + integrity sha512-+C2AHFtcFqjPih0zpYuvof37SFxMQ7OEG2zV9jRI12i9BOy3YQVAHwdKtyyc8pDcDyIc33WCIsZaCFWU7WWxGQ== + dependencies: + "@vue/devtools-api" "^6.5.0" + vue-demi ">=0.14.5" + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-types@^1.0.3, pkg-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.1.0.tgz#3ec1bf33379030fd0a34c227b6c650e8ea7ca271" + integrity sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA== + dependencies: + confbox "^0.1.7" + mlly "^1.6.1" + pathe "^1.1.2" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +postcss-calc@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-9.0.1.tgz#a744fd592438a93d6de0f1434c572670361eb6c6" + integrity sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ== + dependencies: + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + +postcss-colormin@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-6.1.0.tgz#076e8d3fb291fbff7b10e6b063be9da42ff6488d" + integrity sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw== + dependencies: + browserslist "^4.23.0" + caniuse-api "^3.0.0" + colord "^2.9.3" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz#3498387f8efedb817cbc63901d45bd1ceaa40f48" + integrity sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w== + dependencies: + browserslist "^4.23.0" + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz#e768dcfdc33e0216380623652b0a4f69f4678b6c" + integrity sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw== + +postcss-discard-duplicates@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz#d121e893c38dc58a67277f75bb58ba43fce4c3eb" + integrity sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw== + +postcss-discard-empty@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz#ee39c327219bb70473a066f772621f81435a79d9" + integrity sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ== + +postcss-discard-overridden@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz#4e9f9c62ecd2df46e8fdb44dc17e189776572e2d" + integrity sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ== + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + +postcss-merge-longhand@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz#ba8a8d473617c34a36abbea8dda2b215750a065a" + integrity sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^6.1.1" + +postcss-merge-rules@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz#7aa539dceddab56019469c0edd7d22b64c3dea9d" + integrity sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ== + dependencies: + browserslist "^4.23.0" + caniuse-api "^3.0.0" + cssnano-utils "^4.0.2" + postcss-selector-parser "^6.0.16" + +postcss-minify-font-values@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz#a0e574c02ee3f299be2846369211f3b957ea4c59" + integrity sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz#ca3eb55a7bdb48a1e187a55c6377be918743dbd6" + integrity sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q== + dependencies: + colord "^2.9.3" + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz#54551dec77b9a45a29c3cb5953bf7325a399ba08" + integrity sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA== + dependencies: + browserslist "^4.23.0" + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz#197f7d72e6dd19eed47916d575d69dc38b396aff" + integrity sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ== + dependencies: + postcss-selector-parser "^6.0.16" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + integrity sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ== + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-normalize-charset@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz#1ec25c435057a8001dac942942a95ffe66f721e1" + integrity sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ== + +postcss-normalize-display-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz#54f02764fed0b288d5363cbb140d6950dbbdd535" + integrity sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz#e982d284ec878b9b819796266f640852dbbb723a" + integrity sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz#f8006942fd0617c73f049dd8b6201c3a3040ecf3" + integrity sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz#e3cc6ad5c95581acd1fc8774b309dd7c06e5e363" + integrity sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz#40cb8726cef999de984527cbd9d1db1f3e9062c0" + integrity sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz#aaf8bbd34c306e230777e80f7f12a4b7d27ce06e" + integrity sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg== + dependencies: + browserslist "^4.23.0" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz#292792386be51a8de9a454cb7b5c58ae22db0f79" + integrity sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz#fbb009e6ebd312f8b2efb225c2fcc7cf32b400cd" + integrity sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz#366bb663919707093451ab70c3f99c05672aaae5" + integrity sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q== + dependencies: + cssnano-utils "^4.0.2" + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz#4401297d8e35cb6e92c8e9586963e267105586ba" + integrity sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw== + dependencies: + browserslist "^4.23.0" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz#6fa2c586bdc091a7373caeee4be75a0f3e12965d" + integrity sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@6.0.10: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.16: + version "6.0.16" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz#3b88b9f5c5abd989ef4e2fc9ec8eedd34b20fb04" + integrity sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-svgo@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.3.tgz#1d6e180d6df1fa8a3b30b729aaa9161e94f04eaa" + integrity sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g== + dependencies: + postcss-value-parser "^4.2.0" + svgo "^3.2.0" + +postcss-unique-selectors@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz#983ab308896b4bf3f2baaf2336e14e52c11a2088" + integrity sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg== + dependencies: + postcss-selector-parser "^6.0.16" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.23, postcss@^8.4.38: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + +prebuild-install@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +pretty-bytes@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + +pretty-bytes@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz#38cd6bb46f47afbf667c202cfc754bffd2016a3b" + integrity sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ== + +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + +proc-log@^4.0.0, proc-log@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +property-information@^6.0.0, property-information@^6.4.1: + version "6.5.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" + integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== + +protocols@^2.0.0, protocols@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" + integrity sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qrcode.vue@^3.3.3: + version "3.4.1" + resolved "https://registry.yarnpkg.com/qrcode.vue/-/qrcode.vue-3.4.1.tgz#dd8141da9c4ea07ee56b111cd13eadf123af822a" + integrity sha512-wq/zHsifH4FJ1GXQi8/wNxD1KfQkckIpjK1KPTc/qwYU5/Bkd4me0w4xZSg6EXk6xLBkVDE0zxVagewv5EMAVA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + +radix3@^1.1.0, radix3@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.1.2.tgz#fd27d2af3896c6bf4bcdfab6427c69c2afc69ec0" + integrity sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +rc9@^2.1.1, rc9@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/rc9/-/rc9-2.1.2.tgz#6282ff638a50caa0a91a31d76af4a0b9cbd1080d" + integrity sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg== + dependencies: + defu "^6.1.4" + destr "^2.0.3" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + +readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readdir-glob@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== + dependencies: + redis-errors "^1.0.0" + +regenerate-unicode-properties@^10.1.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz#6b0e05489d9076b04c436f318d9b067bba459480" + integrity sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +rehype-external-links@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rehype-external-links/-/rehype-external-links-3.0.0.tgz#2b28b5cda1932f83f045b6f80a3e1b15f168c6f6" + integrity sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw== + dependencies: + "@types/hast" "^3.0.0" + "@ungap/structured-clone" "^1.0.0" + hast-util-is-element "^3.0.0" + is-absolute-url "^4.0.0" + space-separated-tokens "^2.0.0" + unist-util-visit "^5.0.0" + +rehype-raw@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4" + integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== + dependencies: + "@types/hast" "^3.0.0" + hast-util-raw "^9.0.0" + vfile "^6.0.0" + +rehype-slug@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rehype-slug/-/rehype-slug-6.0.0.tgz#1d21cf7fc8a83ef874d873c15e6adaee6344eaf1" + integrity sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A== + dependencies: + "@types/hast" "^3.0.0" + github-slugger "^2.0.0" + hast-util-heading-rank "^3.0.0" + hast-util-to-string "^3.0.0" + unist-util-visit "^5.0.0" + +rehype-sort-attribute-values@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/rehype-sort-attribute-values/-/rehype-sort-attribute-values-5.0.0.tgz#044ef3f0fbe9ddde0b63cf0dc53257b36fa3d990" + integrity sha512-dQdHdCIRnpiU+BkrLSqH+aM4lWJyLqGzv49KvH4gHj+JxYwNqvGhoTXckS3AJu4V9ZutwsTcawP0pC7PhwX0tQ== + dependencies: + "@types/hast" "^3.0.0" + hast-util-is-element "^3.0.0" + unist-util-visit "^5.0.0" + +rehype-sort-attributes@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/rehype-sort-attributes/-/rehype-sort-attributes-5.0.0.tgz#59e7f976d27001682373ac080774cd1386eb446b" + integrity sha512-6tJUH4xHFcdO85CZRwAcEtHNCzjZ9V9S0VZLgo1pzbN04qy8jiVCZ3oAxDmBVG3Rth5b1xFTDet5WG/UYZeJLQ== + dependencies: + "@types/hast" "^3.0.0" + unist-util-visit "^5.0.0" + +remark-emoji@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-4.0.1.tgz#671bfda668047689e26b2078c7356540da299f04" + integrity sha512-fHdvsTR1dHkWKev9eNyhTo4EFwbUvJ8ka9SgeWkMPYFX4WoI7ViVBms3PjlQYgw5TLvNQso3GUB/b/8t3yo+dg== + dependencies: + "@types/mdast" "^4.0.2" + emoticon "^4.0.1" + mdast-util-find-and-replace "^3.0.1" + node-emoji "^2.1.0" + unified "^11.0.4" + +remark-gfm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-4.0.0.tgz#aea777f0744701aa288b67d28c43565c7e8c35de" + integrity sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-gfm "^3.0.0" + micromark-extension-gfm "^3.0.0" + remark-parse "^11.0.0" + remark-stringify "^11.0.0" + unified "^11.0.0" + +remark-mdc@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/remark-mdc/-/remark-mdc-3.2.0.tgz#268a4368ed79ac6783a87a0797937ce55a04cb7f" + integrity sha512-zRi5frIC3O/bcxXgUPHfQ3lyRBKPtokrGnsOPvNrt9bqp4EfjPtduzcWgO4R1WeHYUnvl0zeBStvGsFkJIZf+Q== + dependencies: + "@types/mdast" "^4.0.3" + "@types/unist" "^3.0.2" + flat "^6.0.1" + js-yaml "^4.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.1.0" + micromark "^4.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.1.0" + micromark-util-types "^2.0.0" + parse-entities "^4.0.1" + scule "^1.3.0" + stringify-entities "^4.0.3" + unified "^11.0.4" + unist-util-visit "^5.0.0" + unist-util-visit-parents "^6.0.1" + +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-rehype@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.0.tgz#d5f264f42bcbd4d300f030975609d01a1697ccdc" + integrity sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + mdast-util-to-hast "^13.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +remark-stringify@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" + integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-to-markdown "^2.0.0" + unified "^11.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.1.7, resolve@^1.14.2, resolve@^1.22.1, resolve@^1.22.2: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" + integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rollup-plugin-visualizer@^5.12.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.12.0.tgz#661542191ce78ee4f378995297260d0c1efb1302" + integrity sha512-8/NU9jXcHRs7Nnj07PF2o4gjxmm9lXIrZ8r175bT9dK8qoLlvKTwRMArRCMgpMGlq8CTLugRvEmyMeMXIU2pNQ== + dependencies: + open "^8.4.0" + picomatch "^2.3.1" + source-map "^0.7.4" + yargs "^17.5.1" + +rollup@^2.43.1: + version "2.79.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + optionalDependencies: + fsevents "~2.3.2" + +rollup@^4.13.0, rollup@^4.13.2: + version "4.17.2" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.17.2.tgz#26d1785d0144122277fdb20ab3a24729ae68301f" + integrity sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ== + dependencies: + "@types/estree" "1.0.5" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.17.2" + "@rollup/rollup-android-arm64" "4.17.2" + "@rollup/rollup-darwin-arm64" "4.17.2" + "@rollup/rollup-darwin-x64" "4.17.2" + "@rollup/rollup-linux-arm-gnueabihf" "4.17.2" + "@rollup/rollup-linux-arm-musleabihf" "4.17.2" + "@rollup/rollup-linux-arm64-gnu" "4.17.2" + "@rollup/rollup-linux-arm64-musl" "4.17.2" + "@rollup/rollup-linux-powerpc64le-gnu" "4.17.2" + "@rollup/rollup-linux-riscv64-gnu" "4.17.2" + "@rollup/rollup-linux-s390x-gnu" "4.17.2" + "@rollup/rollup-linux-x64-gnu" "4.17.2" + "@rollup/rollup-linux-x64-musl" "4.17.2" + "@rollup/rollup-win32-arm64-msvc" "4.17.2" + "@rollup/rollup-win32-ia32-msvc" "4.17.2" + "@rollup/rollup-win32-x64-msvc" "4.17.2" + fsevents "~2.3.2" + +run-applescript@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-7.0.0.tgz#e5a553c2bffd620e169d276c1cd8f1b64778fbeb" + integrity sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scule@^1.0.0, scule@^1.1.1, scule@^1.2.0, scule@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3" + integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g== + +semver@^6.0.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.5.0, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" + integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + 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" + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +serve-placeholder@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/serve-placeholder/-/serve-placeholder-2.0.1.tgz#dfa741812f49dfea472a68c4f292dbc40d28389a" + integrity sha512-rUzLlXk4uPFnbEaIz3SW8VISTxMuONas88nYWjAWaM2W9VDbt9tyFOr3lq8RhVOFrT3XISoBw8vni5una8qMnQ== + dependencies: + defu "^6.0.0" + +serve-static@^1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1, set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sharp-ico@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/sharp-ico/-/sharp-ico-0.1.5.tgz#5d263558adfb00903313db9403e20dc66ffcfa5b" + integrity sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q== + dependencies: + decode-ico "*" + ico-endec "*" + sharp "*" + +sharp@*: + version "0.33.3" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.3.tgz#3342fe0aa5ed45a363e6578fa575c7af366216c2" + integrity sha512-vHUeXJU1UvlO/BNwTpT0x/r53WkLUVxrmb5JTgW92fdFCFk0ispLMAeu/jPO2vjkXM1fYUi3K7/qcLF47pwM1A== + dependencies: + color "^4.2.3" + detect-libc "^2.0.3" + semver "^7.6.0" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.33.3" + "@img/sharp-darwin-x64" "0.33.3" + "@img/sharp-libvips-darwin-arm64" "1.0.2" + "@img/sharp-libvips-darwin-x64" "1.0.2" + "@img/sharp-libvips-linux-arm" "1.0.2" + "@img/sharp-libvips-linux-arm64" "1.0.2" + "@img/sharp-libvips-linux-s390x" "1.0.2" + "@img/sharp-libvips-linux-x64" "1.0.2" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.2" + "@img/sharp-libvips-linuxmusl-x64" "1.0.2" + "@img/sharp-linux-arm" "0.33.3" + "@img/sharp-linux-arm64" "0.33.3" + "@img/sharp-linux-s390x" "0.33.3" + "@img/sharp-linux-x64" "0.33.3" + "@img/sharp-linuxmusl-arm64" "0.33.3" + "@img/sharp-linuxmusl-x64" "0.33.3" + "@img/sharp-wasm32" "0.33.3" + "@img/sharp-win32-ia32" "0.33.3" + "@img/sharp-win32-x64" "0.33.3" + +sharp@^0.32.6: + version "0.32.6" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" + integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== + dependencies: + color "^4.2.3" + detect-libc "^2.0.2" + node-addon-api "^6.1.0" + prebuild-install "^7.1.1" + semver "^7.5.4" + simple-get "^4.0.1" + tar-fs "^3.0.4" + tunnel-agent "^0.6.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +shiki@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.3.0.tgz#3eda35cb49f6f0a98525e9da48fc072e6c655a3f" + integrity sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww== + dependencies: + "@shikijs/core" "1.3.0" + +shiki@1.4.0, shiki@^1.1.7: + version "1.4.0" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.4.0.tgz#477ea3d760ea6889b523ed0da772a9031cb27644" + integrity sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q== + dependencies: + "@shikijs/core" "1.4.0" + +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +signal-exit@^3.0.0, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +sigstore@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/sigstore/-/sigstore-2.3.0.tgz#c56b32818d4dc989f6ea3c0897f4d9bff5d14bed" + integrity sha512-q+o8L2ebiWD1AxD17eglf1pFrl9jtW7FHa0ygqY6EKvibK8JHyq9Z26v9MZXeDiw+RbfOJ9j2v70M10Hd6E06A== + dependencies: + "@sigstore/bundle" "^2.3.1" + "@sigstore/core" "^1.0.0" + "@sigstore/protobuf-specs" "^0.3.1" + "@sigstore/sign" "^2.3.0" + "@sigstore/tuf" "^2.3.1" + "@sigstore/verify" "^1.2.0" + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0, simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-git@^3.24.0: + version "3.24.0" + resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-3.24.0.tgz#33a8c88dc6fa74e53eaf3d6bfc27d0182a49ec00" + integrity sha512-QqAKee9Twv+3k8IFOFfPB2hnk6as6Y6ACUpwCtQvRYBAes23Wv3SZlHVobAzqcE8gfsisCvPw3HGW3HYM+VYYw== + dependencies: + "@kwsites/file-exists" "^1.1.1" + "@kwsites/promise-deferred" "^1.1.1" + debug "^4.3.4" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + +sirv@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== + dependencies: + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +skin-tone@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/skin-tone/-/skin-tone-2.0.0.tgz#4e3933ab45c0d4f4f781745d64b9f4c208e41237" + integrity sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA== + dependencies: + unicode-emoji-modifier-base "^1.0.0" + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== + +slugify@^1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b" + integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== + +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +smob@^1.0.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/smob/-/smob-1.5.0.tgz#85d79a1403abf128d24d3ebc1cdc5e1a9548d3ab" + integrity sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig== + +socket.io-client@^4.7.4: + version "4.7.5" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.7.5.tgz#919be76916989758bdc20eec63f7ee0ae45c05b7" + integrity sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.5.2" + socket.io-parser "~4.2.4" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socks-proxy-agent@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz#6b2da3d77364fde6292e810b496cb70440b9b89d" + integrity sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A== + dependencies: + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.7.1" + +socks@^2.7.1: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + +source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +source-map@^0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" + integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== + dependencies: + whatwg-url "^7.0.0" + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.17" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c" + integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== + +speakingurl@^14.0.1: + version "14.0.1" + resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53" + integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== + +splitpanes@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/splitpanes/-/splitpanes-3.1.5.tgz#de81da25681c252d131747a9cb48a17156e2b210" + integrity sha512-r3Mq2ITFQ5a2VXLOy4/Sb2Ptp7OfEO8YIbhVJqJXoFc9hc5nTXXkCvtVDjIGbvC0vdE7tse+xTM9BMjsszP6bw== + +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +ssri@^10.0.0: + version "10.0.5" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" + integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== + dependencies: + minipass "^7.0.3" + +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +std-env@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" + integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== + +streamx@^2.15.0, streamx@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.16.1.tgz#2b311bd34832f08aa6bb4d6a80297c9caef89614" + integrity sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + optionalDependencies: + bare-events "^2.2.0" + +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.matchall@^4.0.6: + version "4.0.11" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" + +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string_decoder@^1.1.1, string_decoder@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringify-entities@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" + integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +strip-literal@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.3.0.tgz#db3942c2ec1699e6836ad230090b84bb458e3a07" + integrity sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg== + dependencies: + acorn "^8.10.0" + +strip-literal@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-2.1.0.tgz#6d82ade5e2e74f5c7e8739b6c84692bd65f0bd2a" + integrity sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw== + dependencies: + js-tokens "^9.0.0" + +stylehacks@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.1.1.tgz#543f91c10d17d00a440430362d419f79c25545a6" + integrity sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg== + dependencies: + browserslist "^4.23.0" + postcss-selector-parser "^6.0.16" + +sucrase@^3.32.0, sucrase@^3.34.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + 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" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.4.0.tgz#17bfcf686288f531db3dea3215510621ccb55954" + integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== + +svgo@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.2.0.tgz#7a5dff2938d8c6096e00295c2390e8e652fa805d" + integrity sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.3.1" + css-what "^6.1.0" + csso "^5.0.5" + picocolors "^1.0.0" + +system-architecture@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d" + integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== + +tabbable@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97" + integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew== + +tailwindcss@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.3.tgz#be48f5283df77dfced705451319a5dffb8621519" + integrity sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A== + 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.21.0" + 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" + +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-fs@^3.0.4: + version "3.0.6" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== + dependencies: + pump "^3.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^3.0.0, tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + +tar@^6.1.11, tar@^6.1.2, tar@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +temp-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" + integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== + +tempy@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.6.0.tgz#65e2c35abc06f1124a97f387b08303442bde59f3" + integrity sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw== + dependencies: + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + +terser@^5.17.4: + version "5.31.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.31.0.tgz#06eef86f17007dbad4593f11a574c7f5eb02c6a1" + integrity sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== + dependencies: + any-promise "^1.0.0" + +tiny-invariant@^1.1.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + +to-data-view@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/to-data-view/-/to-data-view-1.1.0.tgz#08d6492b0b8deb9b29bdf1f61c23eadfa8994d00" + integrity sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tosource@^2.0.0-alpha.3: + version "2.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/tosource/-/tosource-2.0.0-alpha.3.tgz#ef385dac9092e009bf25c018838ddaae436daeb6" + integrity sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug== + +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +trough@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== + +tslib@^2.4.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +tuf-js@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-2.2.0.tgz#4daaa8620ba7545501d04dfa933c98abbcc959b9" + integrity sha512-ZSDngmP1z6zw+FIkIBjvOp/II/mIub/O7Pp12j1WNsiCpg5R5wAc//i555bBQsE44O94btLt0xM/Zr2LQjwdCg== + dependencies: + "@tufjs/models" "2.0.0" + debug "^4.3.4" + make-fetch-happen "^13.0.0" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^3.8.0: + version "3.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706" + integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g== + +type-fest@^4.8.3: + version "4.18.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.18.0.tgz#414399bfdecfc60d6e89af9f5cf197aef1b6515b" + integrity sha512-+dbmiyliDY/2TTcjCS7NpI9yV2iEFlUDk5TKnsbkN7ZoRu5s7bT+zvYtNFhFXC2oLwURGT2frACAZvbbyNBI+w== + +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + +ufo@^1.1.2, ufo@^1.2.0, ufo@^1.3.1, ufo@^1.3.2, ufo@^1.4.0, ufo@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.5.3.tgz#3325bd3c977b6c6cd3160bf4ff52989adc9d3344" + integrity sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw== + +ultrahtml@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/ultrahtml/-/ultrahtml-1.5.3.tgz#e7a903a4b28a0e49b71b0801b444050bb0a369c7" + integrity sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unconfig@^0.3.11: + version "0.3.13" + resolved "https://registry.yarnpkg.com/unconfig/-/unconfig-0.3.13.tgz#8612d57811c1316f30d95f45bb96ce8ce8afc10c" + integrity sha512-N9Ph5NC4+sqtcOjPfHrRcHekBCadCXWTBzp2VYYbySOHW0PfD9XLCeXshTXjkPYwLrBr9AtSeU0CZmkYECJhng== + dependencies: + "@antfu/utils" "^0.7.7" + defu "^6.1.4" + jiti "^1.21.0" + +uncrypto@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/uncrypto/-/uncrypto-0.1.3.tgz#e1288d609226f2d02d8d69ee861fa20d8348ef2b" + integrity sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q== + +unctx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/unctx/-/unctx-2.3.1.tgz#5eb4aa9f96fb5fdac18b88fe5ba8e122fe671a62" + integrity sha512-PhKke8ZYauiqh3FEMVNm7ljvzQiph0Mt3GBRve03IJm7ukfaON2OBK795tLwhbyfzknuRRkW0+Ze+CQUmzOZ+A== + dependencies: + acorn "^8.8.2" + estree-walker "^3.0.3" + magic-string "^0.30.0" + unplugin "^1.3.1" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici@^5.28.2: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + +unenv@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/unenv/-/unenv-1.9.0.tgz#469502ae85be1bd3a6aa60f810972b1a904ca312" + integrity sha512-QKnFNznRxmbOF1hDgzpqrlIf6NC5sbZ2OJ+5Wl3OX8uM+LUJXbj4TXvLJCtwbPTmbMHCLIz6JLKNinNsMShK9g== + dependencies: + consola "^3.2.3" + defu "^6.1.3" + mime "^3.0.0" + node-fetch-native "^1.6.1" + pathe "^1.1.1" + +unhead@1.9.7: + version "1.9.7" + resolved "https://registry.yarnpkg.com/unhead/-/unhead-1.9.7.tgz#c0962857520a0f9915329bb9961562a44ec47e75" + integrity sha512-Kv7aU5l41qiq36t9qMks8Pgsj7adaTBm9aDS6USlmodTXioeqlJ5vEu9DI+8ZZPwRlmof3aDlo1kubyaXdSNmQ== + dependencies: + "@unhead/dom" "1.9.7" + "@unhead/schema" "1.9.7" + "@unhead/shared" "1.9.7" + hookable "^5.5.3" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-emoji-modifier-base@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz#dbbd5b54ba30f287e2a8d5a249da6c0cef369459" + integrity sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" + integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== + +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + +unified@^11.0.0, unified@^11.0.4: + version "11.0.4" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.4.tgz#f4be0ac0fe4c88cb873687c07c64c49ed5969015" + integrity sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + +unimport@^3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/unimport/-/unimport-3.7.1.tgz#37250d0f3f2dcf1e1b66ed13728db0e9f50ba0c3" + integrity sha512-V9HpXYfsZye5bPPYUgs0Otn3ODS1mDUciaBlXljI4C2fTwfFpvFZRywmlOu943puN9sncxROMZhsZCjNXEpzEQ== + dependencies: + "@rollup/pluginutils" "^5.1.0" + acorn "^8.11.2" + escape-string-regexp "^5.0.0" + estree-walker "^3.0.3" + fast-glob "^3.3.2" + local-pkg "^0.5.0" + magic-string "^0.30.5" + mlly "^1.4.2" + pathe "^1.1.1" + pkg-types "^1.0.3" + scule "^1.1.1" + strip-literal "^1.3.0" + unplugin "^1.5.1" + +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== + dependencies: + imurmurhash "^0.1.4" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +unist-builder@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-4.0.0.tgz#817b326c015a6f9f5e92bb55b8e8bc5e578fe243" + integrity sha512-wmRFnH+BLpZnTKpc5L7O67Kac89s9HMrtELpnNaE6TAobq5DTZZs5YaTQfAZBA9bFPECx2uVAPO31c+GVug8mg== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-is@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" + integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^6.0.0, unist-util-visit-parents@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" + integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" + integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + +unplugin-vue-router@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/unplugin-vue-router/-/unplugin-vue-router-0.7.0.tgz#27bd250c7dc698366cce70c5b72b97c3b3766c26" + integrity sha512-ddRreGq0t5vlSB7OMy4e4cfU1w2AwBQCwmvW3oP/0IHQiokzbx4hd3TpwBu3eIAFVuhX2cwNQwp1U32UybTVCw== + dependencies: + "@babel/types" "^7.22.19" + "@rollup/pluginutils" "^5.0.4" + "@vue-macros/common" "^1.8.0" + ast-walker-scope "^0.5.0" + chokidar "^3.5.3" + fast-glob "^3.3.1" + json5 "^2.2.3" + local-pkg "^0.4.3" + mlly "^1.4.2" + pathe "^1.1.1" + scule "^1.0.0" + unplugin "^1.5.0" + yaml "^2.3.2" + +unplugin@^1.1.0, unplugin@^1.10.0, unplugin@^1.10.1, unplugin@^1.3.1, unplugin@^1.5.0, unplugin@^1.5.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-1.10.1.tgz#8ceda065dc71bc67d923dea0920f05c67f2cd68c" + integrity sha512-d6Mhq8RJeGA8UfKCu54Um4lFA0eSaRa3XxdAJg8tIdxbu1ubW0hBCZUL7yI2uGyYCRndvbK8FLHzqy2XKfeMsg== + dependencies: + acorn "^8.11.3" + chokidar "^3.6.0" + webpack-sources "^3.2.3" + webpack-virtual-modules "^0.6.1" + +unstorage@^1.10.1, unstorage@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/unstorage/-/unstorage-1.10.2.tgz#fb7590ada8b30e83be9318f85100158b02a76dae" + integrity sha512-cULBcwDqrS8UhlIysUJs2Dk0Mmt8h7B0E6mtR+relW9nZvsf/u4SkAYyNliPiPW7XtFNb5u3IUMkxGxFTTRTgQ== + dependencies: + anymatch "^3.1.3" + chokidar "^3.6.0" + destr "^2.0.3" + h3 "^1.11.1" + listhen "^1.7.2" + lru-cache "^10.2.0" + mri "^1.2.0" + node-fetch-native "^1.6.2" + ofetch "^1.3.3" + ufo "^1.4.0" + +untun@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/untun/-/untun-0.1.3.tgz#5d10dee37a3a5737ff03d158be877dae0a0e58a6" + integrity sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ== + dependencies: + citty "^0.1.5" + consola "^3.2.3" + pathe "^1.1.1" + +untyped@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/untyped/-/untyped-1.4.2.tgz#7945ea53357635434284e6112fd1afe84dd5dcab" + integrity sha512-nC5q0DnPEPVURPhfPQLahhSTnemVtPzdx7ofiRxXpOB2SYnb3MfdU3DVGyJdS8Lx+tBWeAePO8BfU/3EgksM7Q== + dependencies: + "@babel/core" "^7.23.7" + "@babel/standalone" "^7.23.8" + "@babel/types" "^7.23.6" + defu "^6.1.4" + jiti "^1.21.0" + mri "^1.2.0" + scule "^1.2.0" + +unwasm@^0.3.7, unwasm@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/unwasm/-/unwasm-0.3.9.tgz#01eca80a1cf2133743bc1bf5cfa749cc145beea0" + integrity sha512-LDxTx/2DkFURUd+BU1vUsF/moj0JsoTvl+2tcg2AUOiEzVturhGGx17/IMgGvKUYdZwr33EJHtChCJuhu9Ouvg== + dependencies: + knitwork "^1.0.0" + magic-string "^0.30.8" + mlly "^1.6.1" + pathe "^1.1.2" + pkg-types "^1.0.3" + unplugin "^1.10.0" + +upath@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uqr@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/uqr/-/uqr-0.1.2.tgz#5c6cd5dcff9581f9bb35b982cb89e2c483a41d7d" + integrity sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA== + +uri-js@^4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urlpattern-polyfill@8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" + integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +validate-npm-package-license@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" + integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== + dependencies: + builtins "^5.0.0" + +vee-validate@4.12.6, vee-validate@^4.12.6: + version "4.12.6" + resolved "https://registry.yarnpkg.com/vee-validate/-/vee-validate-4.12.6.tgz#cc1b8ac99ebf00856b40a5118a23787898a70f32" + integrity sha512-EKM3YHy8t1miPh30d5X6xOrfG/Ctq0nbN4eMpCK7ezvI6T98/S66vswP+ihL4QqAK/k5KqreWOxof09+JG7N/A== + dependencies: + "@vue/devtools-api" "^6.5.1" + type-fest "^4.8.3" + +vfile-location@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.2.tgz#220d9ca1ab6f8b2504a4db398f7ebc149f9cb464" + integrity sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg== + dependencies: + "@types/unist" "^3.0.0" + vfile "^6.0.0" + +vfile-message@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" + integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.1.tgz#1e8327f41eac91947d4fe9d237a2dd9209762536" + integrity sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + +vite-hot-client@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vite-hot-client/-/vite-hot-client-0.2.3.tgz#db52aba46edbcfa7906dbca8255fd35b9a9270b2" + integrity sha512-rOGAV7rUlUHX89fP2p2v0A2WWvV3QMX2UYq0fRqsWSvFvev4atHWqjwGoKaZT1VTKyLGk533ecu3eyd0o59CAg== + +vite-node@^1.4.0: + version "1.5.3" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.5.3.tgz#498f4eb6f4e37ff95f66ffb9c905708a75f84b2e" + integrity sha512-axFo00qiCpU/JLd8N1gu9iEYL3xTbMbMrbe5nDp9GL0nb6gurIdZLkkFogZXWnE8Oyy5kfSLwNVIcVsnhE7lgQ== + dependencies: + cac "^6.7.14" + debug "^4.3.4" + pathe "^1.1.1" + picocolors "^1.0.0" + vite "^5.0.0" + +vite-plugin-checker@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.6.4.tgz#aca186ab605aa15bd2c5dd9cc6d7c8fdcbe214ec" + integrity sha512-2zKHH5oxr+ye43nReRbC2fny1nyARwhxdm0uNYp/ERy4YvU9iZpNOsueoi/luXw5gnpqRSvjcEPxXbS153O2wA== + dependencies: + "@babel/code-frame" "^7.12.13" + ansi-escapes "^4.3.0" + chalk "^4.1.1" + chokidar "^3.5.1" + commander "^8.0.0" + fast-glob "^3.2.7" + fs-extra "^11.1.0" + npm-run-path "^4.0.1" + semver "^7.5.0" + strip-ansi "^6.0.0" + tiny-invariant "^1.1.0" + vscode-languageclient "^7.0.0" + vscode-languageserver "^7.0.0" + vscode-languageserver-textdocument "^1.0.1" + vscode-uri "^3.0.2" + +vite-plugin-inspect@^0.8.3: + version "0.8.4" + resolved "https://registry.yarnpkg.com/vite-plugin-inspect/-/vite-plugin-inspect-0.8.4.tgz#1fcdf6703164d651db3f13c8c6610e1ced5108e1" + integrity sha512-G0N3rjfw+AiiwnGw50KlObIHYWfulVwaCBUBLh2xTW9G1eM9ocE5olXkEYUbwyTmX+azM8duubi+9w5awdCz+g== + dependencies: + "@antfu/utils" "^0.7.7" + "@rollup/pluginutils" "^5.1.0" + debug "^4.3.4" + error-stack-parser-es "^0.1.1" + fs-extra "^11.2.0" + open "^10.1.0" + perfect-debounce "^1.0.0" + picocolors "^1.0.0" + sirv "^2.0.4" + +"vite-plugin-pwa@>=0.20.0 <1": + version "0.20.0" + resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.20.0.tgz#1785c8cc8c11c89c0ba8c6557f29e2b58b14dd6d" + integrity sha512-/kDZyqF8KqoXRpMUQtR5Atri/7BWayW8Gp7Kz/4bfstsV6zSFTxjREbXZYL7zSuRL40HGA+o2hvUAFRmC+bL7g== + dependencies: + debug "^4.3.4" + fast-glob "^3.3.2" + pretty-bytes "^6.1.1" + workbox-build "^7.1.0" + workbox-window "^7.1.0" + +vite-plugin-vue-inspector@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-4.0.2.tgz#1d02646b20f4dc72cda0c2e0309551c7b332df73" + integrity sha512-KPvLEuafPG13T7JJuQbSm5PwSxKFnVS965+MP1we2xGw9BPkkc/+LPix5MMWenpKWqtjr0ws8THrR+KuoDC8hg== + dependencies: + "@babel/core" "^7.23.0" + "@babel/plugin-proposal-decorators" "^7.23.0" + "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-transform-typescript" "^7.22.15" + "@vue/babel-plugin-jsx" "^1.1.5" + "@vue/compiler-dom" "^3.3.4" + kolorist "^1.8.0" + magic-string "^0.30.4" + +vite@^5.0.0, vite@^5.2.8: + version "5.2.10" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.10.tgz#2ac927c91e99d51b376a5c73c0e4b059705f5bd7" + integrity sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw== + dependencies: + esbuild "^0.20.1" + postcss "^8.4.38" + rollup "^4.13.0" + optionalDependencies: + fsevents "~2.3.3" + +vscode-jsonrpc@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz#108bdb09b4400705176b957ceca9e0880e9b6d4e" + integrity sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg== + +vscode-languageclient@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz#b505c22c21ffcf96e167799757fca07a6bad0fb2" + integrity sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg== + dependencies: + minimatch "^3.0.4" + semver "^7.3.4" + vscode-languageserver-protocol "3.16.0" + +vscode-languageserver-protocol@3.16.0: + version "3.16.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz#34135b61a9091db972188a07d337406a3cdbe821" + integrity sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A== + dependencies: + vscode-jsonrpc "6.0.0" + vscode-languageserver-types "3.16.0" + +vscode-languageserver-textdocument@^1.0.1: + version "1.0.11" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" + integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA== + +vscode-languageserver-types@3.16.0: + version "3.16.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" + integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== + +vscode-languageserver@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz#49b068c87cfcca93a356969d20f5d9bdd501c6b0" + integrity sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw== + dependencies: + vscode-languageserver-protocol "3.16.0" + +vscode-uri@^3.0.2: + version "3.0.8" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.8.tgz#1770938d3e72588659a172d0fd4642780083ff9f" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + +vue-bundle-renderer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vue-bundle-renderer/-/vue-bundle-renderer-2.0.0.tgz#ecab5c9b2803ab2454ba212afef502e684ddbb8e" + integrity sha512-oYATTQyh8XVkUWe2kaKxhxKVuuzK2Qcehe+yr3bGiaQAhK3ry2kYE4FWOfL+KO3hVFwCdLmzDQTzYhTi9C+R2A== + dependencies: + ufo "^1.2.0" + +vue-demi@>=0.14.5, vue-demi@>=0.14.7: + version "0.14.7" + resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.7.tgz#8317536b3ef74c5b09f268f7782e70194567d8f2" + integrity sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA== + +vue-devtools-stub@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz#a65b9485edecd4273cedcb8102c739b83add2c81" + integrity sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ== + +vue-i18n@^9.9.0: + version "9.13.1" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.13.1.tgz#a292c8021b7be604ebfca5609ae1f8fafe5c36d7" + integrity sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg== + dependencies: + "@intlify/core-base" "9.13.1" + "@intlify/shared" "9.13.1" + "@vue/devtools-api" "^6.5.0" + +vue-observe-visibility@^2.0.0-alpha.1: + version "2.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz#1e4eda7b12562161d58984b7e0dea676d83bdb13" + integrity sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g== + +vue-resize@^2.0.0-alpha.1: + version "2.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz#43eeb79e74febe932b9b20c5c57e0ebc14e2df3a" + integrity sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg== + +vue-router@^4.2.5, vue-router@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.3.2.tgz#08096c7765dacc6832f58e35f7a081a8b34116a7" + integrity sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q== + dependencies: + "@vue/devtools-api" "^6.5.1" + +vue-virtual-scroller@2.0.0-beta.8: + version "2.0.0-beta.8" + resolved "https://registry.yarnpkg.com/vue-virtual-scroller/-/vue-virtual-scroller-2.0.0-beta.8.tgz#eeceda57e4faa5ba1763994c873923e2a956898b" + integrity sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ== + dependencies: + mitt "^2.1.0" + vue-observe-visibility "^2.0.0-alpha.1" + vue-resize "^2.0.0-alpha.1" + +vue@^3.4.21: + version "3.4.26" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.4.26.tgz#936c97e37672c737705d7bdfa62c31af18742269" + integrity sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg== + dependencies: + "@vue/compiler-dom" "3.4.26" + "@vue/compiler-sfc" "3.4.26" + "@vue/runtime-dom" "3.4.26" + "@vue/server-renderer" "3.4.26" + "@vue/shared" "3.4.26" + +web-namespaces@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" + integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack-virtual-modules@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz#ac6fdb9c5adb8caecd82ec241c9631b7a3681b6f" + integrity sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" + integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +which@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" + integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== + dependencies: + isexe "^2.0.0" + +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +workbox-background-sync@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.1.0.tgz#dac65e30af603511f1c92c3e99f53d6c064fde90" + integrity sha512-rMbgrzueVWDFcEq1610YyDW71z0oAXLfdRHRQcKw4SGihkfOK0JUEvqWHFwA6rJ+6TClnMIn7KQI5PNN1XQXwQ== + dependencies: + idb "^7.0.1" + workbox-core "7.1.0" + +workbox-broadcast-update@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-7.1.0.tgz#fe21c491cc70f1e037898bba63de0752ef59bd82" + integrity sha512-O36hIfhjej/c5ar95pO67k1GQw0/bw5tKP7CERNgK+JdxBANQhDmIuOXZTNvwb2IHBx9hj2kxvcDyRIh5nzOgQ== + dependencies: + workbox-core "7.1.0" + +workbox-build@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-7.1.0.tgz#64d1532f1b9ad04d2b8b43ce0b9af06ba3fdd159" + integrity sha512-F6R94XAxjB2j4ETMkP1EXKfjECOtDmyvt0vz3BzgWJMI68TNSXIVNkgatwUKBlPGOfy9n2F/4voYRNAhEvPJNg== + dependencies: + "@apideck/better-ajv-errors" "^0.3.1" + "@babel/core" "^7.24.4" + "@babel/preset-env" "^7.11.0" + "@babel/runtime" "^7.11.2" + "@rollup/plugin-babel" "^5.2.0" + "@rollup/plugin-node-resolve" "^15.2.3" + "@rollup/plugin-replace" "^2.4.1" + "@rollup/plugin-terser" "^0.4.3" + "@surma/rollup-plugin-off-main-thread" "^2.2.3" + ajv "^8.6.0" + common-tags "^1.8.0" + fast-json-stable-stringify "^2.1.0" + fs-extra "^9.0.1" + glob "^7.1.6" + lodash "^4.17.20" + pretty-bytes "^5.3.0" + rollup "^2.43.1" + source-map "^0.8.0-beta.0" + stringify-object "^3.3.0" + strip-comments "^2.0.1" + tempy "^0.6.0" + upath "^1.2.0" + workbox-background-sync "7.1.0" + workbox-broadcast-update "7.1.0" + workbox-cacheable-response "7.1.0" + workbox-core "7.1.0" + workbox-expiration "7.1.0" + workbox-google-analytics "7.1.0" + workbox-navigation-preload "7.1.0" + workbox-precaching "7.1.0" + workbox-range-requests "7.1.0" + workbox-recipes "7.1.0" + workbox-routing "7.1.0" + workbox-strategies "7.1.0" + workbox-streams "7.1.0" + workbox-sw "7.1.0" + workbox-window "7.1.0" + +workbox-cacheable-response@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-7.1.0.tgz#d138cc8ef2c32a9f28f29c5b2b0a8681da846c33" + integrity sha512-iwsLBll8Hvua3xCuBB9h92+/e0wdsmSVgR2ZlvcfjepZWwhd3osumQB3x9o7flj+FehtWM2VHbZn8UJeBXXo6Q== + dependencies: + workbox-core "7.1.0" + +workbox-core@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.1.0.tgz#1867576f994f20d9991b71a7d0b2581af22db170" + integrity sha512-5KB4KOY8rtL31nEF7BfvU7FMzKT4B5TkbYa2tzkS+Peqj0gayMT9SytSFtNzlrvMaWgv6y/yvP9C0IbpFjV30Q== + +workbox-expiration@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-7.1.0.tgz#c9d348ffc8c3d1ffdddaf6c37bf5be830a69073e" + integrity sha512-m5DcMY+A63rJlPTbbBNtpJ20i3enkyOtSgYfv/l8h+D6YbbNiA0zKEkCUaMsdDlxggla1oOfRkyqTvl5Ni5KQQ== + dependencies: + idb "^7.0.1" + workbox-core "7.1.0" + +workbox-google-analytics@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-7.1.0.tgz#25cca57a05554b6121521590543e59628eb15a65" + integrity sha512-FvE53kBQHfVTcZyczeBVRexhh7JTkyQ8HAvbVY6mXd2n2A7Oyz/9fIwnY406ZcDhvE4NFfKGjW56N4gBiqkrew== + dependencies: + workbox-background-sync "7.1.0" + workbox-core "7.1.0" + workbox-routing "7.1.0" + workbox-strategies "7.1.0" + +workbox-navigation-preload@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-7.1.0.tgz#2610674d412a1774b5d9f03c9644c9964407b8b6" + integrity sha512-4wyAbo0vNI/X0uWNJhCMKxnPanNyhybsReMGN9QUpaePLTiDpKxPqFxl4oUmBNddPwIXug01eTSLVIFXimRG/A== + dependencies: + workbox-core "7.1.0" + +workbox-precaching@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-7.1.0.tgz#71e27ec2e85661a41b48dec0c92dae707c429eaa" + integrity sha512-LyxzQts+UEpgtmfnolo0hHdNjoB7EoRWcF7EDslt+lQGd0lW4iTvvSe3v5JiIckQSB5KTW5xiCqjFviRKPj1zA== + dependencies: + workbox-core "7.1.0" + workbox-routing "7.1.0" + workbox-strategies "7.1.0" + +workbox-range-requests@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-7.1.0.tgz#8d4344cd85b87d8077289a64dda59fb614628783" + integrity sha512-m7+O4EHolNs5yb/79CrnwPR/g/PRzMFYEdo01LqwixVnc/sbzNSvKz0d04OE3aMRel1CwAAZQheRsqGDwATgPQ== + dependencies: + workbox-core "7.1.0" + +workbox-recipes@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-7.1.0.tgz#37625cd2fe7e5decd70c8934a673a7cc080a7675" + integrity sha512-NRrk4ycFN9BHXJB6WrKiRX3W3w75YNrNrzSX9cEZgFB5ubeGoO8s/SDmOYVrFYp9HMw6sh1Pm3eAY/1gVS8YLg== + dependencies: + workbox-cacheable-response "7.1.0" + workbox-core "7.1.0" + workbox-expiration "7.1.0" + workbox-precaching "7.1.0" + workbox-routing "7.1.0" + workbox-strategies "7.1.0" + +workbox-routing@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-7.1.0.tgz#c44bda350d1c5eb633ee97a660e64ce5473250c4" + integrity sha512-oOYk+kLriUY2QyHkIilxUlVcFqwduLJB7oRZIENbqPGeBP/3TWHYNNdmGNhz1dvKuw7aqvJ7CQxn27/jprlTdg== + dependencies: + workbox-core "7.1.0" + +workbox-strategies@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-7.1.0.tgz#a589f2adc0df8f33049c7f4d4cdf4c9556715918" + integrity sha512-/UracPiGhUNehGjRm/tLUQ+9PtWmCbRufWtV0tNrALuf+HZ4F7cmObSEK+E4/Bx1p8Syx2tM+pkIrvtyetdlew== + dependencies: + workbox-core "7.1.0" + +workbox-streams@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-7.1.0.tgz#8e080e56b5dee7aa0f956fdd3a10506821d2e786" + integrity sha512-WyHAVxRXBMfysM8ORwiZnI98wvGWTVAq/lOyBjf00pXFvG0mNaVz4Ji+u+fKa/mf1i2SnTfikoYKto4ihHeS6w== + dependencies: + workbox-core "7.1.0" + workbox-routing "7.1.0" + +workbox-sw@7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-7.1.0.tgz#3df97d7cccb647eb94d66be7dc733c9fda26b9fc" + integrity sha512-Hml/9+/njUXBglv3dtZ9WBKHI235AQJyLBV1G7EFmh4/mUdSQuXui80RtjDeVRrXnm/6QWgRUEHG3/YBVbxtsA== + +workbox-window@7.1.0, workbox-window@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-7.1.0.tgz#58a90ba89ca35d26f2b322223ee575c750bac7a1" + integrity sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g== + dependencies: + "@types/trusted-types" "^2.0.2" + workbox-core "7.1.0" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.16.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" + integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== + +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml-eslint-parser@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-1.2.2.tgz#1a9673ebe254328cfc2fa99f297f6d8c9364ccd8" + integrity sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg== + dependencies: + eslint-visitor-keys "^3.0.0" + lodash "^4.17.21" + yaml "^2.0.0" + +yaml@^2.0.0, yaml@^2.3.2, yaml@^2.3.4: + version "2.4.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" + integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.5.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +zhead@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/zhead/-/zhead-2.2.4.tgz#87cd1e2c3d2f465fa9f43b8db23f9716dfe6bed7" + integrity sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag== + +zip-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb" + integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA== + dependencies: + archiver-utils "^5.0.0" + compress-commons "^6.0.2" + readable-stream "^4.0.0" + +zwitch@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==